commit 90d2dc971a13f42db50de89bc8a50bfa6b131537 Author: Jeff Date: Sat Jul 8 09:18:43 2023 +0800 🎨feat:feat:v2.5,JDK17、Activiti7、Vue3、TS、Vite、ElementPlugs 的 全新版本 diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3ee23e27 --- /dev/null +++ b/.gitignore @@ -0,0 +1,253 @@ +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.ear +*.zip +*.tar.gz +*.rar +.idea +*.iml + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* +/target/ +/.classpath +/.project +/.settings/ +web/WebContent/META-INF/MANIFEST.MF +### Maven template +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties +# https://github.com/takari/maven-wrapper#usage-without-binary-jar +.mvn/wrapper/maven-wrapper.jar + +### Java template +# Compiled class file +*.class + +# Log file +*.log + +# BlueJ files +*.ctxt + +# Mobile Tools for Java (J2ME) +.mtj.tmp/ + +# Package Files # +*.jar +*.war +*.nar +*.ear +*.zip +*.tar.gz +*.rar + +# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml +hs_err_pid* + +### macOS template +# General +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + +### Windows template +# Windows thumbnail cache files +Thumbs.db +Thumbs.db:encryptable +ehthumbs.db +ehthumbs_vista.db + +# Dump file +*.stackdump + +# Folder config file +[Dd]esktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# Windows Installer files +*.cab +*.msi +*.msix +*.msm +*.msp + +# Windows shortcuts +*.lnk + +### Eclipse template +.metadata +bin/ +tmp/ +*.tmp +*.bak +*.swp +*~.nib +local.properties +.settings/ +.loadpath +.recommenders + +# External tool builders +.externalToolBuilders/ + +# Locally stored "Eclipse launch configurations" +*.launch + +# PyDev specific (Python IDE for Eclipse) +*.pydevproject + +# CDT-specific (C/C++ Development Tooling) +.cproject + +# CDT- autotools +.autotools + +# Java annotation processor (APT) +.factorypath + +# PDT-specific (PHP Development Tools) +.buildpath + +# sbteclipse plugin +.target + +# Tern plugin +.tern-project + +# TeXlipse plugin +.texlipse + +# STS (Spring Tool Suite) +.springBeans + +# Code Recommenders +.recommenders/ + +# Annotation Processing +.apt_generated/ +.apt_generated_test/ + +# Scala IDE specific (Scala & Java development for Eclipse) +.cache-main +.scala_dependencies +.worksheet + +### JetBrains template +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +# User-specific stuff +.idea/**/workspace.xml +.idea/**/tasks.xml +.idea/**/usage.statistics.xml +.idea/**/dictionaries +.idea/**/shelf + +# Generated files +.idea/**/contentModel.xml + +# Sensitive or high-churn files +.idea/**/dataSources/ +.idea/**/dataSources.ids +.idea/**/dataSources.local.xml +.idea/**/sqlDataSources.xml +.idea/**/dynamic.xml +.idea/**/uiDesigner.xml +.idea/**/dbnavigator.xml + +# Gradle +.idea/**/gradle.xml +.idea/**/libraries + +# Gradle and Maven with auto-import +# When using Gradle or Maven with auto-import, you should exclude module files, +# since they will be recreated, and may cause churn. Uncomment if using +# auto-import. +# .idea/artifacts +# .idea/compiler.xml +# .idea/modules.xml +# .idea/*.iml +# .idea/modules +# *.iml +# *.ipr + +# CMake +cmake-build-*/ + +# Mongo Explorer plugin +.idea/**/mongoSettings.xml + +# File-based project format +*.iws + +# IntelliJ +out/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Cursive Clojure plugin +.idea/replstate.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +# Editor-based Rest Client +.idea/httpRequests + +# Android studio 3.1+ serialized cache file +.idea/caches/build_file_checksums.ser + +*.classpath +*.project diff --git a/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.md5 b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.md5 new file mode 100644 index 00000000..a00fb6f2 --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.md5 @@ -0,0 +1 @@ +2efdb939520e48970404a46039a6bc23 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.sha1 b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.sha1 new file mode 100644 index 00000000..8d5a902c --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.jar.sha1 @@ -0,0 +1 @@ +572204f16e80c7b67a7f380c764d56f9336c785d \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom new file mode 100644 index 00000000..1ab8431d --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom @@ -0,0 +1,19 @@ + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + ab-biz-api + + + com.dstz + ab-base-common + + + com.dstz + ab-groovy-script-api + + + diff --git a/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.md5 b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.md5 new file mode 100644 index 00000000..c0ab7d01 --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.md5 @@ -0,0 +1 @@ +d9e81975644473fe3a61d9cc9cc6dfb5 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.sha1 b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.sha1 new file mode 100644 index 00000000..174ca4eb --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/2.5.0/ab-biz-api-2.5.0.pom.sha1 @@ -0,0 +1 @@ +4ee89232266093c6b5eb97c9561b0d70f917beef \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml new file mode 100644 index 00000000..18ca3aa0 --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + com.dstz + ab-biz-api + + 2.5.0 + + 2.5.0 + + 20230707093924 + + diff --git a/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.md5 b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.md5 new file mode 100644 index 00000000..c24a41d6 --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.md5 @@ -0,0 +1 @@ +1d4edc3bf9ebfd416fda562389234e54 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.sha1 b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.sha1 new file mode 100644 index 00000000..9da0d098 --- /dev/null +++ b/.m2/repository/com/dstz/ab-biz-api/maven-metadata-local.xml.sha1 @@ -0,0 +1 @@ +503883124f635090b03bdc30ce53478ad92dbd5b \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.md5 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.md5 new file mode 100644 index 00000000..31fa8f56 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.md5 @@ -0,0 +1 @@ +406e7da48b390b19875c31c2df806486 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.sha1 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.sha1 new file mode 100644 index 00000000..ae349ff8 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.jar.sha1 @@ -0,0 +1 @@ +e3e4765c3e6fde22c846d68886a7c193f2289a5c \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom new file mode 100644 index 00000000..a0debf4f --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom @@ -0,0 +1,21 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot-biz-starter + + + + org.jsoup + jsoup + 1.8.1 + + + \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.md5 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.md5 new file mode 100644 index 00000000..87cfcbdb --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.md5 @@ -0,0 +1 @@ +1569e1786d50c4c9b1f9d826f2b0a4ad \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.sha1 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.sha1 new file mode 100644 index 00000000..ed9fac3a --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/2.5.0/ab-spring-boot-biz-starter-2.5.0.pom.sha1 @@ -0,0 +1 @@ +69f7e16e15a63ec94e7235414983e2ba6fa7f720 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml new file mode 100644 index 00000000..454e52b9 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + com.dstz + ab-spring-boot-biz-starter + + 2.5.0 + + 2.5.0 + + 20230707093928 + + diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.md5 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.md5 new file mode 100644 index 00000000..1502a784 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.md5 @@ -0,0 +1 @@ +cd98a9eb1f0e0c6e49ce6b6253d7bd31 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.sha1 b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.sha1 new file mode 100644 index 00000000..e5dfd176 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-biz-starter/maven-metadata-local.xml.sha1 @@ -0,0 +1 @@ +96e83dde467a9ef5ef7aa6183ba0737dffcd0c03 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.md5 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.md5 new file mode 100644 index 00000000..1eb2743f --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.md5 @@ -0,0 +1 @@ +419e1396338f89e13f2306304ec0ac4b \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.sha1 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.sha1 new file mode 100644 index 00000000..c71fc4b8 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.jar.sha1 @@ -0,0 +1 @@ +efb19679870e7f9a1559758b8074cb0ce800c602 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom new file mode 100644 index 00000000..ac871c27 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom @@ -0,0 +1,55 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot-wf-starter + + + + com.dstz + ab-spring-boot-starter + ${project.version} + + + com.dstz + ab-wf-client + ${project.version} + + + com.dstz + ab-biz-api + ${project.version} + + + org.activiti + activiti-engine + AB + + + org.tinyjee.jgraphx + jgraphx + 1.10.4.1 + + + org.activiti + activiti-spring-boot-starter + + + activiti-spring-identity + org.activiti.core.common + + + + + io.netty + netty-common + + + diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.md5 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.md5 new file mode 100644 index 00000000..b43b78a2 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.md5 @@ -0,0 +1 @@ +e4c50d12c28da9cecb6986fa7de7f8a5 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.sha1 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.sha1 new file mode 100644 index 00000000..0b7d882c --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/2.5.0/ab-spring-boot-wf-starter-2.5.0.pom.sha1 @@ -0,0 +1 @@ +ca4ebfca93b9a9883b10674f8f63275c03a8592d \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml new file mode 100644 index 00000000..89012909 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + com.dstz + ab-spring-boot-wf-starter + + 2.5.0 + + 2.5.0 + + 20230707093927 + + diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.md5 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.md5 new file mode 100644 index 00000000..a2b01528 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.md5 @@ -0,0 +1 @@ +588a996704469002bf4239d9b54c6197 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.sha1 b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.sha1 new file mode 100644 index 00000000..d152a088 --- /dev/null +++ b/.m2/repository/com/dstz/ab-spring-boot-wf-starter/maven-metadata-local.xml.sha1 @@ -0,0 +1 @@ +e44644199717e05b923ca4b9f2e45186deec4147 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.md5 b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.md5 new file mode 100644 index 00000000..760036b4 --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.md5 @@ -0,0 +1 @@ +0221b2a2d4db6aa49b4d1c7f860a0c4a \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.sha1 b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.sha1 new file mode 100644 index 00000000..947aec4d --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.jar.sha1 @@ -0,0 +1 @@ +6626ccfceedc097ba034252da13afef287555d66 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom new file mode 100644 index 00000000..7282cd2c --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom @@ -0,0 +1,17 @@ + + 4.0.0 + + com.dstz + agile-bpm + 2.5.0 + ../pom.xml + + ab-wf-client + 流程模块客户端包,主要是一些开放式的DTO + + + com.dstz + ab-base-api + + + diff --git a/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.md5 b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.md5 new file mode 100644 index 00000000..b05965f3 --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.md5 @@ -0,0 +1 @@ +102d9afb8b4edb9fc373719657591d9d \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.sha1 b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.sha1 new file mode 100644 index 00000000..082bda14 --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/2.5.0/ab-wf-client-2.5.0.pom.sha1 @@ -0,0 +1 @@ +7e212439a4ab5d70ed8a0e853685cd864c73ac12 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml new file mode 100644 index 00000000..03f02ccc --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + com.dstz + ab-wf-client + + 2.5.0 + + 2.5.0 + + 20230707093926 + + diff --git a/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.md5 b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.md5 new file mode 100644 index 00000000..78848e6b --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.md5 @@ -0,0 +1 @@ +bdcb916d3644499ffc650e3105aa2683 \ No newline at end of file diff --git a/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.sha1 b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.sha1 new file mode 100644 index 00000000..8a4bfd9d --- /dev/null +++ b/.m2/repository/com/dstz/ab-wf-client/maven-metadata-local.xml.sha1 @@ -0,0 +1 @@ +12e926824ba35439d90d66c07d8009d4a0f95a9d \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.md5 b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.md5 new file mode 100644 index 00000000..517063ea --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.md5 @@ -0,0 +1 @@ +40d8539bc56ef37c197a65b8208e4e87 \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.sha1 b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.sha1 new file mode 100644 index 00000000..ac21cffe --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6-AB.jar.sha1 @@ -0,0 +1 @@ +e8faa8dcf42675d8d4873e7ad7a4837f26f763a5 \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom new file mode 100644 index 00000000..28e48a70 --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom @@ -0,0 +1,187 @@ + + + 4.0.0 + + + org.activiti + activiti-core-dependencies + 7.1.0.M6 + ../activiti-core-dependencies + + + activiti-engine + Activiti :: Engine + + + + org.activiti.api + activiti-api-runtime-shared + + + org.activiti.core.common + activiti-project-model + + + org.activiti + activiti-bpmn-converter + + + org.activiti + activiti-bpmn-model + + + org.activiti + activiti-process-validation + + + org.apache.commons + commons-email + + + org.apache.commons + commons-lang3 + + + org.mybatis + mybatis + + + org.springframework + spring-beans + + + org.springframework + spring-core + + + de.odysseus.juel + juel-api + + + de.odysseus.juel + juel-impl + + + de.odysseus.juel + juel-spi + + + junit + junit + provided + + + org.assertj + assertj-core + test + + + com.fasterxml.jackson.core + jackson-core + + + com.fasterxml.jackson.core + jackson-annotations + + + com.fasterxml.jackson.core + jackson-databind + + + org.codehaus.groovy + groovy + test + + + org.codehaus.groovy + groovy-jsr223 + test + + + org.apache.geronimo.specs + geronimo-jta_1.1_spec + provided + + + javax.enterprise.concurrent + javax.enterprise.concurrent-api + provided + + + com.h2database + h2 + test + + + org.mockito + mockito-core + test + + + org.subethamail + subethasmtp-wiser + test + + + com.fasterxml.uuid + java-uuid-generator + provided + + + commons-io + commons-io + test + + + joda-time + joda-time + + + org.slf4j + slf4j-api + + + org.slf4j + jcl-over-slf4j + + + ch.qos.logback + logback-classic + test + + + javax.xml.bind + jaxb-api + test + + + + org.codehaus.btm + btm + test + + + org.hibernate + hibernate-entitymanager + true + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + **/*TestCase.java + **/RepeatingServiceTaskTest.java + + alphabetical + + + + + + diff --git a/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.md5 b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.md5 new file mode 100644 index 00000000..be354407 --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.md5 @@ -0,0 +1 @@ +05dd48f439fb10c82b404b4ab49d9307 \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.sha1 b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.sha1 new file mode 100644 index 00000000..b4304abc --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/7.1.0.M6/activiti-engine-7.1.0.M6.pom.sha1 @@ -0,0 +1 @@ +139f3281de2392871b0b72fc8905cd8f73bab96c \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml new file mode 100644 index 00000000..17075a2f --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml @@ -0,0 +1,12 @@ + + + org.activiti + activiti-engine + + 7.1.0.M6 + + 7.1.0.M6 + + 20230707093922 + + diff --git a/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.md5 b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.md5 new file mode 100644 index 00000000..26703dc4 --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.md5 @@ -0,0 +1 @@ +a4aaae6503e712426acc7c2d33d5b19c \ No newline at end of file diff --git a/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.sha1 b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.sha1 new file mode 100644 index 00000000..660b17e7 --- /dev/null +++ b/.m2/repository/org/activiti/activiti-engine/maven-metadata-local.xml.sha1 @@ -0,0 +1 @@ +532fbdb905cd4585e0ffeb2953d94a4678e1e6cd \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 00000000..4f4b8060 --- /dev/null +++ b/LICENSE @@ -0,0 +1,674 @@ +GNU GENERAL PUBLIC LICENSE + Version 3, 29 June 2007 + + Copyright (C) 2007 Free Software Foundation, Inc. [http://fsf.org/] + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The GNU General Public License is a free, copyleft license for +software and other kinds of works. + + The licenses for most software and other practical works are designed +to take away your freedom to share and change the works. By contrast, +the GNU General Public License is intended to guarantee your freedom to +share and change all versions of a program--to make sure it remains free +software for all its users. We, the Free Software Foundation, use the +GNU General Public License for most of our software; it applies also to +any other work released this way by its authors. You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +them if you wish), that you receive source code or can get it if you +want it, that you can change the software or use pieces of it in new +free programs, and that you know you can do these things. + + To protect your rights, we need to prevent others from denying you +these rights or asking you to surrender the rights. Therefore, you have +certain responsibilities if you distribute copies of the software, or if +you modify it: responsibilities to respect the freedom of others. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must pass on to the recipients the same +freedoms that you received. You must make sure that they, too, receive +or can get the source code. And you must show them these terms so they +know their rights. + + Developers that use the GNU GPL protect your rights with two steps: +(1) assert copyright on the software, and (2) offer you this License +giving you legal permission to copy, distribute and/or modify it. + + For the developers' and authors' protection, the GPL clearly explains +that there is no warranty for this free software. For both users' and +authors' sake, the GPL requires that modified versions be marked as +changed, so that their problems will not be attributed erroneously to +authors of previous versions. + + Some devices are designed to deny users access to install or run +modified versions of the software inside them, although the manufacturer +can do so. This is fundamentally incompatible with the aim of +protecting users' freedom to change the software. The systematic +pattern of such abuse occurs in the area of products for individuals to +use, which is precisely where it is most unacceptable. Therefore, we +have designed this version of the GPL to prohibit the practice for those +products. If such problems arise substantially in other domains, we +stand ready to extend this provision to those domains in future versions +of the GPL, as needed to protect the freedom of users. + + Finally, every program is threatened constantly by software patents. +States should not allow patents to restrict development and use of +software on general-purpose computers, but in those that do, we wish to +avoid the special danger that patents applied to a free program could +make it effectively proprietary. To prevent this, the GPL assures that +patents cannot be used to render the program non-free. + + The precise terms and conditions for copying, distribution and +modification follow. + + TERMS AND CONDITIONS + + 0. Definitions. + + "This License" refers to version 3 of the GNU General Public License. + + "Copyright" also means copyright-like laws that apply to other kinds of +works, such as semiconductor masks. + + "The Program" refers to any copyrightable work licensed under this +License. Each licensee is addressed as "you". "Licensees" and +"recipients" may be individuals or organizations. + + To "modify" a work means to copy from or adapt all or part of the work +in a fashion requiring copyright permission, other than the making of an +exact copy. The resulting work is called a "modified version" of the +earlier work or a work "based on" the earlier work. + + A "covered work" means either the unmodified Program or a work based +on the Program. + + To "propagate" a work means to do anything with it that, without +permission, would make you directly or secondarily liable for +infringement under applicable copyright law, except executing it on a +computer or modifying a private copy. Propagation includes copying, +distribution (with or without modification), making available to the +public, and in some countries other activities as well. + + To "convey" a work means any kind of propagation that enables other +parties to make or receive copies. Mere interaction with a user through +a computer network, with no transfer of a copy, is not conveying. + + An interactive user interface displays "Appropriate Legal Notices" +to the extent that it includes a convenient and prominently visible +feature that (1) displays an appropriate copyright notice, and (2) +tells the user that there is no warranty for the work (except to the +extent that warranties are provided), that licensees may convey the +work under this License, and how to view a copy of this License. If +the interface presents a list of user commands or options, such as a +menu, a prominent item in the list meets this criterion. + + 1. Source Code. + + The "source code" for a work means the preferred form of the work +for making modifications to it. "Object code" means any non-source +form of a work. + + A "Standard Interface" means an interface that either is an official +standard defined by a recognized standards body, or, in the case of +interfaces specified for a particular programming language, one that +is widely used among developers working in that language. + + The "System Libraries" of an executable work include anything, other +than the work as a whole, that (a) is included in the normal form of +packaging a Major Component, but which is not part of that Major +Component, and (b) serves only to enable use of the work with that +Major Component, or to implement a Standard Interface for which an +implementation is available to the public in source code form. A +"Major Component", in this context, means a major essential component +(kernel, window system, and so on) of the specific operating system +(if any) on which the executable work runs, or a compiler used to +produce the work, or an object code interpreter used to run it. + + The "Corresponding Source" for a work in object code form means all +the source code needed to generate, install, and (for an executable +work) run the object code and to modify the work, including scripts to +control those activities. However, it does not include the work's +System Libraries, or general-purpose tools or generally available free +programs which are used unmodified in performing those activities but +which are not part of the work. For example, Corresponding Source +includes interface definition files associated with source files for +the work, and the source code for shared libraries and dynamically +linked subprograms that the work is specifically designed to require, +such as by intimate data communication or control flow between those +subprograms and other parts of the work. + + The Corresponding Source need not include anything that users +can regenerate automatically from other parts of the Corresponding +Source. + + The Corresponding Source for a work in source code form is that +same work. + + 2. Basic Permissions. + + All rights granted under this License are granted for the term of +copyright on the Program, and are irrevocable provided the stated +conditions are met. This License explicitly affirms your unlimited +permission to run the unmodified Program. The output from running a +covered work is covered by this License only if the output, given its +content, constitutes a covered work. This License acknowledges your +rights of fair use or other equivalent, as provided by copyright law. + + You may make, run and propagate covered works that you do not +convey, without conditions so long as your license otherwise remains +in force. You may convey covered works to others for the sole purpose +of having them make modifications exclusively for you, or provide you +with facilities for running those works, provided that you comply with +the terms of this License in conveying all material for which you do +not control copyright. Those thus making or running the covered works +for you must do so exclusively on your behalf, under your direction +and control, on terms that prohibit them from making any copies of +your copyrighted material outside their relationship with you. + + Conveying under any other circumstances is permitted solely under +the conditions stated below. Sublicensing is not allowed; section 10 +makes it unnecessary. + + 3. Protecting Users' Legal Rights From Anti-Circumvention Law. + + No covered work shall be deemed part of an effective technological +measure under any applicable law fulfilling obligations under article +11 of the WIPO copyright treaty adopted on 20 December 1996, or +similar laws prohibiting or restricting circumvention of such +measures. + + When you convey a covered work, you waive any legal power to forbid +circumvention of technological measures to the extent such circumvention +is effected by exercising rights under this License with respect to +the covered work, and you disclaim any intention to limit operation or +modification of the work as a means of enforcing, against the work's +users, your or third parties' legal rights to forbid circumvention of +technological measures. + + 4. Conveying Verbatim Copies. + + You may convey verbatim copies of the Program's source code as you +receive it, in any medium, provided that you conspicuously and +appropriately publish on each copy an appropriate copyright notice; +keep intact all notices stating that this License and any +non-permissive terms added in accord with section 7 apply to the code; +keep intact all notices of the absence of any warranty; and give all +recipients a copy of this License along with the Program. + + You may charge any price or no price for each copy that you convey, +and you may offer support or warranty protection for a fee. + + 5. Conveying Modified Source Versions. + + You may convey a work based on the Program, or the modifications to +produce it from the Program, in the form of source code under the +terms of section 4, provided that you also meet all of these conditions: + + a) The work must carry prominent notices stating that you modified + it, and giving a relevant date. + + b) The work must carry prominent notices stating that it is + released under this License and any conditions added under section + 7. This requirement modifies the requirement in section 4 to + "keep intact all notices". + + c) You must license the entire work, as a whole, under this + License to anyone who comes into possession of a copy. This + License will therefore apply, along with any applicable section 7 + additional terms, to the whole of the work, and all its parts, + regardless of how they are packaged. This License gives no + permission to license the work in any other way, but it does not + invalidate such permission if you have separately received it. + + d) If the work has interactive user interfaces, each must display + Appropriate Legal Notices; however, if the Program has interactive + interfaces that do not display Appropriate Legal Notices, your + work need not make them do so. + + A compilation of a covered work with other separate and independent +works, which are not by their nature extensions of the covered work, +and which are not combined with it such as to form a larger program, +in or on a volume of a storage or distribution medium, is called an +"aggregate" if the compilation and its resulting copyright are not +used to limit the access or legal rights of the compilation's users +beyond what the individual works permit. Inclusion of a covered work +in an aggregate does not cause this License to apply to the other +parts of the aggregate. + + 6. Conveying Non-Source Forms. + + You may convey a covered work in object code form under the terms +of sections 4 and 5, provided that you also convey the +machine-readable Corresponding Source under the terms of this License, +in one of these ways: + + a) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by the + Corresponding Source fixed on a durable physical medium + customarily used for software interchange. + + b) Convey the object code in, or embodied in, a physical product + (including a physical distribution medium), accompanied by a + written offer, valid for at least three years and valid for as + long as you offer spare parts or customer support for that product + model, to give anyone who possesses the object code either (1) a + copy of the Corresponding Source for all the software in the + product that is covered by this License, on a durable physical + medium customarily used for software interchange, for a price no + more than your reasonable cost of physically performing this + conveying of source, or (2) access to copy the + Corresponding Source from a network server at no charge. + + c) Convey individual copies of the object code with a copy of the + written offer to provide the Corresponding Source. This + alternative is allowed only occasionally and noncommercially, and + only if you received the object code with such an offer, in accord + with subsection 6b. + + d) Convey the object code by offering access from a designated + place (gratis or for a charge), and offer equivalent access to the + Corresponding Source in the same way through the same place at no + further charge. You need not require recipients to copy the + Corresponding Source along with the object code. If the place to + copy the object code is a network server, the Corresponding Source + may be on a different server (operated by you or a third party) + that supports equivalent copying facilities, provided you maintain + clear directions next to the object code saying where to find the + Corresponding Source. Regardless of what server hosts the + Corresponding Source, you remain obligated to ensure that it is + available for as long as needed to satisfy these requirements. + + e) Convey the object code using peer-to-peer transmission, provided + you inform other peers where the object code and Corresponding + Source of the work are being offered to the general public at no + charge under subsection 6d. + + A separable portion of the object code, whose source code is excluded +from the Corresponding Source as a System Library, need not be +included in conveying the object code work. + + A "User Product" is either (1) a "consumer product", which means any +tangible personal property which is normally used for personal, family, +or household purposes, or (2) anything designed or sold for incorporation +into a dwelling. In determining whether a product is a consumer product, +doubtful cases shall be resolved in favor of coverage. For a particular +product received by a particular user, "normally used" refers to a +typical or common use of that class of product, regardless of the status +of the particular user or of the way in which the particular user +actually uses, or expects or is expected to use, the product. A product +is a consumer product regardless of whether the product has substantial +commercial, industrial or non-consumer uses, unless such uses represent +the only significant mode of use of the product. + + "Installation Information" for a User Product means any methods, +procedures, authorization keys, or other information required to install +and execute modified versions of a covered work in that User Product from +a modified version of its Corresponding Source. The information must +suffice to ensure that the continued functioning of the modified object +code is in no case prevented or interfered with solely because +modification has been made. + + If you convey an object code work under this section in, or with, or +specifically for use in, a User Product, and the conveying occurs as +part of a transaction in which the right of possession and use of the +User Product is transferred to the recipient in perpetuity or for a +fixed term (regardless of how the transaction is characterized), the +Corresponding Source conveyed under this section must be accompanied +by the Installation Information. But this requirement does not apply +if neither you nor any third party retains the ability to install +modified object code on the User Product (for example, the work has +been installed in ROM). + + The requirement to provide Installation Information does not include a +requirement to continue to provide support service, warranty, or updates +for a work that has been modified or installed by the recipient, or for +the User Product in which it has been modified or installed. Access to a +network may be denied when the modification itself materially and +adversely affects the operation of the network or violates the rules and +protocols for communication across the network. + + Corresponding Source conveyed, and Installation Information provided, +in accord with this section must be in a format that is publicly +documented (and with an implementation available to the public in +source code form), and must require no special password or key for +unpacking, reading or copying. + + 7. Additional Terms. + + "Additional permissions" are terms that supplement the terms of this +License by making exceptions from one or more of its conditions. +Additional permissions that are applicable to the entire Program shall +be treated as though they were included in this License, to the extent +that they are valid under applicable law. If additional permissions +apply only to part of the Program, that part may be used separately +under those permissions, but the entire Program remains governed by +this License without regard to the additional permissions. + + When you convey a copy of a covered work, you may at your option +remove any additional permissions from that copy, or from any part of +it. (Additional permissions may be written to require their own +removal in certain cases when you modify the work.) You may place +additional permissions on material, added by you to a covered work, +for which you have or can give appropriate copyright permission. + + Notwithstanding any other provision of this License, for material you +add to a covered work, you may (if authorized by the copyright holders of +that material) supplement the terms of this License with terms: + + a) Disclaiming warranty or limiting liability differently from the + terms of sections 15 and 16 of this License; or + + b) Requiring preservation of specified reasonable legal notices or + author attributions in that material or in the Appropriate Legal + Notices displayed by works containing it; or + + c) Prohibiting misrepresentation of the origin of that material, or + requiring that modified versions of such material be marked in + reasonable ways as different from the original version; or + + d) Limiting the use for publicity purposes of names of licensors or + authors of the material; or + + e) Declining to grant rights under trademark law for use of some + trade names, trademarks, or service marks; or + + f) Requiring indemnification of licensors and authors of that + material by anyone who conveys the material (or modified versions of + it) with contractual assumptions of liability to the recipient, for + any liability that these contractual assumptions directly impose on + those licensors and authors. + + All other non-permissive additional terms are considered "further +restrictions" within the meaning of section 10. If the Program as you +received it, or any part of it, contains a notice stating that it is +governed by this License along with a term that is a further +restriction, you may remove that term. If a license document contains +a further restriction but permits relicensing or conveying under this +License, you may add to a covered work material governed by the terms +of that license document, provided that the further restriction does +not survive such relicensing or conveying. + + If you add terms to a covered work in accord with this section, you +must place, in the relevant source files, a statement of the +additional terms that apply to those files, or a notice indicating +where to find the applicable terms. + + Additional terms, permissive or non-permissive, may be stated in the +form of a separately written license, or stated as exceptions; +the above requirements apply either way. + + 8. Termination. + + You may not propagate or modify a covered work except as expressly +provided under this License. Any attempt otherwise to propagate or +modify it is void, and will automatically terminate your rights under +this License (including any patent licenses granted under the third +paragraph of section 11). + + However, if you cease all violation of this License, then your +license from a particular copyright holder is reinstated (a) +provisionally, unless and until the copyright holder explicitly and +finally terminates your license, and (b) permanently, if the copyright +holder fails to notify you of the violation by some reasonable means +prior to 60 days after the cessation. + + Moreover, your license from a particular copyright holder is +reinstated permanently if the copyright holder notifies you of the +violation by some reasonable means, this is the first time you have +received notice of violation of this License (for any work) from that +copyright holder, and you cure the violation prior to 30 days after +your receipt of the notice. + + Termination of your rights under this section does not terminate the +licenses of parties who have received copies or rights from you under +this License. If your rights have been terminated and not permanently +reinstated, you do not qualify to receive new licenses for the same +material under section 10. + + 9. Acceptance Not Required for Having Copies. + + You are not required to accept this License in order to receive or +run a copy of the Program. Ancillary propagation of a covered work +occurring solely as a consequence of using peer-to-peer transmission +to receive a copy likewise does not require acceptance. However, +nothing other than this License grants you permission to propagate or +modify any covered work. These actions infringe copyright if you do +not accept this License. Therefore, by modifying or propagating a +covered work, you indicate your acceptance of this License to do so. + + 10. Automatic Licensing of Downstream Recipients. + + Each time you convey a covered work, the recipient automatically +receives a license from the original licensors, to run, modify and +propagate that work, subject to this License. You are not responsible +for enforcing compliance by third parties with this License. + + An "entity transaction" is a transaction transferring control of an +organization, or substantially all assets of one, or subdividing an +organization, or merging organizations. If propagation of a covered +work results from an entity transaction, each party to that +transaction who receives a copy of the work also receives whatever +licenses to the work the party's predecessor in interest had or could +give under the previous paragraph, plus a right to possession of the +Corresponding Source of the work from the predecessor in interest, if +the predecessor has it or can get it with reasonable efforts. + + You may not impose any further restrictions on the exercise of the +rights granted or affirmed under this License. For example, you may +not impose a license fee, royalty, or other charge for exercise of +rights granted under this License, and you may not initiate litigation +(including a cross-claim or counterclaim in a lawsuit) alleging that +any patent claim is infringed by making, using, selling, offering for +sale, or importing the Program or any portion of it. + + 11. Patents. + + A "contributor" is a copyright holder who authorizes use under this +License of the Program or a work on which the Program is based. The +work thus licensed is called the contributor's "contributor version". + + A contributor's "essential patent claims" are all patent claims +owned or controlled by the contributor, whether already acquired or +hereafter acquired, that would be infringed by some manner, permitted +by this License, of making, using, or selling its contributor version, +but do not include claims that would be infringed only as a +consequence of further modification of the contributor version. For +purposes of this definition, "control" includes the right to grant +patent sublicenses in a manner consistent with the requirements of +this License. + + Each contributor grants you a non-exclusive, worldwide, royalty-free +patent license under the contributor's essential patent claims, to +make, use, sell, offer for sale, import and otherwise run, modify and +propagate the contents of its contributor version. + + In the following three paragraphs, a "patent license" is any express +agreement or commitment, however denominated, not to enforce a patent +(such as an express permission to practice a patent or covenant not to +sue for patent infringement). To "grant" such a patent license to a +party means to make such an agreement or commitment not to enforce a +patent against the party. + + If you convey a covered work, knowingly relying on a patent license, +and the Corresponding Source of the work is not available for anyone +to copy, free of charge and under the terms of this License, through a +publicly available network server or other readily accessible means, +then you must either (1) cause the Corresponding Source to be so +available, or (2) arrange to deprive yourself of the benefit of the +patent license for this particular work, or (3) arrange, in a manner +consistent with the requirements of this License, to extend the patent +license to downstream recipients. "Knowingly relying" means you have +actual knowledge that, but for the patent license, your conveying the +covered work in a country, or your recipient's use of the covered work +in a country, would infringe one or more identifiable patents in that +country that you have reason to believe are valid. + + If, pursuant to or in connection with a single transaction or +arrangement, you convey, or propagate by procuring conveyance of, a +covered work, and grant a patent license to some of the parties +receiving the covered work authorizing them to use, propagate, modify +or convey a specific copy of the covered work, then the patent license +you grant is automatically extended to all recipients of the covered +work and works based on it. + + A patent license is "discriminatory" if it does not include within +the scope of its coverage, prohibits the exercise of, or is +conditioned on the non-exercise of one or more of the rights that are +specifically granted under this License. You may not convey a covered +work if you are a party to an arrangement with a third party that is +in the business of distributing software, under which you make payment +to the third party based on the extent of your activity of conveying +the work, and under which the third party grants, to any of the +parties who would receive the covered work from you, a discriminatory +patent license (a) in connection with copies of the covered work +conveyed by you (or copies made from those copies), or (b) primarily +for and in connection with specific products or compilations that +contain the covered work, unless you entered into that arrangement, +or that patent license was granted, prior to 28 March 2007. + + Nothing in this License shall be construed as excluding or limiting +any implied license or other defenses to infringement that may +otherwise be available to you under applicable patent law. + + 12. No Surrender of Others' Freedom. + + If conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot convey a +covered work so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you may +not convey it at all. For example, if you agree to terms that obligate you +to collect a royalty for further conveying from those to whom you convey +the Program, the only way you could satisfy both those terms and this +License would be to refrain entirely from conveying the Program. + + 13. Use with the GNU Affero General Public License. + + Notwithstanding any other provision of this License, you have +permission to link or combine any covered work with a work licensed +under version 3 of the GNU Affero General Public License into a single +combined work, and to convey the resulting work. The terms of this +License will continue to apply to the part which is the covered work, +but the special requirements of the GNU Affero General Public License, +section 13, concerning interaction through a network will apply to the +combination as such. + + 14. Revised Versions of this License. + + The Free Software Foundation may publish revised and/or new versions of +the GNU General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + + Each version is given a distinguishing version number. If the +Program specifies that a certain numbered version of the GNU General +Public License "or any later version" applies to it, you have the +option of following the terms and conditions either of that numbered +version or of any later version published by the Free Software +Foundation. If the Program does not specify a version number of the +GNU General Public License, you may choose any version ever published +by the Free Software Foundation. + + If the Program specifies that a proxy can decide which future +versions of the GNU General Public License can be used, that proxy's +public statement of acceptance of a version permanently authorizes you +to choose that version for the Program. + + Later license versions may give you additional or different +permissions. However, no additional obligations are imposed on any +author or copyright holder as a result of your choosing to follow a +later version. + + 15. Disclaimer of Warranty. + + THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY +APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT +HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY +OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, +THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM +IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF +ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. Limitation of Liability. + + IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS +THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY +GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE +USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF +DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD +PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), +EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF +SUCH DAMAGES. + + 17. Interpretation of Sections 15 and 16. + + If the disclaimer of warranty and limitation of liability provided +above cannot be given local legal effect according to their terms, +reviewing courts shall apply local law that most closely approximates +an absolute waiver of all civil liability in connection with the +Program, unless a warranty or assumption of liability accompanies a +copy of the Program in return for a fee. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +state the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + {one line to give the program's name and a brief idea of what it does.} + Copyright (C) {year} {fullname} + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see [http://www.gnu.org/licenses/]. + +Also add information on how to contact you by electronic and paper mail. + + If the program does terminal interaction, make it output a short +notice like this when it starts in an interactive mode: + + agile-bpm-basic Copyright (C) 2018 jeff + This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, your program's commands +might be different; for a GUI interface, you would use an "about box". + + You should also get your employer (if you work as a programmer) or school, +if any, to sign a "copyright disclaimer" for the program, if necessary. +For more information on this, and how to apply and follow the GNU GPL, see +[http://www.gnu.org/licenses/]. + + The GNU General Public License does not permit incorporating your program +into proprietary programs. If your program is a subroutine library, you +may consider it more useful to permit linking proprietary applications with +the library. If this is what you want to do, use the GNU Lesser General +Public License instead of this License. But first, please read +[http://www.gnu.org/philosophy/why-not-lgpl.html]. \ No newline at end of file diff --git a/LICENSE_补充协议 b/LICENSE_补充协议 new file mode 100644 index 00000000..3cafb858 --- /dev/null +++ b/LICENSE_补充协议 @@ -0,0 +1,58 @@ +AgileBPM 开源版本允许商用 + +除了遵循GPL开源协议外,请严格遵循以下协议。 + +该协议从2020年8月21日 起效,如果有违该协议的请于一月内改正,否则您将面临法律风险! +该协议会在AgileBPM官方网站公告登出,任何涉事企业请勿抱侥幸心理,请勿把法律当儿戏! + +## 禁止条款 + +### 国家法规 + +1、遵守《中华人民共和国计算机信息系统安全保护条例》,禁止侵犯计算机软件著作权。 + +2、任何组织或者个人、不得利用计算机信息系统从事危害国家利益、集体利益和公民合法利益的活动,不得危害计算机信息系统的安全。 + +3、任何单位和个人不得自行建立或者使用其他信道进行国际联网。 + +4、从事国际联网业务的单位和个人,应当遵守国家有关法律、行政法规,严格执行安全保密制度,不得利用国际联网从事危害国家安全、泄露国家秘密等违法犯罪活动,不得制作、查阅、复制和传播妨碍社会治安的信息和淫秽色情等信息。 + +5、任何组织或个人,不得利用计算机国际联网从事危害国家安全、泄露国家秘密等犯罪活动;不得利用计算机国际联网查阅、复制、制造和传播危害国家安全、妨碍社会治安和淫秽色情的信息。发现上述违法犯罪行为和有害信息,应及时向有关主管机关报告。 + +6、任何组织或个人,不得利用计算机国际联网从事危害他人信息系统和网络安全,侵犯他人合法权益的活动。 + +7、国际联网用户应当服从接入单位的管理,遵守用户守则;不得擅自进入未经许可的计算机系统,篡改他人信息;不得在网络上散发恶意信息,冒用他人名义发出信息,侵犯他人隐私;不得制造、传播计算机病毒及从事其他侵犯网络和他人合法权益的活动。 + +8、任何单位和个人发现计算机信息系统泄密后,应及时采取补救措施,并按有关规定及时向上级报告。 + +9、任何单位和个人不得利用国际联网危害国家安全、泄露国家秘密,不得侵犯国家的、社会的、集体的利益和公民的合法权益,不得从事违法犯罪活动。 + +10、任何单位和个人不得利用国际联网制作、复制、查阅和传播下列信息: + +1. 煽动抗拒、破坏宪法和法律、行政法规实施的; +2. 煽动颠覆国家政权,推翻社会主义制度的; +3. 煽动分裂国家、破坏国家统一的; +4. 煽动民族仇恨、民族歧视,破坏民族团结的; +5. 捏造或者歪曲事实,散布谣言,扰乱社会秩序的; +6. 宣扬封建迷信、淫秽、色情、赌博、暴力、凶杀、恐怖,教唆犯罪的; +7. 公然侮辱他人或者捏造事实诽谤他人的; +8. 损害国家机关信誉的; +9. 其他违反宪法和法律、行政法规的。 + +### AgileBPM 补充协议 +11、禁止将AgileBPM以流程开发平台的形式二次出售。 + +12、基于AgileBPM二次开发的产品,或者应用了AgileBPM部分功能或者源代码块的产品,这些产品的名称、产品宣传词禁止 包含 “BPM”,“流程”,“开发平台”关键字。 + +13、不论何时通过任何渠道获得的AgileBPM开源源代码、或者部分功能源代码、或者代码片段均受该协议约束。 + +## 违约条款 +1、如果违背以上任何禁止条款,AgileBPM将禁止使用AgileBPM的任何功能以及源代码。 + +2、违背协议相关企业的产品、以及该产品的衍生产品,以及已经出售了的应用于客户公司的相关产品,均需要移除AgileBPM相关功能源代码,否则相关企业以及受众客户都同样面临侵权问题。 + +3、如果违背协议者应用了部分功能,需要在该协议生效一月内即2020年9月21日移除先关源代码功能。 + +4、如果违背国家相关法律条款的必须立即删除,并根据相关条款予以处罚。 + +5、如果违背“AgileBPM 补充协议”且2020年9月21日前不予改正的企业以及相关企业,我们将追究法律责任,每个侵权程序将处于500000RMB罚款并且缴纳相关违规所得。 \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..7d1a2c67 --- /dev/null +++ b/README.md @@ -0,0 +1,77 @@ +# agilebpm-basic + +## 敏捷开发平台 + +全新开源的 vue3 TS 的前后台分离的开发平台 + +前端基于 `Vue3` 组合式API, `TypeScript`, `Element-plus`,`Pinia`,`Axios`,支持三种布局, 自定义主题风格 +后端基于 `AgileBPM` 流程表单,`SpringBoot `,`MybatisPlus` ,`Activiti7` ,`Jackson` , `JDK17` ,`Hutool` 等主流技术栈 + +后端是 Maven模块化管理的SOA的 SpringBoot 单体架构,模块间低耦合,可选择模块组合成微服务架构。 + + +## 起步 + +1. 下载 `agile-bpm-basic`项目,以 maven 项目引入工程 并 根目录下 执行 `mvn clean install -DskipTests` + +2. 执行数据库脚本创建数据库 `/doc/sql/mysql/full/agilebpm_full.sql` + +3. 修改数据库、Redis 等配置文件`\ab-spring-boot\ab-spring-boot-app\src\main\resources\application.yml`,配置文件会有详细注释,请自行修改。 + +4. 通过 Main 方法启动后端服务 `\ab-spring-boot\ab-spring-boot-app\src\main\java\com\dstz\AbSpringBootApp.java`,默认端口为 `8080` + +5. 下载 前端工程 `agilebpm-ui` ,并在根目录下 执行 命令 `yarn install` + +6. 完成安装后,在 `vite.config.ts`中修改后台服务地址,如 `http://localhost:8080/` 然后执行 ` npm run dev ` 启动前端项目。 + +默认访问 前端地址 `http://127.0.0.1:8088/` 即可体验项目 + + +推荐 vscode 作为前端开发IDE,请务必安装一下插件 +- local-history (opens new window)local-history](可找回丢失代码) +- eslint (opens new window)eslint(建议开启 Eslint 保存时自动修复) +- stylelint (opens new window)stylelint +- Prettier - Code formatter 代码自动格式化 +- volar (opens new window)vue3 开发必备 + + +## 目前 `2.5` 版本涵盖了以下功能 + +- 个人办公: 待办事项、抄送传阅、办理历史、发起申请、申请历史、我的草稿 + +- 内容管理: 公告、新闻 + +- 组织管理:组织管理、用户管理、角色管理、 岗位管理。笔者十多年研发见过无数组织架构,它堪称最精简最完美的设计。 + +- 流程管理: 表单表单设计、流程设计、流程实例管理、任务管理、系统对话框管理。(目前基于AgileBPM的商业组件,如果觉得不合适可切换为其他框架的流程模块,目前没有能入眼的) + +- 系统管理:字典分类管理、异常日志、系统资源、系统属性、常用脚本、短信邮件通知 + +## 系统界面预览 + + + +## 开发计划 + +### v 2.6 计划新增 OA 人事模块 + +规划包含 招聘需求、招聘面试、员工档案、转正、调岗、离职、 等功能 + +### 规划中排期待定的事项 + +- 人事中 请假、加班、出差、外勤打卡 + +- 资产管理 的资产信息、资产领用、资产转移、采购申请 + +- 持续维护前端工具包,以及前端组件库,等组件库稳定后开放源码出来 + +- 维护系统中用的部分功能的 TS 类型定义,由于目前开发工作繁重,部分页面 暂未维护。 + +- 首页组件,如公告新闻、待办等 + +- 常用语,适配黑夜模式,全面支持国际化,站内消息通知 + +欢迎有兴趣的同学 pull request ,或者提需求 + + + diff --git a/ab-auth/ab-auth-api/pom.xml b/ab-auth/ab-auth-api/pom.xml new file mode 100644 index 00000000..f2db6e7e --- /dev/null +++ b/ab-auth/ab-auth-api/pom.xml @@ -0,0 +1,30 @@ + + + + ab-auth + com.dstz + 2.5.0 + + 4.0.0 + + ab-auth-api + + + + com.dstz + ab-base-common + ${project.version} + + + com.dstz + ab-org-api + ${project.version} + + + org.springframework.security + spring-security-core + + + \ No newline at end of file diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/MultipleFromAuthentication.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/MultipleFromAuthentication.java new file mode 100644 index 00000000..56dc93ed --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/MultipleFromAuthentication.java @@ -0,0 +1,31 @@ +package com.dstz.auth.authentication.api; + + +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UserDetails; + +/** + * 多来源登录认证 + * + * @author lightning + */ +public interface MultipleFromAuthentication { + + /** + * 获取来源 + * + * @return 来源 + */ + String getFrom(); + + /** + * 鉴权认证 + * + * @param userDetails 登录用户详情 + * @param authentication 用户密码鉴权串 + * @throws AuthenticationException 鉴权异常 + */ + void authentication(UserDetails userDetails, UsernamePasswordAuthenticationToken authentication) throws AuthenticationException; + +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysApplicationApi.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysApplicationApi.java new file mode 100644 index 00000000..57d57172 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysApplicationApi.java @@ -0,0 +1,34 @@ +package com.dstz.auth.authentication.api; + +import com.dstz.auth.authentication.vo.SysApplicationVO; + +import java.util.List; + +/** + * 系统应用接口 + * + * @author wacxhs + */ +public interface SysApplicationApi{ + + /** + * 根据编码获取系统应用 + * + * @param code 编码 + * @return 系统应用 + */ + SysApplicationVO getByCode(String code); + + /** + * 获取所有应用编码 + * + * @return 所有应用编码 + */ + List getAllCode(); + + /** + * 获取当前可用的移动端主应用 + * @return + */ + SysApplicationVO getEnabledMobileApp(); +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysResourceApi.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysResourceApi.java new file mode 100644 index 00000000..9cf5c12c --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/SysResourceApi.java @@ -0,0 +1,63 @@ +package com.dstz.auth.authentication.api; + +import com.dstz.auth.authentication.api.model.ISysApplication; +import com.dstz.auth.authentication.api.model.ISysResource; + +import java.util.List; +import java.util.Set; +/** + * 系统资源接口 + * + * @author lightning + */ +public interface SysResourceApi { + + /** + * 通过资源id获取资源对象 + * @param id 资源ID + * @return ISysResource 资源对象 + */ + ISysResource getResourceById(String id); + + /** + * 通过资源code集合删除资源对象 + * @param code 资源code集合 + */ + void deleteResourceByCode(List code); + + /** + * 获取当前用户拥有的系统 + * @return + */ + List getCurrentUserSystem(); + /** + * 获取默认系统 + * @return + */ + ISysApplication getDefaultSystem(String currentUserId); + /** + * 根据id获取系统资源 + * @return + */ + List getBySystemId(String systemId); + /** + * 根据systemId和userId获取系统资源 + * @return + */ + List getByAppIdAndUser(String appId, String userId); + /** + * 根据url获取拥有资源 + * @return + */ + Set getAccessRoleByUrl(String url); + + /** + * 根据url查询是url还是方法接口 + */ + boolean isRoleByUrl(String url); + + /** + * 获取移动端办理事项的资源 + */ + ISysResource getTodoResource(); +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthApiConstant.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthApiConstant.java new file mode 100644 index 00000000..44be0a79 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthApiConstant.java @@ -0,0 +1,96 @@ +package com.dstz.auth.authentication.api.constant; + +/** + * auth模块常量 + * + * @author lightning + */ +public class AuthApiConstant { + private AuthApiConstant() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * 来源-system + */ + public static final String SYSTEM = "system"; + /** + * 系统别名 + */ + public static final String AGILEBPM = "agilebpm"; + /** + * 登录超时跳转url + */ + public static final String SSO_REDIRECTURL = "SSO_redirectUrl"; + + /** + * 超管账户 + */ + public static final String ADMIN = "admin"; + + //----------oauth2相关配置------------ + + /** + * 令牌默认有效期2小时 + */ + public static final Integer ACCESSTOKEN_VALIDITY_SECONDS = 7200; + + /** + * 刷新令牌默认有效期3天 + */ + public static final Integer REFRESHTOKEN_VALIDITY_SECONDS = 259200; + + + public static final String AUTHORIZATION = "Authorization"; + + public static final String BEARER = "Bearer"; + + /** + * 配置令牌端点安全约束配置 + */ + public static final String TOKEN_SERVER_SECURITY_CONFIGURER = "permitAll()"; + + /** + * 定制授权页 + */ + public static final String TOKEN_SERVER_AUTH_DEFAULTPATH = "/oauth/confirm_access"; + + /** + * 定制授权页 + */ + public static final String TOKEN_SERVER_AUTH_CUSTOMPATH = "/customer/confirm_access"; + + /** + * oauth 客户端key + */ + public static final String OAUTH_TOKEN_CLIENT_KEY = "client_id"; + + public static final String OAUTH_TOKEN_CLIENT_SECRET = "client_secret"; + + + /** + * oauth 客户端 refresh_token + */ + public static final String OAUTH_TOKEN_REFRESH_TOKEN = "refresh_token"; + + /** + * oauth 客户端grant_type + */ + public static final String OAUTH_TOKEN_GRANT_TYPE = "grant_type"; + + + + + + + /** + * oauth 客户端username + */ + public static final String OAUTH_TOKEN_USER_NAME = "username"; + + /** + * oauth 客户端password + */ + public static final String OAUTH_TOKEN_PASSWORD = "password"; + +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthCacheKeyConstant.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthCacheKeyConstant.java new file mode 100644 index 00000000..4f5bf40f --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthCacheKeyConstant.java @@ -0,0 +1,59 @@ +package com.dstz.auth.authentication.api.constant; + +import javax.swing.*; + +/** + * 缓存key常量 + * + * @author lightning + */ +public class AuthCacheKeyConstant { + + /** + * auth_client 方法缓存key + */ + public static final String CACHE_REGION_AUTH_CLIENT = "OAUTH_REGION"; + /** + * auth_client 方法缓存key SpEL表达式 + */ + public static final String AUTH_CLIENT_RECEIVE_EL = "'authClientEl:'.concat(#root.args[0])"; + + public static final String AUTH_APP_LIST_EL = "'authAppListEl:"; + + /** + * auth_client 方法缓存key SpEL表达式 + */ + public static final String CACHE_EVICT_AUTH_CLIENT_RECEIVE_EL = "'authClientEl:'.concat(#root.args[0].getCode())"; + + /** + * 根据url缓存权限映射 + */ + public static final String URL_ROLE_MAPPING = "agilebpm:sys:resoucesUrlRoleMapping:"; + /** + * 登录用户缓存key + */ + public static final String LOGIN_USER_CACHE_KEY = "agilebpm:loginUser:"; + + /** + * 用户token缓存 + */ + public static final String USER_TOKEN = "user:token:"; + + + /** + * 用户登录次数统计 + */ + public static final String USER_LOGIN_COUNT = "user:login:count:"; + + /** + * 忽略鉴权列表 + */ + public static final String IGNORE_AUTH_URL_LIST = "'ignore:auth:url'"; + + /** + * 判断url是否需要方法鉴权 + */ + public static final String IS_ROLE = "'isRole:'.concat(#root.args[0])"; + + +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthStatusCode.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthStatusCode.java new file mode 100644 index 00000000..d18cc9d6 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/AuthStatusCode.java @@ -0,0 +1,169 @@ +package com.dstz.auth.authentication.api.constant; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * 鉴权接口响应码 + * + * @author lightning + */ +public enum AuthStatusCode implements IBaseCode { + + /** + * 必传参数为空 + */ + PARAM_CLIENT_ID_IS_NOT_FOUND("param_client_id_is_not_found", "必传参数clientId为空"), + /** + * 账户或密码不对 + */ + LOGIN_ERROR("login_error", "账户或密码不对"), + + USER_NAME_OR_PASSORD_ERROR("user_name_or_passord_error", "{}"), + /** + * 应用配置错误 + */ + APP_CONFIG_ERROR("app_config_error", "应用配置错误"), + /** + * 账户找不到 + */ + ACCOUNT_NOT_FIND("account_not_find", "账户不存在"), + /** + * 账户不能为空 + */ + ACCOUNT_CANNOT_BE_EMPTY("account_cannot_be_empty", "账户不能为空"), + /** + * 密码不能为空 + */ + PASSWORD_CANNOT_BE_EMPTY("password_cannot_be_empty", "密码不能为空"), + /** + * 帐号已禁用 + */ + ACCOUNT_DISABLED("account_disabled", "帐号已禁用"), + /** + * 帐号已锁定 + */ + ACCOUNT_IS_LOCKED("account_is_locked", "帐号已锁定"), + /** + * 帐号已过期 + */ + ACCOUNT_HAS_EXPIRED("account_has_expired", "帐号已过期"), + /** + * 账号或密码错误次数过多,将禁止登录 + */ + DISABLE_LOGIN("disable_login", "账号或密码错误次数过多,将禁止登录{}"), + + DISABLE_LOGIN_WARN("disable_login_warn","账户密码错误,已经失败 {} 次,若连续失败 {} 次将禁止登录{}"), + /** + * 验证码不能为空 + */ + CAPTCHA_CANNOT_BE_EMPTY("captcha_cannot_be_empty", "验证码不能为空"), + /** + * 验证类型 + */ + GRANTTYPE_CANNOT_BE_EMPTY("granttype_cannot_be_empty", "验证类型不能为空"), + /** + * 验证码错误 + */ + CAPTCHA_ERROR("captcha_error", "验证码错误,请重试"), + /** + * 密码为初始密码 + */ + PASSWORD_NEEDS_TO_BE_CHANGED("password_needs_to_be_changed", "密码为初始密码,请修改密码后登录"), + /** + * 当前用户尚未分配任何资源 + */ + USER_HAS_NOT_ASSIGNED_ANY_RESOURCES("user_has_not_assigned_any_resources", "当前用户尚未分配任何资源"), + + APPLICATION_NO_PERMISSIONS("application_no_permissions", "没有该应用的权限:{}"), + + RESOURCES_CODE_REPEAT("resources_code_repeat", "别名已存在,请修改!:{}"), + + DELETE_RESOURCES_ERROR("delete_resources_error", "删除子系统资源失败"), + + SYS_IS_NOT_DEFINITION("sys_is_not_definition", "当前系统“{} ”不存在! 请添加子系统,并配置资源菜单!\" : \"您没有当前系统“{}”的访问权限!请联系管理员。"), + + PARAM_IS_NULL("param_is_null", "必传参数 {} 为空"), + + APPLICATION_GET_TOKEN_ERROR("application_get_token_error", "用户:{} 获取token异常 clientid:{}"), + APPLICATION_REFRESH_TOKEN_ERROR("application_refresh_token_error", "刷新token异常 clientid:{}"), + /** + * 根据当前clientid未查到数据 + */ + LOADCLIENT_BY_CLIENTID_ERROR("loadclient_by_clientid_error", "根据当前clientid未查到数据"), + + /** + * There is no client authentication. Try adding an appropriate authentication filter + */ + NO_CLIENT_AUTHENTICATION("no_client_authentication", "没有找到匹配的身份验证过滤器"), + + LOGIN_TIMEOUT("LOGIN_TIMEOUT", "登录超时,请重新登录"), + + NO_AUTH_TOKEN("no_auth_token", "请求未传入鉴权信息"), + + TOKEN_INVALID("token_invalid", "登录超时"), + + NOT_PERMISSION("not_permission", "权限不足"), + + LOAD_CLIENT_BY_CLIENTID_ERROR("load_client_by_clientid_error", "加载应用信息异常:{}"), + + NO_FIND_APP("no_find_app", "根据client_id:{} 找不到对应 应用"), + + RESOUCE_TYPE_CONSTANT_ERROR("resouce_type_constant_error", "菜单类型的资源,上级资源 {} 也必须是菜单!"), + + METHOD_NOT_ALLOWED("method_not_allowed", "方法未被允许"), + + SERVICE_ERROR("service_error", "内部错误"), + + LOGIN_AUTHORIZATION_FILTER_ERROR("login_authorization_filter_error", "鉴权过滤器AuthorizationTokenCheckFilter获取token异常"), + + /** + * 非法的当前用户组织,一般引起该错误,例如管理员分配了组织或者用户修改了请求参数 + */ + ILLEGAL_CURRENT_ORG("IllegalCurrentOrg", "非法的当前用户组织, 非法组织编码:{}"), + + /** + * 用户未分配组织 + */ + USER_UNABSORBED_ORG("UNABSORBED_ORG", "用户未分配组织"), + + /** + * 应用无菜单资源 + */ + APP_NO_RESOURCE("AppNoResource", "切换系统失败,该系统下没有可访问的菜单资源"), + + + /** + * 该请求未分配给任何角色,或接口不存在 + */ + API_NONEXISTENT_OR_NO_ACCESS("api_nonexistent_or_no_access", "该请求未分配给任何角色,或接口不存在:{}"), + + + /** + * 您没有该接口的访问权限 + */ + API_NO_ACCESS("api_no_access", "您没有该接口的访问权限:"), + + + APPCLICATION_ENABLE_DEFAULT_MOBILE_APP_NOT_FIND("appclication_enable_default_mobile_app_not_find", "系统找不到移动端默认应用,请先配置移动端默认应用"), + + + ; + + private final String code; + private final String desc; + + AuthStatusCode(String code, String description) { + this.code = code; + this.desc = description; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return desc; + } +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/ResouceTypeConstant.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/ResouceTypeConstant.java new file mode 100644 index 00000000..a38a08dc --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/constant/ResouceTypeConstant.java @@ -0,0 +1,38 @@ +package com.dstz.auth.authentication.api.constant; + +/** + * 组织级别 + * + * @author lightning + */ +public enum ResouceTypeConstant { + /** + * 菜单 + */ + MENU("menu", "菜单"), + /** + * 链接 + */ + LINK("link", "链接"), + /** + * 按钮 + */ + BUTTON("button", "按钮"); + + private final String key; + private final String label; + + + ResouceTypeConstant(String key, String label) { + this.key = key; + this.label = label; + } + + public String getLabel() { + return label; + } + + public String getKey() { + return key; + } +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysApplication.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysApplication.java new file mode 100644 index 00000000..55896eed --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysApplication.java @@ -0,0 +1,93 @@ +package com.dstz.auth.authentication.api.model; + +/** + * 系统应用 + * + * @author lightning + */ +public interface ISysApplication { + + /** + * 返回 主键 + * + * @return + */ + String getId(); + + /** + * 返回 系统名称 + * + * @return + */ + String getName(); + + /** + * 返回 系统别名 + * + * @return + */ + String getCode(); + + + /** + * 密匙 + */ + String getSecret(); + + /** + * 刷新秒数 + */ + Integer getRefreshTokenValidity(); + + /** + * 有效期 + */ + Integer getAccessTokenValidity(); + + /** + * 系统地址,空则为当前系统 + */ + String getUrl(); + + + /** + * 回调地址 + */ + String getRedirectUri(); + + /** + * 打开方式 + */ + String getOpenType(); + + + /** + * 返回 是否可用 1 可用,0 ,不可用 + * + * @return + */ + Integer getEnabled(); + + /** + * 是否默认 + */ + Integer getIsDefault(); + + /** + * 描述备注 + */ + String getDesc(); + + /** + * 扩展配置 + */ + String getConfig(); + + /** + * 是否可用app + * @return + */ + Integer getAppType(); + + +} \ No newline at end of file diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysResource.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysResource.java new file mode 100644 index 00000000..4d8f9ed0 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/api/model/ISysResource.java @@ -0,0 +1,97 @@ +package com.dstz.auth.authentication.api.model; + +import com.dstz.base.api.model.Tree; +/** + * 系统资源 + * + * @author lightning + */ +public interface ISysResource extends Tree { + + + /** + * 返回 主键 + * + * @return + */ + public String getId(); + + /** + * 返回 子系统ID + * + * @return + */ + public String getAppId(); + + /** + * 返回 资源别名 + * + * @return + */ + public String getCode(); + + + public String getPath(); + + /** + * 返回 资源名 + * + * @return + */ + public String getName(); + + /** + * 返回 默认地址 + * + * @return + */ + public String getUrl(); + + /** + * 返回 显示到菜单(1,显示,0 ,不显示) + * + * @return + */ + public Integer getEnable(); + + /** + * 返回 OPENED_ + * + * @return + */ + public Integer getOpened(); + + /** + * 是否API资源 + */ + + Integer getIsApi(); + + /** + * 返回 图标 + * + * @return + */ + public String getIcon(); + + /** + * 返回类型 + * + * @return + */ + public String getType(); + + /** + * 返回 排序 + * + * @return + */ + public Integer getSn(); + + + public String getParentId(); + + Boolean getHidden(); + + +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/dto/UserLoginDTO.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/dto/UserLoginDTO.java new file mode 100644 index 00000000..ce569aa2 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/dto/UserLoginDTO.java @@ -0,0 +1,72 @@ +package com.dstz.auth.authentication.dto; + +public class UserLoginDTO implements java.io.Serializable { + + /** + * 账户 + */ + private String account; + + /** + * 密码 + */ + private String password; + + /** + * 验证码 + */ + private String captcha; + + + /** + * 接收方,pc/mobile/android" + */ + private String audience = "pc"; + + + /** + * 记住账户 + */ + private String rememberMe; + + + public String getAudience() { + return audience; + } + + public void setAudience(String audience) { + this.audience = audience; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getCaptcha() { + return captcha; + } + + public void setCaptcha(String captcha) { + this.captcha = captcha; + } + + public String getRememberMe() { + return rememberMe; + } + + public void setRememberMe(String rememberMe) { + this.rememberMe = rememberMe; + } +} diff --git a/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/vo/SysApplicationVO.java b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/vo/SysApplicationVO.java new file mode 100644 index 00000000..01a44658 --- /dev/null +++ b/ab-auth/ab-auth-api/src/main/java/com/dstz/auth/authentication/vo/SysApplicationVO.java @@ -0,0 +1,128 @@ +package com.dstz.auth.authentication.vo; + +/** + * 系统应用 + * + * @author wacxhs + */ +public class SysApplicationVO implements java.io.Serializable { + + private static final long serialVersionUID = 5700878947689459575L; + + /** + * 编码 + */ + private String code; + + /** + * 密匙 + */ + private String secret; + + /** + * 授权范围 + */ + private String scope; + + /** + * 资源集合 + */ + private String resourceIds; + + /** + * 授权类型 + */ + private String grantTypes; + + /** + * 回调地址 + */ + private String redirectUri; + + /** + * 自动授权 + */ + private Integer autoapprove; + + /** + * 有效期 + */ + private Integer accessTokenValidity; + + /** + * 刷新秒数 + */ + private Integer refreshTokenValidity; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public String getResourceIds() { + return resourceIds; + } + + public void setResourceIds(String resourceIds) { + this.resourceIds = resourceIds; + } + + public String getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(String grantTypes) { + this.grantTypes = grantTypes; + } + + public String getRedirectUri() { + return redirectUri; + } + + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + public Integer getAutoapprove() { + return autoapprove; + } + + public void setAutoapprove(Integer autoapprove) { + this.autoapprove = autoapprove; + } + + public Integer getAccessTokenValidity() { + return accessTokenValidity; + } + + public void setAccessTokenValidity(Integer accessTokenValidity) { + this.accessTokenValidity = accessTokenValidity; + } + + public Integer getRefreshTokenValidity() { + return refreshTokenValidity; + } + + public void setRefreshTokenValidity(Integer refreshTokenValidity) { + this.refreshTokenValidity = refreshTokenValidity; + } +} diff --git a/ab-auth/ab-auth-core/pom.xml b/ab-auth/ab-auth-core/pom.xml new file mode 100644 index 00000000..d34ec1d0 --- /dev/null +++ b/ab-auth/ab-auth-core/pom.xml @@ -0,0 +1,31 @@ + + + + ab-auth + com.dstz + 2.5.0 + + 4.0.0 + + ab-auth-core + + + + com.dstz + ab-base-web + + + com.dstz + ab-auth-api + ${project.version} + + + com.dstz + ab-sys-api + ${project.version} + + + + \ No newline at end of file diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysApplicationApiImpl.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysApplicationApiImpl.java new file mode 100644 index 00000000..d085512e --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysApplicationApiImpl.java @@ -0,0 +1,59 @@ +package com.dstz.auth.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.SysApplicationApi; +import com.dstz.auth.authentication.vo.SysApplicationVO; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.APPCLICATION_ENABLE_DEFAULT_MOBILE_APP_NOT_FIND; + +/** + * 系统应用接口实现 + * + * @author wacxhs + */ +@Service("sysApplicationApi") +public class SysApplicationApiImpl implements SysApplicationApi { + + private final SysApplicationManager sysApplicationManager; + + public SysApplicationApiImpl(SysApplicationManager sysApplicationManager) { + this.sysApplicationManager = sysApplicationManager; + } + + @Override + public SysApplicationVO getByCode(String code) { + SysApplication sysApplication = sysApplicationManager.getByAlias(code); + return BeanCopierUtils.transformBean(sysApplication, SysApplicationVO.class); + } + + @Override + public List getAllCode() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysApplication.class) + .select(SysApplication::getCode); + List sysApplicationList = sysApplicationManager.selectByWrapper(queryWrapper); + return CollUtil.map(sysApplicationList, SysApplication::getCode, false); + } + + @Override + public SysApplicationVO getEnabledMobileApp() { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysApplication.class) + .eq(SysApplication::getEnabled, NumberPool.INTEGER_ONE) + .eq(SysApplication::getIsDefault, NumberPool.INTEGER_ONE) + .eq(SysApplication::getAppType, NumberPool.INTEGER_ONE); + List sysApplicationList = sysApplicationManager.selectByWrapper(queryWrapper); + Assert.isTrue(CollUtil.isNotEmpty(sysApplicationList),()-> new BusinessMessage(APPCLICATION_ENABLE_DEFAULT_MOBILE_APP_NOT_FIND)); + return CollUtil.isNotEmpty(sysApplicationList) ? BeanCopierUtils.transformBean(sysApplicationList.get(0), SysApplicationVO.class) : null; + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysResourceApiImpl.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysResourceApiImpl.java new file mode 100644 index 00000000..260764ce --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/api/SysResourceApiImpl.java @@ -0,0 +1,106 @@ +package com.dstz.auth.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.SysResourceApi; +import com.dstz.auth.authentication.api.model.ISysApplication; +import com.dstz.auth.authentication.api.model.ISysResource; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.ResourceRoleManager; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.rest.model.vo.SysResourceVO; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.utils.BeanCopierUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Set; + +import static com.dstz.auth.authentication.api.constant.AuthCacheKeyConstant.IS_ROLE; + +/** + * 用户系统资源服务接口 + * + * @author lightning + */ +@Service +public class SysResourceApiImpl implements SysResourceApi { + @Autowired + SysResourceManager sysResourceManager; + @Autowired + SysApplicationManager sysApplicationManager; + @Autowired + ResourceRoleManager resourceRoleManager; + + //办理事项的固定url地址 + private static final String TODO_RESOURCE = "/pages/bpm/definitionList"; + + /** + * 通过资源id获取资源对象 + * + * @param id 资源ID + * @return ISysResource 资源对象 + */ + @Override + public ISysResource getResourceById(String id) { + return BeanCopierUtils.transformBean(sysResourceManager.getById(id), SysResourceVO.class); + } + + /** + * 通过资源code集合删除资源对象 + * + * @param codeList 资源code集合 + */ + @Override + public void deleteResourceByCode(List codeList) { + sysResourceManager.deleteResourceByCode(codeList); + } + + @Override + public List getCurrentUserSystem() { + return (List) sysApplicationManager.getCurrentUserSystem(); + } + + @Override + public ISysApplication getDefaultSystem(String currentUserId) { + return sysApplicationManager.getDefaultSystem(currentUserId); + } + + @Override + public List getBySystemId(String systemId) { + List bySystemId = sysResourceManager.getBySystemId(systemId); + return (List) sysResourceManager.getBySystemId(systemId); + } + + @Override + public List getByAppIdAndUser(String appId, String userId) { + List byAppIdAndUserId = sysResourceManager.getByAppIdAndUserId(appId, userId); + return (List) sysResourceManager.getByAppIdAndUserId(appId, userId); + } + + @Override + public Set getAccessRoleByUrl(String url) { + return resourceRoleManager.getAccessRoleByUrl(url); + } + + @Override + @Cacheable(cacheNames = AbCacheRegionConstant.SYS_RESOURCE, key = IS_ROLE) + public boolean isRoleByUrl(String url) { + return CollUtil.isNotEmpty(sysResourceManager.selectByWrapper(Wrappers.lambdaQuery(SysResource.class).select(SysResource::getType).eq(SysResource::getType, "API").eq(SysResource::getUrl, url))); + } + + @Override + public ISysResource getTodoResource() { + List resources = sysResourceManager.selectByWrapper(Wrappers.lambdaQuery(SysResource.class) + .eq(SysResource::getUrl, TODO_RESOURCE)); + if (CollectionUtil.isEmpty(resources)) { + return null; + } + return BeanCopierUtils.transformBean(resources.get(0), SysResourceVO.class); + } + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/ResourceRole.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/ResourceRole.java new file mode 100644 index 00000000..00d20a95 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/ResourceRole.java @@ -0,0 +1,238 @@ +package com.dstz.auth.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 资源角色关联表 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@TableName("sys_resource_role") +public class ResourceRole extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 应用ID + */ + @TableField("app_id_") + private String appId; + + /** + * 资源权限ID + */ + @TableField("resource_id_") + private String resourceId; + + /** + * 角色ID + */ + @TableField("role_id_") + private String roleId; + + /** + * 半选中 + */ + @TableField("half_checked_") + private Integer halfChecked; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 角色别名。 + */ + @TableField(exist = false) + protected String roleAlias; + /** + * 资源url连接。 + */ + @TableField(exist = false) + protected String url; + + /** + * 资源别名。 + */ + @TableField(exist = false) + protected String resAlias; + + public String getRoleAlias() { + return roleAlias; + } + + public void setRoleAlias(String roleAlias) { + this.roleAlias = roleAlias; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getResAlias() { + return resAlias; + } + + public void setResAlias(String resAlias) { + this.resAlias = resAlias; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public Integer getHalfChecked() { + return halfChecked; + } + + public void setHalfChecked(Integer halfChecked) { + this.halfChecked = halfChecked; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + + public static final class Builder { + private String id; + private String appId; + private String resourceId; + private String roleId; + private Integer halfChecked; + private Date createTime; + private String createBy; + + private Builder() { + } + + public static Builder newBuilder() { + return new Builder(); + } + + public Builder withId(String id) { + this.id = id; + return this; + } + + public Builder withAppId(String appId) { + this.appId = appId; + return this; + } + + public Builder withResourceId(String resourceId) { + this.resourceId = resourceId; + return this; + } + + public Builder withRoleId(String roleId) { + this.roleId = roleId; + return this; + } + + public Builder withHalfChecked(Integer halfChecked) { + this.halfChecked = halfChecked; + return this; + } + + public Builder withCreateTime(Date createTime) { + this.createTime = createTime; + return this; + } + + public Builder withCreateBy(String createBy) { + this.createBy = createBy; + return this; + } + + public ResourceRole build() { + ResourceRole resourceRole = new ResourceRole(); + resourceRole.setId(id); + resourceRole.setAppId(appId); + resourceRole.setResourceId(resourceId); + resourceRole.setRoleId(roleId); + resourceRole.setHalfChecked(halfChecked); + resourceRole.setCreateTime(createTime); + resourceRole.setCreateBy(createBy); + return resourceRole; + } + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysApplication.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysApplication.java new file mode 100644 index 00000000..9328b49f --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysApplication.java @@ -0,0 +1,394 @@ +package com.dstz.auth.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.auth.authentication.api.model.ISysApplication; +import com.dstz.base.entity.AbModel; +import java.util.Date; +import java.io.Serializable; + +/** + *

+ * 应用 + *

+ * + * @author lightning + * @since 2022-08-03 + */ +@TableName("sys_application") +public class SysApplication extends AbModel implements ISysApplication { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 应用名 + */ + @TableField("name_") + private String name; + + /** + * 编码 + */ + @TableField("code_") + private String code; + + /** + * 密匙 + */ + @TableField("secret_") + private String secret; + + /** + * 资源集合 + */ + @TableField("resource_ids_") + private String resourceIds; + + /** + * 授权范围 + */ + @TableField("scope_") + private String scope; + + /** + * 刷新秒数 + */ + @TableField("refresh_token_validity_") + private Integer refreshTokenValidity; + + /** + * 有效期 + */ + @TableField("access_token_validity_") + private Integer accessTokenValidity; + + /** + * 授权类型 + */ + @TableField("grant_types_") + private String grantTypes; + + /** + * 自动授权 + */ + @TableField("autoapprove_") + private Integer autoapprove; + + /** + * 权限 + */ + @TableField("authorities_") + private String authorities; + + /** + * 系统地址,空则为当前系统 + */ + @TableField("url_") + private String url; + + /** + * 回调地址 + */ + @TableField("redirect_uri_") + private String redirectUri; + + /** + * 打开方式 + */ + @TableField("open_type_") + private String openType; + + /** + * 是否可用 + */ + @TableField("enabled_") + private Integer enabled; + + /** + * 是否默认打开 + */ + @TableField("is_default_") + private Integer isDefault; + + /** + * 描述备注 + */ + @TableField("desc_") + private String desc; + + /** + * 扩展配置 + */ + @TableField("config_") + private String config; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + + /** + * 应用类型 0 web应用 1 移动端 2第三方应用 + */ + @TableField("app_type_") + private Integer appType; + + @Override + public Integer getAppType() { + return appType; + } + + public void setAppType(Integer appType) { + this.appType = appType; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getSecret() { + return secret; + } + + public void setSecret(String secret) { + this.secret = secret; + } + + public String getResourceIds() { + return resourceIds; + } + + public void setResourceIds(String resourceIds) { + this.resourceIds = resourceIds; + } + + public String getScope() { + return scope; + } + + public void setScope(String scope) { + this.scope = scope; + } + + public Integer getRefreshTokenValidity() { + return refreshTokenValidity; + } + + public void setRefreshTokenValidity(Integer refreshTokenValidity) { + this.refreshTokenValidity = refreshTokenValidity; + } + + public Integer getAccessTokenValidity() { + return accessTokenValidity; + } + + public void setAccessTokenValidity(Integer accessTokenValidity) { + this.accessTokenValidity = accessTokenValidity; + } + + public String getGrantTypes() { + return grantTypes; + } + + public void setGrantTypes(String grantTypes) { + this.grantTypes = grantTypes; + } + + public Integer getAutoapprove() { + return autoapprove; + } + + public void setAutoapprove(Integer autoapprove) { + this.autoapprove = autoapprove; + } + + public String getAuthorities() { + return authorities; + } + + public void setAuthorities(String authorities) { + this.authorities = authorities; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getRedirectUri() { + return redirectUri; + } + + public void setRedirectUri(String redirectUri) { + this.redirectUri = redirectUri; + } + + public String getOpenType() { + return openType; + } + + public void setOpenType(String openType) { + this.openType = openType; + } + + public Integer getEnabled() { + return enabled; + } + + public void setEnabled(Integer enabled) { + this.enabled = enabled; + } + + public Integer getIsDefault() { + return isDefault; + } + + public void setIsDefault(Integer isDefault) { + this.isDefault = isDefault; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getConfig() { + return config; + } + + + + public void setConfig(String config) { + this.config = config; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysResource.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysResource.java new file mode 100644 index 00000000..d70b39e0 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/entity/SysResource.java @@ -0,0 +1,309 @@ +package com.dstz.auth.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; +import com.fasterxml.jackson.annotation.JsonFormat; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 系统权限资源定义 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@TableName("sys_resource") +public class SysResource extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 应用ID + */ + @TableField("app_id_") + private String appId; + + /** + * 权限编码 + */ + @TableField("code_") + private String code; + + /** + * 名字 + */ + @TableField("name_") + private String name; + + /** + * 路由地址 + */ + @TableField("url_") + private String url; + + /** + * 是否启用 + */ + @TableField("enable_") + private Integer enable; + + /** + * 是否默认打开 + */ + @TableField("opened_") + private Integer opened; + + /** + * 是否API资源 + */ + @TableField("is_api_") + private Integer isApi; + + /** + * 图标 + */ + @TableField("icon_") + private String icon; + + /** + * menu,button,API + */ + @TableField("type_") + private String type; + + /** + * 排序 + */ + @TableField("sn_") + private Integer sn; + + /** + * 父节点ID + */ + @TableField("parent_id_") + private String parentId; + + /** + * 路径 + */ + @TableField("path_") + private String path; + + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + @JsonFormat(timezone = "GMT+8", pattern = "yyyy-MM-dd HH:mm:ss") + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public Integer getEnable() { + return enable; + } + + public void setEnable(Integer enable) { + this.enable = enable; + } + + public Integer getOpened() { + return opened; + } + + public void setOpened(Integer opened) { + this.opened = opened; + } + + public Integer getIsApi() { + return isApi; + } + + public void setIsApi(Integer isApi) { + this.isApi = isApi; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/ResourceRoleManager.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/ResourceRoleManager.java new file mode 100644 index 00000000..69e33d25 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/ResourceRoleManager.java @@ -0,0 +1,55 @@ +package com.dstz.auth.core.manager; + +import com.dstz.auth.core.entity.ResourceRole; +import com.dstz.auth.rest.model.dto.GrantRoleResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.manager.AbBaseManager; + +import java.util.List; +import java.util.Set; + +/** + *

+ * 资源角色关联表 通用业务类 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +public interface ResourceRoleManager extends AbBaseManager { + /** + * 根据id查询 + * @param roleId + */ + List getAllByRoleId(String roleId); + + /** + * 分配角色资源。 + * + * @param dto grant dto + */ + void grantRoleResource(GrantRoleResourceDTO dto); + + /** + * 通过url 获取可访问的角色 + * @param url + * @return + */ + Set getAccessRoleByUrl(String url); + + /** + * 根据角色id和编码查询资源树 + * @param roleId + * @param code + * @return + */ + List getRoleResTreeData(String roleId, String code); + + /** + * 根据系统id和角色id查询资源树 + * @param systemId + * @param roleId + * @return + */ + List getRoleRes(String systemId, String roleId); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysApplicationManager.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysApplicationManager.java new file mode 100644 index 00000000..64acb0f4 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysApplicationManager.java @@ -0,0 +1,75 @@ +package com.dstz.auth.core.manager; + +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.manager.AbBaseManager; + +import java.util.List; + +/** + *

+ * 应用 通用业务类 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +public interface SysApplicationManager extends AbBaseManager { + /** + * 判断别名是否存在。 + * + * @param subsystem + * @return + */ + boolean isExist(SysApplication subsystem); + + /** + * 获取默认子系统。 + * 1.获取用户有权限的系统,如果没有权限则返回空。 + * 2.如果权限子系统,判断是否有默认的子系统,有则返回。 + * 3.否则取第一个。 + * + * @return + */ + SysApplication getDefaultSystem(String userId); + + /** + * 设置默认子系统。 + * 1.如果是默认的则取消。 + * 2.非默认则设置默认。 + * + * @param systemId + */ + void setDefaultSystem(String systemId); + + /** + * 通过应用系统对象获取资源树 + * + * @param sysApplication 应用对象 + * @return 资源树对象 + */ + List getTreeDataByApplication(SysApplication sysApplication); + + /** + * 获取当前用户可用系统 + * @return + */ + List getCurrentUserSystem(); + /** + * 根据userId获取系统 + * @return + */ + List getSystemByUser(String userId); + /** + * 根据code获取系统 + * @return + */ + SysApplication getByAlias(String code); + + + /** + * 根据id更新应用 + */ + + int updateById(SysApplication subsystem); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysResourceManager.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysResourceManager.java new file mode 100644 index 00000000..3304aa4c --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/SysResourceManager.java @@ -0,0 +1,88 @@ +package com.dstz.auth.core.manager; + +import com.dstz.auth.authentication.api.model.ISysResource; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.rest.model.dto.SysResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceVO; +import com.dstz.base.manager.AbBaseManager; + +import java.util.List; + +/** + *

+ * 系统权限资源定义 通用业务类 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +public interface SysResourceManager extends AbBaseManager { + + /** + * 根据子系统ID获取实体列表。 + */ + List getBySystemId(String id); + + + /** + * 根据系统和角色ID获取资源。 + * + * @param systemId + * @param roleId + * @return + */ + List getBySystemAndRole(String systemId, String roleId); + + /** + * 判断资源是否存在。 + * + * @param resource + * @return + */ + boolean isExist(SysResource resource); + + /** + * 根据资源id递归删除资源数据。 + * + * @param resId + */ + void removeByResId(String resId); + + /** + * 根据系统id和用户id获取资源。 + * + * @param appId 应用ID + * @param userId 用户ID + * @return 资源列表 + */ + List getByAppIdAndUserId(String appId, String userId); + + /** + * 根据id获取资源明细信息 + * + * @param id + * @param systemId + * @param parentId + * @return + */ + SysResourceVO getResourceDetailById(String id, String parentId, String systemId); + + + /** + * 保存树结构 + * @param sysResource + */ + void saveTree(SysResourceDTO sysResource); + + /** + * 保存单条资源 + * @param sysResource + */ + String saveSysResource(SysResource sysResource); + + /** + * 通过资源code集合删除资源对象 + * @param codeList 资源code集合 + */ + void deleteResourceByCode(List codeList); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/ResourceRoleManagerImpl.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/ResourceRoleManagerImpl.java new file mode 100644 index 00000000..1ed095c0 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/ResourceRoleManagerImpl.java @@ -0,0 +1,156 @@ +package com.dstz.auth.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.core.entity.ResourceRole; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.ResourceRoleManager; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.core.mapper.ResourceRoleMapper; +import com.dstz.auth.rest.model.dto.GrantRoleResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.google.common.collect.Streams; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 资源角色关联表 通用服务实现类 + * + * @author lightning + * @since 2022-02-07 + */ +@Service("resourceRoleManager") +public class ResourceRoleManagerImpl extends AbBaseManagerImpl implements ResourceRoleManager { + + @Autowired + ResourceRoleMapper resourceRoleMapper; + + @Autowired + ICache iCache; + + @Autowired + ThreadPoolTaskExecutor threadPoolTaskExecutor; + + @Autowired + SysApplicationManager sysApplicationManager; + + @Autowired + SysResourceManager sysResourceManager; + + @Autowired + private GroupApi groupApi; + + @Override + public List getAllByRoleId(String roleId) { + return resourceRoleMapper.getByRoleId(roleId); + } + + + @CacheEvict(cacheNames = AbCacheRegionConstant.SYS_RESOURCE, allEntries = true) + @Override + public void grantRoleResource(GrantRoleResourceDTO dto) { + resourceRoleMapper.removeByRoleAndSystem(dto.getRoleId(), dto.getAppId()); + + Stream resIdStream = Stream.empty(); + ResourceRole.Builder builder = ResourceRole.Builder.newBuilder().withRoleId(dto.getRoleId()).withAppId(dto.getAppId()).withHalfChecked(NumberPool.BOOLEAN_FALSE); + if (CollUtil.isNotEmpty(dto.getResIds())) { + resIdStream = Stream.concat(resIdStream, dto.getResIds().stream()); + } + // 设置半选中 + resIdStream = Stream.concat(resIdStream, Stream.of(StrUtil.EMPTY).peek(o -> builder.withHalfChecked(NumberPool.BOOLEAN_TRUE))); + if (CollUtil.isNotEmpty(dto.getHalfResIds())) { + resIdStream = Stream.concat(resIdStream, dto.getHalfResIds().stream()); + } + + Iterator iterator = resIdStream + .filter(resId -> !StrPool.NUMBER_ZERO.equals(resId) && StrUtil.isNotEmpty(resId)) + .map(resId -> builder.withResourceId(resId).build()) + .iterator(); + + // 批量入库 + bulkCreate(IterUtil.asIterable(iterator)); + } + + @Cacheable(cacheNames = AbCacheRegionConstant.SYS_RESOURCE, key = "#url") + @Override + public Set getAccessRoleByUrl(String url) { + url = StrUtil.trimToNull(url); + if (StrUtil.isEmpty(url)) { + return Collections.emptySet(); + } + + // 根据资源地址获取角色ID + Set roleIds = resourceRoleMapper.getRoleIdByResourceUrl(url); + if (CollUtil.isEmpty(roleIds)) { + return Collections.emptySet(); + } + + // 获取角色编码 + return Optional.ofNullable(groupApi.getByGroupIds(GroupType.ROLE.getType(), roleIds)) + .map(Streams::stream) + .orElseGet(Stream::empty) + .filter(o -> o.getAttrValue("enabled", Boolean.class)) + .map(IGroup::getGroupCode) + .collect(Collectors.toSet()); + } + + @Override + public List getRoleResTreeData(String roleId, String code) { + SysApplication system = sysApplicationManager.getByAlias(code); + Assert.notNull(system,()->new BusinessMessage(AuthStatusCode.SYS_IS_NOT_DEFINITION.formatDefaultMessage(code))); + return BeanConversionUtils.listToTree(getRoleRes(system.getId(), roleId)); + } + + @Override + public List getRoleRes(String systemId, String roleId) { + + List roleResourceList = sysResourceManager.getBySystemAndRole(systemId, roleId); + Set userResourceId = new HashSet<>(roleResourceList.size(), 1); + roleResourceList.forEach(resouces -> userResourceId.add(resouces.getId())); + List resourceList = sysResourceManager.getBySystemId(systemId); + List sysResourceTreeVOS = BeanCopierUtils.transformList(resourceList,SysResourceTreeVO.class); + for (SysResourceTreeVO sysResource : sysResourceTreeVOS) { + if (userResourceId.contains(sysResource.getId())) { + sysResource.setChecked(true); + } + } + + if (CollectionUtil.isEmpty(resourceList)) { + resourceList = new ArrayList<>(); + } + + SysResource rootRes = new SysResource(); + String rootName = sysApplicationManager.getById(systemId).getName(); + rootRes.setName(rootName); + rootRes.setId(NumberPool.INTEGER_ZERO.toString()); + rootRes.setCode("root"); + // 根节点 + rootRes.setAppId(systemId); + resourceList.add(rootRes); + return sysResourceTreeVOS; + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysApplicationManagerImpl.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysApplicationManagerImpl.java new file mode 100644 index 00000000..c1541e7c --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysApplicationManagerImpl.java @@ -0,0 +1,163 @@ +package com.dstz.auth.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.core.mapper.ResourceRoleMapper; +import com.dstz.auth.core.mapper.SysApplicationMapper; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.context.UserContext; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.CacheEvict; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.*; + +/** + * 应用 通用服务实现类 + * + * @author lightning + * @since 2022-02-07 + */ +@Service("sysApplicationManager") +public class SysApplicationManagerImpl extends AbBaseManagerImpl implements SysApplicationManager { + @Autowired + SysApplicationMapper sysApplicationMapper; + @Autowired + SysResourceManager sysResourceManager; + @Autowired + private ResourceRoleMapper resourceRoleMapper; + @Autowired + private GroupApi groupApi; + + @Override + public boolean isExist(SysApplication subsystem) { + return sysApplicationMapper.isExist(subsystem) > 0; + } + + /** + * 通过应用系统对象获取资源树 + * + * @param sysApplication 应用对象 + * @return 资源树对象 + */ + @Override + public List getTreeDataByApplication(SysApplication sysApplication) { + List resourceList = sysResourceManager.getBySystemId(sysApplication.getId()); + List groupList = BeanCopierUtils.transformList(resourceList, SysResourceTreeVO.class); + if (CollectionUtil.isEmpty(groupList)) { + groupList = new ArrayList<>(); + } + SysResourceTreeVO rootResource = new SysResourceTreeVO(); + rootResource.setName(sysApplication.getName()); + rootResource.setId(NumberPool.INTEGER_ZERO.toString()); + // 根节点 + rootResource.setAppId(sysApplication.getId()); + groupList.add(rootResource); + return BeanConversionUtils.listToTree(groupList); + } + + @Override + public SysApplication getDefaultSystem(String userId) { + List sysApplications = getSystemByUser(userId); + if (CollectionUtil.isEmpty(sysApplications)) { + return null; + } + + return Optional.ofNullable(CollUtil.findOne(sysApplications, app -> NumberPool.BOOLEAN_TRUE.equals(app.getIsDefault()))) + .orElseGet(() -> CollUtil.getFirst(sysApplications)); + } + + @Override + public void setDefaultSystem(String systemId) { + SysApplication subSystem = sysApplicationMapper.selectById(systemId); + if (subSystem.getIsDefault() == NumberPool.BOOLEAN_TRUE) { + subSystem.setIsDefault(NumberPool.BOOLEAN_FALSE); + } else { + sysApplicationMapper.updNoDefault(); + subSystem.setIsDefault(NumberPool.BOOLEAN_TRUE); + } + sysApplicationMapper.updateById(subSystem); + } + + + @Override + public List getCurrentUserSystem() { + UserContext userContext = UserContextUtils.getUserContext(); + if (userContext.isSuperAdmin()) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysApplication.class).eq(SysApplication::getEnabled, NumberPool.BOOLEAN_TRUE); + return sysApplicationMapper.selectList(queryWrapper); + } + + // 获取用户角色编码 + Collection authorities = userContext.getAuthorities(); + if (CollUtil.isEmpty(authorities)) { + return new ArrayList<>(); + } + + // 获取当前用户角色列表 + List roleIds = Optional.ofNullable(groupApi.getByGroupCodes(GroupType.ROLE.getType(), authorities)) + .map(IterUtil::asIterable) + .map(iter -> CollUtil.map(iter, IGroup::getGroupId, true)) + .orElse(null); + + // 角色ID未获取到返回空列表 + if (CollUtil.isEmpty(roleIds)) { + return new ArrayList<>(); + } + + // 角色ID获取关联的应用ID + Set appIds = resourceRoleMapper.selectAppIdByRoleIds(roleIds); + if (CollUtil.isEmpty(appIds)) { + return new ArrayList<>(); + } + + // 查询应用列表 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysApplication.class).in(SysApplication::getId, appIds).eq(SysApplication::getEnabled, NumberPool.BOOLEAN_TRUE); + return sysApplicationMapper.selectList(queryWrapper); + } + + @Cacheable(cacheNames = AbCacheRegionConstant.SYS_APPLICATION, key = "#code") + @Override + public SysApplication getByAlias(String code) { + return sysApplicationMapper.getByAlias(code); + } + + @CacheEvict(cacheNames = AbCacheRegionConstant.SYS_APPLICATION, key = "#subsystem.code", allEntries = true) + @Override + public int updateById(SysApplication subsystem) { + return sysApplicationMapper.updateById(subsystem); + } + + @Override + public List getSystemByUser(String userId) { + List roleIds = CollUtil.map(groupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), userId), IGroup::getGroupId, true); + if (CollUtil.isEmpty(roleIds)) { + return new ArrayList<>(); + } + Set appIds = resourceRoleMapper.selectAppIdByRoleIds(roleIds); + if (CollUtil.isNotEmpty(appIds)) { + return new ArrayList<>(); + } + + // 查询应用列表 + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysApplication.class).in(SysApplication::getId, appIds).eq(SysApplication::getEnabled, NumberPool.BOOLEAN_TRUE); + return sysApplicationMapper.selectList(queryWrapper); + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysResourceManagerImpl.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysResourceManagerImpl.java new file mode 100644 index 00000000..110ed609 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/manager/impl/SysResourceManagerImpl.java @@ -0,0 +1,182 @@ +package com.dstz.auth.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.authentication.api.constant.ResouceTypeConstant; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.core.mapper.SysResourceMapper; +import com.dstz.auth.rest.model.dto.SysResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceVO; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 系统权限资源定义 通用服务实现类 + * + * @author lightning + * @since 2022-02-07 + */ +@Service("sysResourceManager") +public class SysResourceManagerImpl extends AbBaseManagerImpl implements SysResourceManager { + + @Autowired + SysResourceMapper sysResourceMapper; + @Autowired + private GroupApi groupApi; + + + @Override + public List getBySystemId(String id) { + return sysResourceMapper.getBySystemId(id); + } + + @Override + public List getBySystemAndRole(String systemId, String roleId) { + return sysResourceMapper.getBySystemAndRole(systemId, roleId); + } + + @Override + public boolean isExist(SysResource resource) { + return sysResourceMapper.isExist(resource) > 0; + } + + @Override + public void removeByResId(String resId) { + SysResource resource = sysResourceMapper.selectById(resId); + if (resource == null) { + return; + } + List relatedResouces = new ArrayList<>(); + + getChildList(relatedResouces, resource.getId()); + if (CollectionUtil.isNotEmpty(relatedResouces)) { + sysResourceMapper.deleteBatchIds(relatedResouces.stream().map(SysResource::getId).collect(Collectors.toList())); + } + sysResourceMapper.deleteById(resId); + } + + + private void getChildList(List relatedResouces, String id) { + List children = sysResourceMapper.getByParentId(id); + if (CollectionUtil.isEmpty(children)) { + return; + } + + for (SysResource r : children) { + getChildList(relatedResouces, r.getId()); + } + relatedResouces.addAll(children); + } + + @Override + public List getByAppIdAndUserId(String appId, String userId) { + List roleIds = CollUtil.map(groupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), userId), IGroup::getGroupId, true); + if (CollUtil.isEmpty(roleIds)) { + return new ArrayList<>(); + } + return sysResourceMapper.getByAppIdAndRoleIds(appId, roleIds); + } + + @Override + public SysResourceVO getResourceDetailById(String id, String parentId, String systemId) { + SysResourceVO sysResourceVO = new SysResourceVO(); + if (StrUtil.isEmpty(id)) { + SysResource parent = this.getById(parentId); + if (parent != null) { + sysResourceVO.setAppId(parent.getAppId()); + sysResourceVO.setParentName(parent.getName()); + sysResourceVO.setParentId(parentId); + } else { + sysResourceVO.setAppId(systemId); + sysResourceVO.setParentId(parentId); + } + + sysResourceVO.setOpened(NumberPool.BOOLEAN_TRUE); + return sysResourceVO; + } else { + SysResource sysResource = this.getById(id); + + BeanCopierUtils.copyProperties(sysResource, sysResourceVO); + SysResource parent = this.getById(sysResource.getParentId()); + if (parent != null) { + sysResourceVO.setParentName(parent.getName()); + } + } + return sysResourceVO; + } + + @Override + public void saveTree(SysResourceDTO sysResource) { + if (CollUtil.isNotEmpty(sysResource.getChildren())) { + saveTreeList(sysResource.getChildren(), sysResource.getId()); + } + } + + @Override + public String saveSysResource(SysResource sysResource) { + checkResouce(sysResource); + + if (StrUtil.isEmpty(sysResource.getId())) { + List list = sysResourceMapper.getByParentId(sysResource.getParentId()); + sysResource.setSn(list.size() + 1); + Assert.isFalse(this.isExist(sysResource), () -> new BusinessMessage(AuthStatusCode.RESOURCES_CODE_REPEAT.formatDefaultMessage(sysResource.getName()+" "+sysResource.getCode()))); + this.create(sysResource); + } else { + this.update(sysResource); + } + return sysResource.getId(); + } + + @Override + public void deleteResourceByCode(List codeList) { + remove(Wrappers.lambdaQuery(SysResource.class) + .in(CollectionUtil.isNotEmpty(codeList), SysResource::getCode, codeList)); + } + + private void checkResouce(SysResource sysResource) { + if (StrUtil.isEmpty(sysResource.getId())) { + Assert.isFalse(this.isExist(sysResource), () -> new BusinessMessage(AuthStatusCode.RESOURCES_CODE_REPEAT)); + } + // 如果是菜单、那上级也必须是菜单、防止按钮下面配置菜单 + if (ResouceTypeConstant.MENU.getKey().equals(sysResource.getType())) { + SysResource parent = this.getById(sysResource.getParentId()); + if (parent == null) return; + + Assert.isTrue(ResouceTypeConstant.MENU.getKey().equals(parent.getType()), () -> new BusinessException(AuthStatusCode.RESOUCE_TYPE_CONSTANT_ERROR.formatDefaultMessage(parent.getName()))); + } + + sysResource.setUrl(StrUtil.isNotEmpty(sysResource.getUrl()) ? sysResource.getUrl().trim() : ""); + } + + private void saveTreeList(List sysResourceList, String parentId) { + + for (int i = 0; i < sysResourceList.size(); i++) { + SysResourceDTO resource = sysResourceList.get(i); + resource.setParentId(parentId); + resource.setSn(i + 1); + Assert.isFalse(this.isExist(resource), () -> new BusinessMessage(AuthStatusCode.RESOURCES_CODE_REPEAT.formatDefaultMessage(resource.getName()+" "+resource.getCode()))); + this.createOrUpdate(resource); + if (CollUtil.isNotEmpty(resource.getChildren())) { + saveTreeList(resource.getChildren(), resource.getId()); + } + } + } + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/ResourceRoleMapper.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/ResourceRoleMapper.java new file mode 100644 index 00000000..87a1dd1a --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/ResourceRoleMapper.java @@ -0,0 +1,56 @@ +package com.dstz.auth.core.mapper; + +import com.dstz.auth.core.entity.ResourceRole; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; +import java.util.Set; + +/** + *

+ * 资源角色关联表 Mapper 接口 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@Mapper +public interface ResourceRoleMapper extends AbBaseMapper { + /** + * 根据roleId获取资源关联信息 + * @param roleId + * @return + */ + List getByRoleId(String roleId); + + /** + * 根据roleId和appId移除资源关联 + * @param roleId + * @param appId + */ + void removeByRoleAndSystem(@Param("roleId")String roleId, @Param("appId")String appId); + + /** + * 获取资源和角色的映射关系 + * @return + */ + List getAllResRole(); + + /** + * 根据资源URL获取角色ID + * + * @param url 资源URL + * @return 角色ID集合 + */ + Set getRoleIdByResourceUrl(String url); + + /** + * 根据角色ID查询应用ID集合 + * + * @param roleIds 角色ID + * @return 应用ID集合 + */ + Set selectAppIdByRoleIds(@Param("roleIds") Iterable roleIds); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysApplicationMapper.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysApplicationMapper.java new file mode 100644 index 00000000..b637d08e --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysApplicationMapper.java @@ -0,0 +1,36 @@ +package com.dstz.auth.core.mapper; + +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 应用 Mapper 接口 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@Mapper +public interface SysApplicationMapper extends AbBaseMapper { + + /** + * 判断别名是否存在 + * + * @param subsystem + * @return + */ + Integer isExist(SysApplication subsystem); + + /** + * 更新为默认。 + */ + void updNoDefault(); + + /** + * 根据code获取系统 + * @return + */ + SysApplication getByAlias(String systemAlias); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysResourceMapper.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysResourceMapper.java new file mode 100644 index 00000000..6a621664 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/core/mapper/SysResourceMapper.java @@ -0,0 +1,61 @@ +package com.dstz.auth.core.mapper; + +import com.dstz.auth.core.entity.SysResource; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 系统权限资源定义 Mapper 接口 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@Mapper +public interface SysResourceMapper extends AbBaseMapper { + /** + * 根据子系统ID取定义对象。 + * + * @param appId + * @return + */ + List getBySystemId(@Param("appId")String appId); + + /** + * 根据角色和系统id获取资源。 + * + * @param appId + * @param roleId + * @return + */ + List getBySystemAndRole(@Param("appId") String appId, @Param("roleId") String roleId); + + /** + * 判断资源是否存在。 + * + * @param resource + * @return + */ + Integer isExist(SysResource resource); + + /** + * 根据父ID获取下级节点。 + * + * @param parentId + * @return + */ + List getByParentId(String parentId); + + /** + * 根据系统id和用户id获取资源列表。 + * + * @param appId 系统id + * @param roleIds 角色ID集合 + * @return 系统资源 + */ + List getByAppIdAndRoleIds(@Param("appId") String appId, @Param("roleIds") Iterable roleIds); +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/ResourceRoleController.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/ResourceRoleController.java new file mode 100644 index 00000000..1a74d258 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/ResourceRoleController.java @@ -0,0 +1,92 @@ +package com.dstz.auth.rest.controller; + + +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.core.entity.ResourceRole; +import com.dstz.auth.core.manager.ResourceRoleManager; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.rest.model.dto.GrantRoleResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.web.controller.AbCrudController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +/** + *

+ * 资源角色关联表 前端控制器 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/resRole") +public class ResourceRoleController extends AbCrudController { + + @Autowired + ResourceRoleManager resourceRoleManager; + + @Autowired + SysResourceManager sysResourceManager; + + @Autowired + SysApplicationManager sysApplicationManager; + + @Override + protected String getEntityDesc() { + return "资源角色关联表"; + } + + + /** + * 角色资源分配明细页面 + * + * @param id + * @return + * @throws Exception ModelAndView + */ + @RequestMapping(value = "getJson", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse getJson(@RequestParam("id") String id) throws Exception { + if (StrUtil.isEmpty(id)) { + throw new BusinessMessage(AuthStatusCode.PARAM_IS_NULL.formatDefaultMessage("id")); + } + return ApiResponse.success(resourceRoleManager.getById(id)); + } + + + @RequestMapping("getTreeData") + public ApiResponse> getTreeData(@RequestParam("roleId") String roleId, @RequestParam("systemId") String systemId) throws Exception { + return ApiResponse.success(resourceRoleManager.getRoleRes(systemId, roleId)); + } + + + @RequestMapping("getRoleResTreeData") + public ApiResponse> getRoleResTreeData(@RequestParam("roleId") String roleId, @RequestParam("code") String code) throws Exception { + return ApiResponse.success(resourceRoleManager.getRoleResTreeData(roleId, code)); + } + + /** + * 角色资源分配 + * + * @param dto 分配数据对象 + * @return 接口处理结果 + */ + @RequestMapping(value = "grantRoleResource") + public ApiResponse grantRoleResource(@Valid @RequestBody GrantRoleResourceDTO dto) { + resourceRoleManager.grantRoleResource(dto); + return ApiResponse.success(); + } + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysApplicationController.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysApplicationController.java new file mode 100644 index 00000000..6bb2c129 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysApplicationController.java @@ -0,0 +1,124 @@ +package com.dstz.auth.rest.controller; + + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.constant.AuthCacheKeyConstant; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.mapper.SysApplicationMapper; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.web.controller.AbCrudController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + * 系统应用 前端控制器 + * + * @author lightning + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/application") +public class SysApplicationController extends AbCrudController { + + + @Autowired + SysApplicationManager sysApplicationManager; + + @Autowired + SysApplicationMapper sysApplicationMapper; + + @Autowired + ICache iCache; + + + @Override + protected String getEntityDesc() { + return "应用"; + } + + /** + * 分页查询按时间排序 + * + * @param queryParamDto 查询条件 + * @return 查询结果 + */ + @Override + @RequestMapping(value = "listJson") + public ApiResponse listJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + queryParamDto.setSortColumn("createTime"); + queryParamDto.setSortOrder("desc"); + return super.listJson(queryParamDto); + } + + + /** + * 移动端并且启用的应用列表 + * + * @return 查询结果 + */ + @GetMapping(value = "mobileApplication") + public ApiResponse> mobileApplication() { + return success(sysApplicationManager.selectByWrapper(Wrappers.lambdaQuery(SysApplication.class) + .eq(SysApplication::getEnabled, 1) + .eq(SysApplication::getAppType, 1))); + } + + + @RequestMapping(value = "getUserSystem", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse getUserSystem() { + return success(sysApplicationMapper.selectList(new QueryWrapper<>())); + } + + /** + * 子系统定义明细页面(查询指定ID) + * + * @param id + * @return SysApplication + * @throws Exception ModelAndView + */ + @RequestMapping("getJson") + public ApiResponse getJson(@NotBlank(message = "id不能为空") @RequestParam("id") String id) { + SysApplication subsystem = sysApplicationMapper.selectById(id); + return success(subsystem); + } + + /** + * @param sysApplication + * @return 保存子系统定义信息 + * @throws Exception void + * @throws + */ + @RequestMapping("save") + @Override + public ApiResponse save(@RequestBody SysApplication sysApplication) { + String resultMsg = null; + Assert.isFalse(sysApplicationManager.isExist(sysApplication), () -> new BusinessMessage(AuthStatusCode.RESOURCES_CODE_REPEAT.formatDefaultMessage(sysApplication.getCode()))); + + if (StrUtil.isEmpty(sysApplication.getId())) { + sysApplicationMapper.insert(sysApplication); + resultMsg = "添加子系统定义成功"; + } else { + sysApplicationManager.updateById(sysApplication); + resultMsg = "更新子系统定义成功"; + } + iCache.invalidate(AuthCacheKeyConstant.CACHE_REGION_AUTH_CLIENT, AuthCacheKeyConstant.AUTH_APP_LIST_EL); + return success(resultMsg); + } + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysResourceController.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysResourceController.java new file mode 100644 index 00000000..f1f29d62 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/SysResourceController.java @@ -0,0 +1,164 @@ +package com.dstz.auth.rest.controller; + + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.rest.model.dto.SysResourceDTO; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.auth.rest.model.vo.SysResourceVO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.web.controller.AbCrudController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.PARAM_IS_NULL; + +/** + *

+ * 系统权限资源定义 前端控制器 + *

+ * + * @author lightning + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/resource") +public class SysResourceController extends AbCrudController { + + @Autowired + SysResourceManager sysResourceManager; + @Autowired + SysApplicationManager sysApplicationManager; + + + @Override + protected String getEntityDesc() { + return "系统权限资源定义"; + } + + + /** + * 资源明细页面 + * + * @param id + * @param parentId + * @param systemId + * @return + * @throws Exception ModelAndView + */ + @RequestMapping("getJson") + public ApiResponse getJson(@RequestParam(value = "id", required = false) String id, + @RequestParam(value = "parentId", required = false) String parentId, + @RequestParam(value = "systemId", required = false) String systemId) { + return ApiResponse.success(sysResourceManager.getResourceDetailById(id, parentId, systemId)); + } + + /** + * 保存子系统资源信息 + * + * @param sysResource @throws Exception void @throws + */ + @Override + @RequestMapping("save") + public ApiResponse save(@RequestBody SysResource sysResource) { + return ApiResponse.success(sysResourceManager.saveSysResource(sysResource)); + } + + + /** + * 批量删除子系统资源记录 + * + * @param id + */ + @Override + @RequestMapping("remove") + public ApiResponse remove(@RequestParam("id") String id) { + + try { + sysResourceManager.removeByResId(id); + return ApiResponse.success("删除子系统资源成功"); + } catch (Exception e) { + throw new BusinessMessage(AuthStatusCode.DELETE_RESOURCES_ERROR); + } + } + + @RequestMapping("sysResourceGet") + public ApiResponse sysResourceGet(@RequestParam("id") String id) { + SysResource sysResource = sysResourceManager.getById(id); + return ApiResponse.success(sysResource); + } + + /** + * 通过应用id获取资源树 + * + * @param systemId 应用ID + * @return 资源树对象 + */ + @RequestMapping("getTreeData") + public ApiResponse> getTreeData(@RequestParam("systemId") String systemId) { + Assert.notEmpty(systemId, () -> new BusinessMessage(PARAM_IS_NULL.formatDefaultMessage(" systemId "))); + return ApiResponse.success(sysApplicationManager.getTreeDataByApplication(sysApplicationManager.getById(systemId))); + } + + /** + * 通过应用code获取资源树 + * + * @param systemCode 应用编码 + * @return 资源树对象 + */ + @RequestMapping("getTreeDataByCode") + public ApiResponse> getTreeDataByCode(@RequestParam("systemCode") String systemCode) { + Assert.notEmpty(systemCode, () -> new BusinessMessage(PARAM_IS_NULL.formatDefaultMessage(" systemCode "))); + SysApplication sysApplication = sysApplicationManager.selectOne(Wrappers.lambdaQuery(SysApplication.class) + .eq(SysApplication::getCode, systemCode)); + return ApiResponse.success(sysApplicationManager.getTreeDataByApplication(sysApplication)); + } + + @RequestMapping("getUserResource") + public ApiResponse getUserResource(@RequestParam("userId") String userId, @RequestParam("systemId") String appId) { + + + Map mapParam = new HashMap<>(16); + if (StrUtil.isEmpty(appId)) { + List subsystemList = sysApplicationManager.getSystemByUser(userId); + Assert.notNull(subsystemList, () -> new BusinessMessage(AuthStatusCode.USER_HAS_NOT_ASSIGNED_ANY_RESOURCES)); + appId = subsystemList.get(0).getId(); + mapParam.put("subsystemList", subsystemList); + } + + List groupList = sysResourceManager.getByAppIdAndUserId(appId, userId); + SysResource rootResource = new SysResource(); + rootResource.setName("菜单资源"); + rootResource.setId(NumberPool.INTEGER_ZERO.toString()); + rootResource.setAppId(appId); // 根节点 + groupList.add(rootResource); + mapParam.put("groupList", groupList); + return ApiResponse.success(mapParam); + } + + + @RequestMapping(value = "saveTree", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse saveTree(@RequestBody SysResourceDTO sysResource) { + sysResourceManager.saveTree(sysResource); + return ApiResponse.success("操作成功"); + } + + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/UserResourceController.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/UserResourceController.java new file mode 100644 index 00000000..f063571e --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/controller/UserResourceController.java @@ -0,0 +1,210 @@ +package com.dstz.auth.rest.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.authentication.api.constant.ResouceTypeConstant; +import com.dstz.auth.authentication.api.model.ISysApplication; +import com.dstz.auth.core.entity.SysApplication; +import com.dstz.auth.core.entity.SysResource; +import com.dstz.auth.core.manager.SysApplicationManager; +import com.dstz.auth.core.manager.SysResourceManager; +import com.dstz.auth.rest.model.vo.SysResourceTreeVO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +/** + * 用户资源 + * + * @author lightning + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/userResource") +public class UserResourceController { + + @Autowired + private SysResourceManager sysResourceManager; + + @Autowired + private GroupApi groupApi; + + @Autowired + private SysApplicationManager sysApplicationManager; + + @RequestMapping(value = "/userMsg", method = {RequestMethod.POST, RequestMethod.GET}) + public ApiResponse userMsg() { + return userMsg(false); + } + + @RequestMapping(value = "/appUserMsg", method = {RequestMethod.POST, RequestMethod.GET}) + public ApiResponse appUserMsg() { + return userMsg(true); + } + + private ApiResponse userMsg(boolean isMobile) { + final IUser currentUser = UserContextUtils.getUser().orElseThrow(() -> new BusinessException(GlobalApiCodes.LOGIN_INVALID)); + + //获取当前用户可用的应用列表 + List sysApplications; + + // 筛选出不同端的应用列表 + if (isMobile) { + sysApplications = CollUtil.filter(sysApplicationManager.getCurrentUserSystem(), item -> NumberPool.INTEGER_ONE.equals(item.getAppType())); + } else { + sysApplications = CollUtil.filter(sysApplicationManager.getCurrentUserSystem(), item -> !NumberPool.INTEGER_ONE.equals(item.getAppType())); + } + + if (CollectionUtil.isEmpty(sysApplications)) { + throw new BusinessMessage(AuthStatusCode.USER_HAS_NOT_ASSIGNED_ANY_RESOURCES); + } + + ISysApplication currentApplication = getCurrentUserApp(isMobile, currentUser , sysApplications); + + + Map map = MapUtil.newHashMap(); + map.put("currentEnviroment", SpringUtil.getActiveProfile()); + map.put("subsystemList", sysApplications); + map.put("currentSystem", currentApplication.getCode()); + map.put("username", currentUser.getFullName()); + map.put("currentOrg", UserContextUtils.getGroup().orElse(null)); + map.put("orgList", groupApi.getByGroupTypeAndUserId(GroupType.ORG.getType(), currentUser.getUserId())); + map.put("user", userModel(currentUser)); + map.put("pwdIsexpire", getUserPwdIsExpired()); + + getSysResource(map, currentApplication.getId(), currentUser.getUserId()); + + return ApiResponse.success(map); + } + + + private ISysApplication getCurrentUserApp(boolean isMobile, IUser currentUser, List sysApplications) { + // TODO 取切换后的应用名称 + return sysApplications.stream() + .filter(sysApplication -> NumberPool.BOOLEAN_TRUE.equals(sysApplication.getIsDefault())) + .findFirst() + .orElseGet(() -> CollUtil.getFirst(sysApplications)); + } + + private Map userModel(IUser user) { + Map userModel = BeanCopierUtils.transformMap(user, IUser.class); + // 签名 + userModel.put("signature", user.getAttrValue("signature", String.class)); + // 头像 + userModel.put("photo", user.getAttrValue("photo", String.class)); + return userModel; + } + + + /** + * <12 只是12毫秒 ,用户到期时间小于 当前时间 则视为需要重置密码 + * + * @return Boolean + */ + private Boolean getUserPwdIsExpired() { + Optional user = UserContextUtils.getUserContext().getUser(); + Date expireDate = user.map(o -> o.getAttrValue(IUser.ATTR_EXPIRE_DATE, Date.class)).orElse(null); + return expireDate != null && expireDate.getTime() - System.currentTimeMillis() > 0; + } + + private void getSysResource(Map map, String appId, String userId) { + List sysResourceList; + if (UserContextUtils.isSuperAdmin()) { + sysResourceList = sysResourceManager.getBySystemId(appId); + } else { + sysResourceList = sysResourceManager.getByAppIdAndUserId(appId, userId); + } + // 菜单和按钮分离 + List menuList = new ArrayList<>(); + Map buttonPermission = MapUtil.newHashMap(); + List sysResources = BeanCopierUtils.transformList(sysResourceList, SysResourceTreeVO.class); + for (SysResourceTreeVO sysResource : sysResources) { + if (ResouceTypeConstant.MENU.getKey().equals(sysResource.getType())) { + menuList.add(sysResource); + } else { + buttonPermission.put(sysResource.getCode(), NumberPool.INTEGER_ONE.equals(sysResource.getEnable())); + } + } + map.put("userMenuList", BeanConversionUtils.listToTree(menuList)); + map.put("buttonPermission", buttonPermission); + } + + /** + * 切换应用 + * + * @param appCode 应用编码 + * @return 接口响应 + */ + @RequestMapping(value = "/switchApp/{appCode}", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse switchApp(@PathVariable("appCode") String appCode, @RequestParam(value = "isMobile", defaultValue = "false", required = false) boolean isMobile) { + SysApplication sysApplication = CollUtil.findOne(sysApplicationManager.getCurrentUserSystem(), item -> StrUtil.equalsIgnoreCase(appCode, item.getCode())); + Assert.notNull(sysApplication, () -> new BusinessMessage(AuthStatusCode.APPLICATION_NO_PERMISSIONS.formatDefaultMessage(appCode))); + List sysResourceList; + if (UserContextUtils.isSuperAdmin()) { + sysResourceList = sysResourceManager.getBySystemId(sysApplication.getId()); + } else { + sysResourceList = sysResourceManager.getByAppIdAndUserId(sysApplication.getId(), UserContextUtils.getUserId()); + } + List menuList = new ArrayList<>(); + List sysResources = BeanCopierUtils.transformList(sysResourceList, SysResourceTreeVO.class); + for (SysResourceTreeVO sysResource : sysResources) { + if (ResouceTypeConstant.MENU.getKey().equals(sysResource.getType()) && sysResource.getEnable().equals(NumberPool.INTEGER_ONE)) { + menuList.add(sysResource); + } + } + if (menuList.size() == NumberPool.INTEGER_ZERO) { + return AuthStatusCode.APP_NO_RESOURCE.formatDefaultMessage(sysApplication.getName()).buildApiResponse(); + } + // 打开方式为0(跳转),需检查是否有菜单资源 + if (StrPool.NUMBER_ZERO.equals(sysApplication.getOpenType())) { + if (sysResourceManager.selectCount(Wrappers.lambdaQuery(SysResource.class).eq(SysResource::getAppId, sysApplication.getId())) <= NumberPool.INTEGER_ZERO) { + return AuthStatusCode.APP_NO_RESOURCE.formatDefaultMessage(sysApplication.getName()).buildApiResponse(); + } + } + // TODO 切换应用实现 + return ApiResponse.success(); + } + + /** + * 切换组织 + * + * @param orgCode 组织编码 + * @return 接口响应 + */ + @RequestMapping(value = "/switchOrg/{orgCode}", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse switchOrg(@PathVariable("orgCode") String orgCode) { + final IUser currentUser = UserContextUtils.getValidUser(); + List orgList = groupApi.getByGroupTypeAndUserId(GroupType.ORG.getType(), currentUser.getUserId()); + if (CollUtil.isEmpty(orgList)) { + return AuthStatusCode.USER_UNABSORBED_ORG.buildApiResponse(); + } + IGroup org = CollUtil.findOne(orgList, filterItem -> StrUtil.equals(filterItem.getGroupCode(), orgCode)); + if (org == null) { + return AuthStatusCode.ILLEGAL_CURRENT_ORG.formatDefaultMessage(orgCode).buildApiResponse(); + } + // TODO 切换组织实现 + return ApiResponse.success(); + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/GrantRoleResourceDTO.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/GrantRoleResourceDTO.java new file mode 100644 index 00000000..b5a6358f --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/GrantRoleResourceDTO.java @@ -0,0 +1,76 @@ +package com.dstz.auth.rest.model.dto; + +import javax.validation.constraints.NotEmpty; +import java.util.Set; + +/** + * 角色资源分配 + * + * @author wacxhs + */ +public class GrantRoleResourceDTO { + + /** + * 角色ID + */ + @NotEmpty(message = "角色ID不能为空") + private String roleId; + + /** + * 应用ID + */ + @NotEmpty(message = "应用ID不能为空") + private String appId; + + /** + * 资源ID + */ + private Set resIds; + + /** + * 半选中资源 + */ + private Set halfResIds; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getAppId() { + return appId; + } + + public void setAppId(String appId) { + this.appId = appId; + } + + public Set getResIds() { + return resIds; + } + + public void setResIds(Set resIds) { + this.resIds = resIds; + } + + public Set getHalfResIds() { + return halfResIds; + } + + public void setHalfResIds(Set halfResIds) { + this.halfResIds = halfResIds; + } + + @Override + public String toString() { + return "GrantRoleResourceDTO{" + + "roleId='" + roleId + '\'' + + ", appId='" + appId + '\'' + + ", resIds=" + resIds + + ", halfResIds=" + halfResIds + + '}'; + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/SysResourceDTO.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/SysResourceDTO.java new file mode 100644 index 00000000..139ed398 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/dto/SysResourceDTO.java @@ -0,0 +1,17 @@ +package com.dstz.auth.rest.model.dto; + +import com.dstz.auth.core.entity.SysResource; + +import java.util.List; + +public class SysResourceDTO extends SysResource { + private List children ; + + public List getChildren() { + return children; + } + + public void setChildren(List children) { + this.children = children; + } +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceTreeVO.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceTreeVO.java new file mode 100644 index 00000000..b92cc0e2 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceTreeVO.java @@ -0,0 +1,116 @@ +package com.dstz.auth.rest.model.vo; + +import com.baomidou.mybatisplus.annotation.TableField; +import com.dstz.auth.authentication.api.model.ISysResource; +import com.dstz.auth.core.entity.SysResource; + +import java.io.Serializable; +import java.util.List; + +public class SysResourceTreeVO extends SysResource implements ISysResource, Serializable { + + + /** + * 是否已分配给角色 + */ + @TableField(exist = false) + protected boolean checked = false; + + + private List children; + + @Override + public List getChildren() { + return children; + } + + @Override + public void setChildren(List children) { + this.children = children; + } + + + public boolean isChecked() { + return checked; + } + + public void setChecked(boolean checked) { + this.checked = checked; + } + + + @Override + public String getId() { + return super.getId(); + } + + @Override + public String getAppId() { + return super.getAppId(); + } + + + @Override + public String getCode() { + return super.getCode(); + } + + @Override + public String getPath() { + return super.getPath(); + } + + @Override + public String getName() { + return super.getName(); + } + + @Override + public String getUrl() { + return super.getUrl(); + } + + @Override + public Integer getEnable() { + return super.getEnable(); + } + + @Override + public Integer getOpened() { + return super.getOpened(); + } + + @Override + public Integer getIsApi() { + return super.getIsApi(); + } + + @Override + public String getIcon() { + return super.getIcon(); + } + + @Override + public String getType() { + return super.getType(); + } + + @Override + public Integer getSn() { + return super.getSn(); + } + + @Override + public String getParentId() { + return super.getParentId(); + } + + + @Override + public Boolean getHidden() { + if (this.getEnable() == null) return false; + + return this.getEnable() == 0; + } + +} diff --git a/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceVO.java b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceVO.java new file mode 100644 index 00000000..4b1a685c --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/java/com/dstz/auth/rest/model/vo/SysResourceVO.java @@ -0,0 +1,48 @@ +package com.dstz.auth.rest.model.vo; + +import com.dstz.auth.authentication.api.model.ISysResource; +import com.dstz.auth.core.entity.SysResource; + +import java.io.Serializable; +import java.util.List; + +public class SysResourceVO extends SysResource implements ISysResource, Serializable { + private List children; + + private String parentName; + + protected boolean checked = false; + + public boolean isChecked() { + return checked; + } + + public void setChecked(boolean checked) { + this.checked = checked; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public List getChildren() { + return children; + } + + @Override + public void setChildren(List list) { + this.children = children; + } + + + @Override + public Boolean getHidden() { + if (this.getEnable() == null) return false; + + return this.getEnable() == 0; + } +} diff --git a/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/ResourceRoleMapper.xml b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/ResourceRoleMapper.xml new file mode 100644 index 00000000..b5c1eab7 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/ResourceRoleMapper.xml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + DELETE FROM sys_resource_role + WHERE + role_id_=#{roleId} + AND + app_id_=#{appId} + + + + + + + + diff --git a/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysApplicationMapper.xml b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysApplicationMapper.xml new file mode 100644 index 00000000..db2ea906 --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysApplicationMapper.xml @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE sys_application SET is_default_=0 + + + + + + + diff --git a/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysResourceMapper.xml b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysResourceMapper.xml new file mode 100644 index 00000000..4beb303c --- /dev/null +++ b/ab-auth/ab-auth-core/src/main/resources/com/dstz/auth/core/mapper/SysResourceMapper.xml @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-auth/ab-auth-spring-security-oauth2/pom.xml b/ab-auth/ab-auth-spring-security-oauth2/pom.xml new file mode 100644 index 00000000..437a62e5 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/pom.xml @@ -0,0 +1,68 @@ + + + + ab-auth + com.dstz + 2.5.0 + + 4.0.0 + + ab-auth-spring-security-oauth2 + + + + org.springframework.security + spring-security-config + + + org.springframework.security + spring-security-web + + + + com.dstz + ab-auth-api + ${project.version} + + + + com.dstz + ab-sys-api + ${project.version} + + + + com.dstz + ab-base-web + + + + cn.hutool + hutool-captcha + + + + cn.hutool + hutool-http + ${hutool.version} + + + io.springboot.security + spring-security-oauth2-authorization-server + 0.3.0 + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + 2.1.2.RELEASE + + + jaxb-impl + com.sun.xml.bind + + + + + diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/api/WebSessionAttributesApiImpl.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/api/WebSessionAttributesApiImpl.java new file mode 100644 index 00000000..f1a16d39 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/api/WebSessionAttributesApiImpl.java @@ -0,0 +1,91 @@ +package com.dstz.auth.api; + +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.web.api.WebSessionAttributesApi; +import com.google.common.collect.ImmutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.AbstractAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Component; + +import java.io.Serializable; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * web 会话属性接口实现 + * + * @author wacxhs + */ +@Component +public class WebSessionAttributesApiImpl implements WebSessionAttributesApi { + + private static final Logger logger = LoggerFactory.getLogger(WebSessionAttributesApiImpl.class); + + @Autowired + private TokenStore tokenStore; + + @Override + public void setValue(String name, Serializable value) { + Map values = ImmutableMap.of(name, value); + setValues(values); + } + + @Override + public void setValues(Map values) { + OAuth2Authentication oauth2Authentication = getOauth2Authentication(); + if (oauth2Authentication == null) { + return; + } + AbstractAuthenticationToken authenticationToken = (AbstractAuthenticationToken) oauth2Authentication.getUserAuthentication(); + Map originDetails = CastUtils.cast(authenticationToken.getDetails()); + + Map newDetails = new LinkedHashMap<>(); + if (originDetails != null) { + newDetails.putAll(originDetails); + } + + // 更新值 + for (Map.Entry entry : values.entrySet()) { + if (entry.getValue() == null) { + newDetails.remove(entry.getKey()); + } else { + newDetails.put(entry.getKey(), entry.getValue()); + } + } + authenticationToken.setDetails(newDetails); + + // 存储 + OAuth2AccessToken accessToken = tokenStore.readAccessToken(((OAuth2AuthenticationDetails)oauth2Authentication.getDetails()).getTokenValue()); + tokenStore.storeAccessToken(accessToken, oauth2Authentication); + } + + @Override + public Serializable getValue(String name) { + OAuth2Authentication oauth2Authentication = getOauth2Authentication(); + if (oauth2Authentication == null) { + return null; + } + Object details = oauth2Authentication.getUserAuthentication().getDetails(); + if (details == null) { + return null; + } + return CastUtils.>cast(details).get(name); + } + + private OAuth2Authentication getOauth2Authentication() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (!(authentication instanceof OAuth2Authentication)) { + logger.info("当前非OAuth2实现,无法切换租户"); + return null; + } + return (OAuth2Authentication) authentication; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbAuthenticationTokenGenerator.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbAuthenticationTokenGenerator.java new file mode 100644 index 00000000..54b829ad --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbAuthenticationTokenGenerator.java @@ -0,0 +1,45 @@ +package com.dstz.auth.authentication; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import com.dstz.base.common.utils.AbRequestUtils; +import org.springframework.security.oauth2.common.util.OAuth2Utils; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.OAuth2Request; +import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator; + +import javax.servlet.http.HttpServletRequest; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.TreeSet; +import java.util.UUID; + +/** + * 重写token生成 + * + * @author lightning + */ +public class AbAuthenticationTokenGenerator extends DefaultAuthenticationKeyGenerator { + + private static final String SCOPE = "scope"; + + private static final String USERNAME = "username"; + + private static final String UUID = "uuid"; + + @Override + public String extractKey(OAuth2Authentication authentication) { + Map values = new LinkedHashMap<>(); + OAuth2Request authorizationRequest = authentication.getOAuth2Request(); + if (!authentication.isClientOnly()) { + values.put(USERNAME, authentication.getName()); + } + if (authorizationRequest.getScope() != null) { + values.put(SCOPE, OAuth2Utils.formatParameterList(new TreeSet<>(authorizationRequest.getScope()))); + } + // authorizationRequest.getRequestParameters().get("param"); + values.put(UUID, StrUtil.uuid()); + return generateKey(values); + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbClientDetailsServiceImpl.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbClientDetailsServiceImpl.java new file mode 100644 index 00000000..d7603eed --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbClientDetailsServiceImpl.java @@ -0,0 +1,70 @@ +package com.dstz.auth.authentication; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.SysApplicationApi; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.authentication.vo.SysApplicationVO; +import com.dstz.auth.exception.Auth2CustomizeExceptionTranslator; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.exceptions.InvalidClientException; +import org.springframework.security.oauth2.provider.ClientDetails; +import org.springframework.security.oauth2.provider.client.BaseClientDetails; +import org.springframework.security.oauth2.provider.client.JdbcClientDetailsService; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.Collections; + +/** + * 客户端信息 + * + * @author lightning + */ +@Component +public class AbClientDetailsServiceImpl extends JdbcClientDetailsService { + + @Autowired + private SysApplicationApi sysApplicationApi; + + public AbClientDetailsServiceImpl(DataSource dataSource) { + super(dataSource); + } + + @Override + public ClientDetails loadClientByClientId(String clientId) throws InvalidClientException { + try { + SysApplicationVO sysApplication = sysApplicationApi.getByCode(clientId); + Assert.notNull(sysApplication, () -> new BusinessException(AuthStatusCode.NO_FIND_APP.formatDefaultMessage(clientId))); + + BaseClientDetails baseClientDetails = new BaseClientDetails(); + //客户端clientId + baseClientDetails.setClientId(sysApplication.getCode()); + //客户端秘钥 + baseClientDetails.setClientSecret("{noop}" + sysApplication.getSecret()); + // 方法级权限控制 指定客户端申请的权限范围 + baseClientDetails.setScope(StrUtil.split(sysApplication.getScope(), StrPool.COMMA)); + //客户端所能访问的资源id集合,多个资源时用逗号(,)分隔 如果没设置,就是所有权限 + baseClientDetails.setResourceIds(StrUtil.isNotEmpty(sysApplication.getResourceIds()) ? StrUtil.split(sysApplication.getResourceIds(), StrPool.COMMA) : null); + //支持的鉴权类型 + baseClientDetails.setAuthorizedGrantTypes(StrUtil.isNotEmpty(sysApplication.getGrantTypes()) ? StrUtil.split(sysApplication.getGrantTypes(), StrPool.COMMA) : CollUtil.newArrayList()); + + //回调地址 + baseClientDetails.setRegisteredRedirectUri(StrUtil.isNotEmpty(sysApplication.getRedirectUri()) ? CollUtil.newHashSet(StrUtil.split(sysApplication.getRedirectUri(), StrPool.COMMA)) : null); + + //设置用户是否自动Approval操作, 默认值为 'false'该字段只适用于grant_type="authorization_code"的情况,当用户登录成功后,若该值为'true'或支持的scope值,则会跳过用户Approve的页面, 直接授权. + baseClientDetails.setAutoApproveScopes(Collections.singletonList(sysApplication.getAutoapprove().toString())); + + //token有效时间 + baseClientDetails.setAccessTokenValiditySeconds(sysApplication.getAccessTokenValidity()); + //刷新token有效时间 + baseClientDetails.setRefreshTokenValiditySeconds(sysApplication.getRefreshTokenValidity()); + return baseClientDetails; + } catch (Exception e) { + throw new Auth2CustomizeExceptionTranslator.CustomizeException(AuthStatusCode.LOAD_CLIENT_BY_CLIENTID_ERROR.formatDefaultMessage(e.getMessage())); + } + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbDaoAuthenticationProvider.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbDaoAuthenticationProvider.java new file mode 100644 index 00000000..ccf91750 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbDaoAuthenticationProvider.java @@ -0,0 +1,94 @@ +package com.dstz.auth.authentication; + +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.MultipleFromAuthentication; +import com.dstz.auth.authentication.api.constant.AuthApiConstant; +import com.dstz.auth.model.LoginUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.authentication.dao.DaoAuthenticationProvider; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.web.authentication.session.SessionAuthenticationStrategy; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; + +/** + * 重写根据系统来源登录 + * + * @author lightning + */ +public class AbDaoAuthenticationProvider extends DaoAuthenticationProvider { + + @Autowired + private ApplicationContext applicationContext; + + @Autowired(required = false) + private SessionAuthenticationStrategy sessionStrategy; + + + + private Map multipleFromAuthenticationBeanMap; + + /** + * 将来源转换为小写,在匹配时不区分大小写 + * + * @param from 账户来源 + * @return 小写字符来源 + */ + private String fromToLowerCase(String from) { + return StrUtil.swapCase(from); + } + + private MultipleFromAuthentication getMultipleFromAuthenticationImplement(String from) { + Map localMap = multipleFromAuthenticationBeanMap; + if (localMap == null) { + synchronized (this) { + localMap = multipleFromAuthenticationBeanMap; + if (localMap == null) { + Collection multipleFromAuthenticationBeans = applicationContext.getBeansOfType(MultipleFromAuthentication.class).values(); + localMap = new HashMap<>(multipleFromAuthenticationBeans.size()); + for (MultipleFromAuthentication multipleFromAuthenticationBean : multipleFromAuthenticationBeans) { + localMap.put(fromToLowerCase(multipleFromAuthenticationBean.getFrom()), multipleFromAuthenticationBean); + } + multipleFromAuthenticationBeanMap = localMap; + } + } + } + return localMap.get(fromToLowerCase(from)); + } + + @Override + protected void additionalAuthenticationChecks(UserDetails userDetails, UsernamePasswordAuthenticationToken + authentication) { + if (!(userDetails instanceof LoginUser)) { + super.additionalAuthenticationChecks(userDetails, authentication); + } else { + LoginUser loginUser = (LoginUser) userDetails; + if (StrUtil.isEmpty(loginUser.getFrom()) || AuthApiConstant.SYSTEM.equalsIgnoreCase(loginUser.getFrom())) { + super.additionalAuthenticationChecks(userDetails, authentication); + } else { + multipleFromAuthentication(loginUser, authentication); + } + } + //兼容其它版本未注入 + if (sessionStrategy != null) { + ServletRequestAttributes requestAttributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + sessionStrategy.onAuthentication(new UsernamePasswordAuthenticationToken(authentication.getPrincipal(), null, userDetails.getAuthorities()), requestAttributes.getRequest(), requestAttributes.getResponse()); + } + } + + private void multipleFromAuthentication(LoginUser userDetails, UsernamePasswordAuthenticationToken + authentication) { + MultipleFromAuthentication multipleFromAuthentication = getMultipleFromAuthenticationImplement(userDetails.getFrom()); + if (multipleFromAuthentication == null) { + super.additionalAuthenticationChecks(userDetails, authentication); + } else { + multipleFromAuthentication.authentication(userDetails, authentication); + } + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleAuthProvider.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleAuthProvider.java new file mode 100644 index 00000000..b3fa16e5 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleAuthProvider.java @@ -0,0 +1,79 @@ +package com.dstz.auth.authentication; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.auth.authentication.api.SysResourceApi; +import com.dstz.auth.constant.PlatformConsts; +import com.dstz.base.common.utils.UserContextUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.access.AccessDecisionManager; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.authentication.InsufficientAuthenticationException; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.web.FilterInvocation; + +import java.util.Collection; +import java.util.Collections; +import java.util.Set; + +/** + * 访问决策管理器。 通过当前资源需要角色,判断用户是否有这些角色,若有则通过 + * + * @author lightning + */ +public class AbResourceRoleAuthProvider implements AccessDecisionManager { + + private final Logger logger = LoggerFactory.getLogger(getClass()); + + @Autowired + private SysResourceApi sysResourceApi; + + /** + * 判断 当前用户所拥有的角色 是否满足 当前资源所需的角色 + */ + @Override + public void decide(Authentication authentication, Object object, Collection configAttributes) throws AccessDeniedException, InsufficientAuthenticationException { + if (configAttributes.contains(PlatformConsts.ROLE_CONFIG_ANONYMOUS)) { + return; + } + if (authentication.isAuthenticated()) { + FilterInvocation filterInvocation = (FilterInvocation) object; + final String servletPath = filterInvocation.getRequest().getServletPath(); + + if (UserContextUtils.isSuperAdmin()) { + logger.trace("路径:{}, 当前访问用户为超级管理员, 跳过资源管控", servletPath); + return; + } + + Set accessRoleByUrl = sysResourceApi.getAccessRoleByUrl(servletPath); + if (CollUtil.isEmpty(accessRoleByUrl)) { + logger.trace("路径:{}, 未查询到授权角色,当前访问将不做权限校验", servletPath); + return; + } + + // 遍历当前用户的角色,如果当前页面对应的角色包含当前用户的某个角色则有权限访问。 + for (GrantedAuthority grantedAuthority : ObjectUtil.>defaultIfNull(authentication.getAuthorities(), Collections.emptyList())) { + if (accessRoleByUrl.contains(grantedAuthority.getAuthority())) { + return; + } + } + } + throw new AccessDeniedException("对不起,您没有该资源访问权限"); + } + + + @Override + public boolean supports(ConfigAttribute attribute) { + return true; + } + + @Override + public boolean supports(Class clazz) { + return true; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleRelationProvider.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleRelationProvider.java new file mode 100644 index 00000000..43ca65a2 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/authentication/AbResourceRoleRelationProvider.java @@ -0,0 +1,41 @@ +package com.dstz.auth.authentication; + +import cn.hutool.core.collection.CollUtil; +import com.dstz.auth.constant.PlatformConsts; +import com.dstz.auth.utils.IngoreChecker; +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collection; + +/** + * 根据当前的URL获取他上面分配的角色列表 + * + * @author lightning + */ +public class AbResourceRoleRelationProvider extends IngoreChecker implements FilterInvocationSecurityMetadataSource { + + @Override + public Collection getAttributes(Object object) throws IllegalArgumentException { + FilterInvocation filterInvocation = ((FilterInvocation) object); + HttpServletRequest request = filterInvocation.getRequest(); + // 是否匿名访问 + if (isIngores(request)) { + return CollUtil.newHashSet(PlatformConsts.ROLE_CONFIG_ANONYMOUS); + } + return CollUtil.newHashSet(PlatformConsts.URL_AUTH); + } + + @Override + public Collection getAllConfigAttributes() { + return null; + } + + @Override + public boolean supports(Class clazz) { + return true; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/AuthConstant.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/AuthConstant.java new file mode 100644 index 00000000..674d5ab0 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/AuthConstant.java @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2018-2028, Chill Zhuang All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * Neither the name of the dreamlu.net developer nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * Author: Chill 庄骞 (smallchill@163.com) + */ +package com.dstz.auth.constant; + +/** + * 授权校验常量 + * + * @author lightning + */ +public interface AuthConstant { + + /** + * 表字段 + */ + String CLIENT_FIELDS = "client_id_, CONCAT('{noop}',client_secret_) as client_secret_, resource_ids_, scope_, grant_types_, " + + "redirect_uri_, authorities_, access_token_validity_, " + + "refresh_token_validity_, commentate_, autoapprove_, status_, is_deleted_, create_time_, create_by_, create_org_id_, update_time_,updater_,update_by_"; + + /** + * 查询语句 + */ + String BASE_STATEMENT = "select " + CLIENT_FIELDS + " from oauth_client"; + + /** + * 查询排序 + */ + String DEFAULT_FIND_STATEMENT = BASE_STATEMENT + " order by client_id_"; + + /** + * 查询条件 + */ + String DEFAULT_SELECT_STATEMENT = BASE_STATEMENT + " where client_id_ = ?"; + + /** + * oauth内置接口集合 + */ + String [] OAUTH_DEFAULT_URL = new String[]{"/oauth/token","/oauth/authorize"}; + + /** + * oauth grant_type + */ + String GRANT_TYPE = "grant_type"; + + /** + * grant_type authorization_code + */ + String AUTHORIZATION_CODE = "authorization_code"; + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/PlatformConsts.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/PlatformConsts.java new file mode 100644 index 00000000..17c14738 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/constant/PlatformConsts.java @@ -0,0 +1,26 @@ +package com.dstz.auth.constant; + +import org.springframework.security.access.ConfigAttribute; +import org.springframework.security.access.SecurityConfig; + +/** + * 权限类型 + * + * @author lightning + */ +public class PlatformConsts { + + /** + * 匿名级 + */ + private static final String ROLE_ANONYMOUS = "ROLE_ANONYMOUS"; + + /** + * 验证权限 + */ + private final static String ROLE_AUTH = "ROLE_AUTH"; + + public static final ConfigAttribute ROLE_CONFIG_ANONYMOUS = new SecurityConfig(ROLE_ANONYMOUS); + + public static final ConfigAttribute URL_AUTH = new SecurityConfig(ROLE_AUTH); +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/Auth2CustomizeExceptionTranslator.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/Auth2CustomizeExceptionTranslator.java new file mode 100644 index 00000000..81244298 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/Auth2CustomizeExceptionTranslator.java @@ -0,0 +1,94 @@ +package com.dstz.auth.exception; + +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.codes.IBaseCode; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.core.userdetails.UsernameNotFoundException; +import org.springframework.security.oauth2.common.DefaultThrowableAnalyzer; +import org.springframework.security.oauth2.common.exceptions.InvalidGrantException; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; +import org.springframework.security.oauth2.provider.error.WebResponseExceptionTranslator; +import org.springframework.security.web.util.ThrowableAnalyzer; +import org.springframework.web.HttpRequestMethodNotSupportedException; +/** + * 自定义oauth异常 + * + * @author lightning + */ +public class Auth2CustomizeExceptionTranslator implements WebResponseExceptionTranslator { + + private ThrowableAnalyzer throwableAnalyzer = new DefaultThrowableAnalyzer(); + + @Override + public ResponseEntity translate(Exception e) throws Exception { + + // Try to extract a SpringSecurityException from the stacktrace + Throwable[] causeChain = throwableAnalyzer.determineCauseChain(e); + Exception ase = (OAuth2Exception) throwableAnalyzer.getFirstThrowableOfType(OAuth2Exception.class, causeChain); + //用户名或密码错误 + if (ase instanceof InvalidGrantException) { + return dealResponseEntity(AuthStatusCode.LOGIN_ERROR); + } + + ase = (AuthenticationException) throwableAnalyzer.getFirstThrowableOfType(AuthenticationException.class, + causeChain); + //登录用户名未找到 + if (ase instanceof UsernameNotFoundException) { + return dealResponseEntity(AuthStatusCode.ACCOUNT_NOT_FIND); + } + + ase = (AccessDeniedException) throwableAnalyzer + .getFirstThrowableOfType(AccessDeniedException.class, causeChain); + //无访问权限 + if (ase instanceof AccessDeniedException) { + return dealResponseEntity(AuthStatusCode.APP_CONFIG_ERROR.getCode(), AuthStatusCode.APP_CONFIG_ERROR.getMessage() + e.getMessage()); + } + + ase = (HttpRequestMethodNotSupportedException) throwableAnalyzer.getFirstThrowableOfType( + HttpRequestMethodNotSupportedException.class, causeChain); + //方法未被允许 + if (ase instanceof HttpRequestMethodNotSupportedException) { + return dealResponseEntity(AuthStatusCode.METHOD_NOT_ALLOWED.getCode(), AuthStatusCode.METHOD_NOT_ALLOWED.getMessage() + e.getMessage()); + + } + //其他异常 + return dealResponseEntity(AuthStatusCode.SERVICE_ERROR.getCode(), AuthStatusCode.SERVICE_ERROR.getMessage() + e.getMessage()); + + + } + + private static ResponseEntity dealResponseEntity(IBaseCode baseCode) { + return dealResponseEntity(baseCode.getCode(), baseCode.getMessage()); + } + + private static ResponseEntity dealResponseEntity(String code, String message) { + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiResponse.fail(code, message)); + } + + public static class CustomizeException extends MyOAuth2Exception { + String code = ""; + + public CustomizeException(IBaseCode baseCode) { + super(baseCode.getMessage()); + this.code = baseCode.getCode(); + } + + public CustomizeException(String code, String msg) { + super(msg); + this.code = code; + } + + @Override + public String getOAuth2ErrorCode() { + return code; + } + } + + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/AuthExceptionEntryPoint.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/AuthExceptionEntryPoint.java new file mode 100644 index 00000000..0300b68a --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/AuthExceptionEntryPoint.java @@ -0,0 +1,30 @@ +package com.dstz.auth.exception; + +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 鉴权失败返回信息 + * + * @author lightning + */ +public class AuthExceptionEntryPoint implements AuthenticationEntryPoint { + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException { + byte[] responseBytes = JsonUtils.toJSONBytes(ApiResponse.fail(AuthStatusCode.TOKEN_INVALID.getCode(), AuthStatusCode.TOKEN_INVALID.getMessage())); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + response.setContentLength(responseBytes.length); + response.getOutputStream().write(responseBytes, 0, responseBytes.length); + response.getOutputStream().flush(); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeAccessDeniedHandler.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeAccessDeniedHandler.java new file mode 100644 index 00000000..12e00eba --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeAccessDeniedHandler.java @@ -0,0 +1,24 @@ +package com.dstz.auth.exception; + +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +/** + * 权限不足返回信息 + * + * @author lightning + */ +public class CustomizeAccessDeniedHandler implements AccessDeniedHandler { + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException accessDeniedException) throws IOException, ServletException { + response.setContentType("text/html;charset=UTF-8"); + response.getWriter().write(JsonUtils.toJSONString(ApiResponse.fail(AuthStatusCode.NOT_PERMISSION.getCode(), AuthStatusCode.NOT_PERMISSION.getMessage()))); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOAuthExceptionDeserializer.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOAuthExceptionDeserializer.java new file mode 100644 index 00000000..c5c918bb --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOAuthExceptionDeserializer.java @@ -0,0 +1,104 @@ +package com.dstz.auth.exception; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.JsonToken; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.deser.std.StdDeserializer; +import org.springframework.security.oauth2.common.exceptions.*; +import org.springframework.security.oauth2.common.util.OAuth2Utils; + +import java.io.IOException; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 自定义反序列化 + * + * @author lightning + */ +public class CustomizeOAuthExceptionDeserializer extends StdDeserializer { + public CustomizeOAuthExceptionDeserializer() { + super(OAuth2Exception.class); + } + + @Override + public OAuth2Exception deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException, + JsonProcessingException { + + JsonToken t = jp.getCurrentToken(); + if (t == JsonToken.START_OBJECT) { + t = jp.nextToken(); + } + Map errorParams = new HashMap(); + for (; t == JsonToken.FIELD_NAME; t = jp.nextToken()) { + // Must point to field name + String fieldName = jp.getCurrentName(); + // And then the value... + t = jp.nextToken(); + // Note: must handle null explicitly here; value deserializers won't + Object value; + if (t == JsonToken.VALUE_NULL) { + value = null; + } + else if (t == JsonToken.START_ARRAY) { + value = jp.readValueAs(List.class); + } else if (t == JsonToken.START_OBJECT) { + value = jp.readValueAs(Map.class); + } else { + value = jp.getText(); + } + errorParams.put(fieldName, value); + } + + Object errorCode = errorParams.get(OAuth2Exception.ERROR); + String errorMessage = errorParams.get(OAuth2Exception.DESCRIPTION) != null ? errorParams.get(OAuth2Exception.DESCRIPTION).toString() : null; + if (errorMessage == null) { + errorMessage = errorCode == null ? "OAuth Error" : errorCode.toString(); + } + + OAuth2Exception ex; + if (OAuth2Exception.INVALID_CLIENT.equals(errorCode)) { + ex = new InvalidClientException(errorMessage); + } else if (OAuth2Exception.UNAUTHORIZED_CLIENT.equals(errorCode)) { + ex = new UnauthorizedClientException(errorMessage); + } else if (OAuth2Exception.INVALID_GRANT.equals(errorCode)) { + if (errorMessage.toLowerCase().contains("redirect") && errorMessage.toLowerCase().contains("match")) { + ex = new RedirectMismatchException(errorMessage); + } else { + ex = new InvalidGrantException(errorMessage); + } + } else if (OAuth2Exception.INVALID_SCOPE.equals(errorCode)) { + ex = new InvalidScopeException(errorMessage); + } else if (OAuth2Exception.INVALID_TOKEN.equals(errorCode)) { + ex = new InvalidTokenException(errorMessage); + } else if (OAuth2Exception.INVALID_REQUEST.equals(errorCode)) { + ex = new InvalidRequestException(errorMessage); + } else if (OAuth2Exception.REDIRECT_URI_MISMATCH.equals(errorCode)) { + ex = new RedirectMismatchException(errorMessage); + } else if (OAuth2Exception.UNSUPPORTED_GRANT_TYPE.equals(errorCode)) { + ex = new UnsupportedGrantTypeException(errorMessage); + } else if (OAuth2Exception.UNSUPPORTED_RESPONSE_TYPE.equals(errorCode)) { + ex = new UnsupportedResponseTypeException(errorMessage); + } else if (OAuth2Exception.INSUFFICIENT_SCOPE.equals(errorCode)) { + ex = new InsufficientScopeException(errorMessage, OAuth2Utils.parseParameterList((String) errorParams + .get("scope"))); + } else if (OAuth2Exception.ACCESS_DENIED.equals(errorCode)) { + ex = new UserDeniedAuthorizationException(errorMessage); + } else { + ex = new OAuth2Exception(errorMessage); + } + + Set> entries = errorParams.entrySet(); + for (Map.Entry entry : entries) { + String key = entry.getKey(); + if (!"error".equals(key) && !"error_description".equals(key)) { + Object value = entry.getValue(); + ex.addAdditionalInformation(key, value == null ? null : value.toString()); + } + } + return ex; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOauthExceptionSerializer.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOauthExceptionSerializer.java new file mode 100644 index 00000000..d3cbd17e --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/CustomizeOauthExceptionSerializer.java @@ -0,0 +1,35 @@ +package com.dstz.auth.exception; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.std.StdSerializer; + +import java.io.IOException; +/** + * 自定义序列化 + * + * @author lightning + */ +public class CustomizeOauthExceptionSerializer extends StdSerializer { + public CustomizeOauthExceptionSerializer() { + super(MyOAuth2Exception.class); + } + + + @Override + public void serialize(MyOAuth2Exception value, JsonGenerator jgen, SerializerProvider provider) throws IOException, + JsonProcessingException { + + jgen.writeStartObject(); + jgen.writeFieldName("isOk"); + jgen.writeBoolean(Boolean.FALSE); + jgen.writeFieldName("code"); + jgen.writeString(value.getOAuth2ErrorCode()); + jgen.writeFieldName("message"); + jgen.writeString(value.getMessage()); + jgen.writeFieldName("msg"); + jgen.writeString(value.getMessage()); + jgen.writeEndObject(); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/MyOAuth2Exception.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/MyOAuth2Exception.java new file mode 100644 index 00000000..cae72531 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/exception/MyOAuth2Exception.java @@ -0,0 +1,23 @@ +package com.dstz.auth.exception; + +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import org.springframework.security.oauth2.common.exceptions.OAuth2Exception; + +/** + * 自定义异常类 + * + * @author lightning + */ +@JsonSerialize(using = CustomizeOauthExceptionSerializer.class) +@JsonDeserialize(using = CustomizeOAuthExceptionDeserializer.class) +public class MyOAuth2Exception extends OAuth2Exception { + public MyOAuth2Exception(String msg, Throwable t) { + super(msg, t); + } + + public MyOAuth2Exception(String msg) { + super(msg); + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/AuthorizationTokenCheckFilter.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/AuthorizationTokenCheckFilter.java new file mode 100644 index 00000000..aa93fe9c --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/AuthorizationTokenCheckFilter.java @@ -0,0 +1,46 @@ +package com.dstz.auth.filter; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import com.dstz.auth.utils.IngoreChecker; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; +import org.springframework.security.oauth2.common.OAuth2AccessToken; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static com.dstz.auth.authentication.api.constant.AuthApiConstant.AUTHORIZATION; +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.NO_AUTH_TOKEN; + +/** + * Authorization参数过滤 + * + * @author lightning + */ +public class AuthorizationTokenCheckFilter extends IngoreChecker implements Filter { + + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + //处理匿名接口 + if (this.isIngores(req)) { + chain.doFilter(request, response); + return; + } + String token = ServletUtil.getHeader(req, AUTHORIZATION, StrPool.EMPTY); + if (StrUtil.isBlank(token) || !token.trim().startsWith(OAuth2AccessToken.BEARER_TYPE)) { + response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + ServletUtil.write((HttpServletResponse) response, JsonUtils.toJSONString(ApiResponse.fail(NO_AUTH_TOKEN.getCode(), NO_AUTH_TOKEN.getMessage())), MediaType.APPLICATION_JSON_VALUE); + } else { + chain.doFilter(request, response); + } + } +} + diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/CorsFilter.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/CorsFilter.java new file mode 100644 index 00000000..14c2e276 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/CorsFilter.java @@ -0,0 +1,51 @@ +package com.dstz.auth.filter; + +import org.springframework.core.Ordered; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.HttpStatus; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * Cors过滤器 + * + * @author lightning + */ +public class CorsFilter extends OncePerRequestFilter implements Ordered { + + @Override + protected void doFilterInternal(HttpServletRequest req, HttpServletResponse resp, FilterChain filterChain) throws ServletException, IOException { + //此处我们忽略跨域问题由 @RefererCsrfFilter.java 来处理 + //默认全部支持跨域 + setHeader(resp, HttpHeaders.ACCESS_CONTROL_ALLOW_HEADERS, "accept, origin, content-type, accessToken, token, Authorization, Cookie"); + setHeader(resp, HttpHeaders.ACCESS_CONTROL_EXPOSE_HEADERS, "Ab-Product-Past"); + setHeader(resp, HttpHeaders.ACCESS_CONTROL_ALLOW_METHODS, "POST,GET,PUT"); + setHeader(resp, HttpHeaders.ACCESS_CONTROL_ALLOW_ORIGIN, req.getHeader(HttpHeaders.ORIGIN)); + setHeader(resp, HttpHeaders.ACCESS_CONTROL_ALLOW_CREDENTIALS, Boolean.TRUE.toString()); + + if (req.getMethod().equals(HttpMethod.OPTIONS.name())) { + resp.setStatus(HttpStatus.OK.value()); + setHeader(resp, HttpHeaders.ACCESS_CONTROL_MAX_AGE, "3600"); + } else { + filterChain.doFilter(req, resp); + } + } + + private void setHeader(HttpServletResponse resp, String name, String value) { + if (resp.getHeader(name) == null) { + resp.setHeader(name, value); + } + } + + @Override + public int getOrder() { + return -300; + } +} + diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/RefererCsrfFilter.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/RefererCsrfFilter.java new file mode 100644 index 00000000..c9656b92 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/RefererCsrfFilter.java @@ -0,0 +1,64 @@ +package com.dstz.auth.filter; + +import cn.hutool.extra.servlet.ServletUtil; +import com.dstz.auth.utils.IngoreChecker; +import com.dstz.auth.utils.RefererCsrfChecker; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; + +import javax.servlet.Filter; +import javax.servlet.FilterChain; +import javax.servlet.FilterConfig; +import javax.servlet.ServletException; +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +/** + * 防止CSRF跨站请求攻击。
+ * 这个主要是防止外链连入到本系统。 + * + * @author lightning + */ +public class RefererCsrfFilter extends RefererCsrfChecker implements Filter { + + + @Override + public void destroy() { + + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + + //判断是否外链。 + String referer = req.getHeader("Referer"); + String serverName = request.getServerName(); + //请求不是来自本网站。 + if (null != referer && referer.indexOf(serverName) < 0) { + //是否包含当前URL + boolean isIngoreUrl = this.isIngores(referer); + if (isIngoreUrl) { + chain.doFilter(request, response); + } else { + String msg = String.format("系统不支持当前域名的访问,请联系管理员!
服务器:%s,当前域名:%s", serverName, referer); + response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + ServletUtil.write((HttpServletResponse) response, JsonUtils.toJSONString(ApiResponse.fail(GlobalApiCodes.INTERNAL_ERROR.getCode(), msg)), MediaType.APPLICATION_JSON_VALUE); + } + } else { + chain.doFilter(request, response); + } + } + + @Override + public void init(FilterConfig config) { + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityInterceptor.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityInterceptor.java new file mode 100644 index 00000000..11897e28 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityInterceptor.java @@ -0,0 +1,68 @@ +package com.dstz.auth.filter; + + +import org.springframework.security.access.SecurityMetadataSource; +import org.springframework.security.access.intercept.AbstractSecurityInterceptor; +import org.springframework.security.access.intercept.InterceptorStatusToken; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; + +import javax.servlet.*; +import java.io.IOException; + +/** + * springsecurity 安全过滤器。 + * + * @author lightning + */ +public class SecurityInterceptor extends AbstractSecurityInterceptor implements Filter { + + private FilterInvocationSecurityMetadataSource securityMetadataSource; + + @Override + public void destroy() { + } + + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { + FilterInvocation fi = new FilterInvocation(request, response, chain); + boolean canInvokeNext = true; + if (canInvokeNext) { + invoke(fi); + } + } + + + public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() { + return this.securityMetadataSource; + } + + public Class getSecureObjectClass() { + return FilterInvocation.class; + } + + public void invoke(FilterInvocation fi) throws IOException, ServletException { + super.setRejectPublicInvocations(false); + InterceptorStatusToken token = super.beforeInvocation(fi); + try { + fi.getChain().doFilter(fi.getRequest(), fi.getResponse()); + } finally { + super.afterInvocation(token, null); + } + } + + public SecurityMetadataSource obtainSecurityMetadataSource() { + return this.securityMetadataSource; + } + + public void setSecurityMetadataSource( + FilterInvocationSecurityMetadataSource newSource) { + this.securityMetadataSource = newSource; + } + + @Override + public void init(FilterConfig filterConfig) { + //此处可能会拿到 xml配置里的 头 + } + + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityRequestCsrfMatcher.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityRequestCsrfMatcher.java new file mode 100644 index 00000000..0e32b0f1 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/SecurityRequestCsrfMatcher.java @@ -0,0 +1,26 @@ +package com.dstz.auth.filter; + +import com.dstz.auth.utils.IngoreChecker; +import org.springframework.security.web.util.matcher.RequestMatcher; + +import javax.servlet.http.HttpServletRequest; +/** + * csrf 安全过滤器。 + * + * @author lightning + */ +public class SecurityRequestCsrfMatcher extends IngoreChecker implements RequestMatcher { + + @Override + public boolean matches(HttpServletRequest request) { + + boolean isIngoreUrl = isIngores(request); + + if (isIngoreUrl) { + return true; + } + + return false; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/XssFilter.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/XssFilter.java new file mode 100644 index 00000000..2e6673bc --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/filter/XssFilter.java @@ -0,0 +1,83 @@ +package com.dstz.auth.filter; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.auth.utils.IngoreChecker; + +import javax.servlet.*; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; +import java.util.Enumeration; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +/** + * XSS安全过滤器。 + *
+ *  这个功能是为了放置XSS攻击。
+ *  如果有Xss攻击:
+ *  	1.表单提交方式,平台将去到一个提示页面。
+ *  	2.AJAX提交方式,弹出提示信息。
+ *  可以配置某些不需要检测的URL.
+ * 
+ * + * @author lightning + */ +public class XssFilter extends IngoreChecker implements Filter { + + private Pattern regex = Pattern.compile("<(\\S*?)[^>]*>.*?|<[^>]+>", Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | Pattern.DOTALL | Pattern.MULTILINE); + + @Override + public void destroy() { + } + + @Override + public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) + throws IOException, ServletException { + HttpServletRequest req = (HttpServletRequest) request; + //页面是否忽略。 + boolean isIngoreUrl = isIngores(req); + if (isIngoreUrl) { + chain.doFilter(request, response); + } else { + //检测是否有XSS攻击。 + boolean hasXss = checkXss(req); + if (hasXss) { + response.getWriter().print(JsonUtils.toJSONString(ApiResponse.fail(GlobalApiCodes.PARAMETER_INVALID.getCode(),"检测到提交内容含HTML代码,被拦截!"))); + } else { + chain.doFilter(request, response); + } + } + } + + + @Override + public void init(FilterConfig config) { + } + + /** + * 判断输入是否有XSS注入问题。 + * + * @param request + * @return + */ + private boolean checkXss(HttpServletRequest request) { + Enumeration params = request.getParameterNames(); + while (params.hasMoreElements()) { + String key = params.nextElement().toString(); + String[] vals = request.getParameterValues(key); + String val = StrUtil.join("", vals); + if (StrUtil.isEmpty(val)) continue; + + Matcher regexMatcher = regex.matcher(val); + if (regexMatcher.find()) { + return true; + } + } + return false; + } + + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAccessDeniedHandler.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAccessDeniedHandler.java new file mode 100644 index 00000000..5af41ae6 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAccessDeniedHandler.java @@ -0,0 +1,53 @@ +package com.dstz.auth.forbidden; + +import com.dstz.auth.authentication.api.constant.AuthApiConstant; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; +import org.springframework.security.access.AccessDeniedException; +import org.springframework.security.web.access.AccessDeniedHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 返回无权限 + * + * @author lightning + */ +public class DefaultAccessDeniedHandler implements AccessDeniedHandler { + + + @Override + public void handle(HttpServletRequest request, HttpServletResponse response, AccessDeniedException ex) throws IOException, ServletException { + // 登录超时记录下当前redirectURL + request.getSession().setAttribute(AuthApiConstant.SSO_REDIRECTURL, getBackUrl(request)); + + byte[] dataBytes = JsonUtils.toJSONString(ApiResponse.fail(AuthStatusCode.LOGIN_TIMEOUT.getCode(), ex.getMessage())).getBytes(); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setContentLength(dataBytes.length); + response.getOutputStream().write(dataBytes); + } + + private String getBackUrl(HttpServletRequest request) { + //服务器地址 + StringBuffer strBackUrl = new StringBuffer("http://").append(request.getServerName()) + + .append(":") + + .append(request.getServerPort()) //端口号 + + .append(request.getContextPath()) //项目名称 + + .append(request.getServletPath()) //请求页面或其他地址 + + .append("?").append((request.getQueryString())); + + + return strBackUrl.toString(); + } + +} \ No newline at end of file diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAuthenticationEntryPoint.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAuthenticationEntryPoint.java new file mode 100644 index 00000000..d863f157 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/forbidden/DefaultAuthenticationEntryPoint.java @@ -0,0 +1,62 @@ +package com.dstz.auth.forbidden; + +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.constant.AuthApiConstant; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.security.core.AuthenticationException; +import org.springframework.security.web.AuthenticationEntryPoint; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 超时访问 + * + * @author lightning + */ +public class DefaultAuthenticationEntryPoint implements AuthenticationEntryPoint { + + @Override + public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) throws IOException, ServletException { + // 登录超时记录下当前redirectURL + String referer = request.getHeader("Referer"); + String Url = getBackUrl(request); + if (StrUtil.isNotEmpty(referer) && StrUtil.isNotEmpty(request.getContentType())) { + request.getSession().setAttribute(AuthApiConstant.SSO_REDIRECTURL, referer); + } + + byte[] dataBytes = JsonUtils.toJSONString(ApiResponse.fail(GlobalApiCodes.ACCESS_FORBIDDEN.getCode(), "登录访问超时!")).getBytes(); + response.setStatus(HttpStatus.OK.value()); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.setContentLength(dataBytes.length); + response.getOutputStream().write(dataBytes); + } + + private String getBackUrl(HttpServletRequest request) { + //服务器地址 + StringBuffer strBackUrl = new StringBuffer("http://").append(request.getServerName()) + + .append(":") + //端口号 + .append(request.getServerPort()) + //项目名称 + .append(request.getContextPath()) + + .append(request.getServletPath()); + + if (StrUtil.isNotEmpty(request.getQueryString())) { + strBackUrl.append("?").append((request.getQueryString())); + } + + + return strBackUrl.toString(); + } + + +} \ No newline at end of file diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbLoginService.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbLoginService.java new file mode 100644 index 00000000..e9f7d431 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbLoginService.java @@ -0,0 +1,95 @@ +package com.dstz.auth.login; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.auth.login.model.TokenReqVO; +import com.dstz.auth.model.LoginUser; +import com.dstz.auth.utils.LoginUtils; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.encrypt.EncryptUtil; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.AbRequestUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.stereotype.Service; + +import java.util.Date; + +import static com.dstz.auth.authentication.api.constant.AuthCacheKeyConstant.*; +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.*; +import static com.dstz.base.common.constats.AbCacheRegionConstant.LOGIN_CACHE_REGION; + +@Service("abLoginService") +public class AbLoginService { + + private final String API_SUCCESS = "Success"; + @Autowired + ICache iCache; + @Autowired + AbTokenService tokenService; + @Autowired + UserApi userApi; + + /** + * 登录后处理数据 + * + * @param apiResponse + */ + public void loginPostposition(ApiResponse apiResponse) { + try { + if (apiResponse.getIsOk() && API_SUCCESS.equals(apiResponse.getCode())) { + String token = apiResponse.getData().getValue(); + OAuth2Authentication oAuth2Authentication = tokenService.getOAuth2AuthenticationByToken(token); + // 设置当前线程登录上下文信息 + SecurityContextHolder.getContext().setAuthentication(oAuth2Authentication); + UserContextUtils.getUserContext().clear(); + } + } catch (Exception e) { + throw new BusinessMessage(LOGIN_TIMEOUT); + + } + } + + /** + * 登录验证 + * + * @param tokenReqVO + */ + public void authLoginParam(TokenReqVO tokenReqVO) { + Assert.notBlank(tokenReqVO.getGrantType(),()-> new BusinessMessage(GRANTTYPE_CANNOT_BE_EMPTY)); + //校验验证码 + if (LoginUtils.queryCaptchaSwitch()) { + Assert.notBlank(tokenReqVO.getCaptcha(), () -> new BusinessMessage(CAPTCHA_CANNOT_BE_EMPTY)); + + String sessionId = AbRequestUtils.getHttpServletRequest().getSession().getId(); + Assert.isTrue(iCache.exists(LOGIN_CACHE_REGION, sessionId), () -> new BusinessMessage(CAPTCHA_ERROR)); + + String captcha = iCache.getIfPresent(LOGIN_CACHE_REGION, sessionId); + Assert.isTrue(tokenReqVO.getCaptcha().equalsIgnoreCase(captcha), () -> new BusinessMessage(CAPTCHA_ERROR)); + iCache.invalidate(LOGIN_CACHE_REGION, sessionId); + } + + IUser user = userApi.getByUsername(tokenReqVO.getUserName()); + Assert.notNull(user, () -> new BusinessMessage(ACCOUNT_NOT_FIND)); + Assert.isTrue(user.getAttrValue("status", Integer.class) == NumberPool.BOOLEAN_TRUE, () -> new BusinessMessage(ACCOUNT_DISABLED)); + + // 检查历史失败过几次 + String passordErrorMsg = LoginUtils.chckLoginFailedTimes(tokenReqVO.getUserName(), AbRequestUtils.getRequestIp()); + // 判断用户名密码是否匹配 + Assert.isTrue(ObjectUtil.isNotNull(user) && EncryptUtil.encryptSha256(tokenReqVO.getPassword()).equals(user.getAttrValue("password", String.class)), () -> new BusinessMessage(USER_NAME_OR_PASSORD_ERROR.formatDefaultMessage().formatDefaultMessage(passordErrorMsg))); + // 如果为默认密码,且系统配置 【默认密码需要重置】,则走重置页面 + if (LoginUtils.queryIsResetPwd()) { + // 如果没有过期时间则为默认密码 + Assert.isTrue(ObjectUtil.isNotNull(user.getAttrValue("expireDate", Date.class)), () -> new BusinessMessage(PASSWORD_NEEDS_TO_BE_CHANGED)); + } + //登录成功清除缓存 + iCache.invalidate(LOGIN_CACHE_REGION, USER_LOGIN_COUNT + tokenReqVO.getUserName()); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbTokenService.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbTokenService.java new file mode 100644 index 00000000..c5d52e3a --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/AbTokenService.java @@ -0,0 +1,180 @@ +package com.dstz.auth.login; + +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.api.SysApplicationApi; +import com.dstz.auth.authentication.api.model.ISysApplication; +import com.dstz.auth.login.model.RefreshTokenReqVO; +import com.dstz.auth.login.model.TokenReqVO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.common.utils.UserContextUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.core.userdetails.User; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationDetails; +import org.springframework.security.oauth2.provider.endpoint.TokenEndpoint; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Service; +import org.springframework.web.HttpRequestMethodNotSupportedException; +import org.springframework.web.client.RestTemplate; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import static com.dstz.auth.authentication.api.constant.AuthApiConstant.*; +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.APPLICATION_GET_TOKEN_ERROR; +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.APPLICATION_REFRESH_TOKEN_ERROR; + +@Service("abTokenService") +public class AbTokenService { + + @Autowired + UserDetailsService userDetailsService; + + @Autowired + ICache iCache; + + @Autowired + RestTemplate restTemplate; + + @Autowired + TokenEndpoint tokenEndpoint; + + @Autowired + TokenStore tokenStore; + + @Autowired + SysApplicationApi sysApplicationApi; + + /** + * 根据用户名获取token + * + * @param account + * @return + */ + public List getOAuth2AccessTokenByAccount(String account) { + return Optional.ofNullable(sysApplicationApi.getAllCode()) + .map(Collection::stream) + .orElseGet(Stream::empty) + .map(clientId -> tokenStore.findTokensByClientIdAndUserName(clientId, account)) + .flatMap(Collection::stream) + .collect(Collectors.toList()); + } + + /** + * 根据账户移除token + * + * @param account + */ + public void removeAccessToken(String account) { + getOAuth2AccessTokenByAccount(account).forEach(tokenStore::removeAccessToken); + } + + /** + * 根据loginvo获取token + * + * @param tokenReqVO + * @return + */ + public ApiResponse getToken(TokenReqVO tokenReqVO) { + Map paramMap = new HashMap<>(); + paramMap.put(OAUTH_TOKEN_CLIENT_KEY, tokenReqVO.getClientId()); + paramMap.put(OAUTH_TOKEN_GRANT_TYPE, tokenReqVO.getGrantType()); + paramMap.put(OAUTH_TOKEN_USER_NAME, tokenReqVO.getUserName()); + paramMap.put(OAUTH_TOKEN_PASSWORD, tokenReqVO.getPassword()); + paramMap.put(OAUTH_TOKEN_CLIENT_SECRET, tokenReqVO.getClientSecret()); + + User userDetail = new User(tokenReqVO.getClientId(), tokenReqVO.getClientSecret(), new ArrayList<>()); + Authentication request_token = new UsernamePasswordAuthenticationToken(userDetail, null, new ArrayList<>()); + try { + return (ApiResponse) tokenEndpoint.postAccessToken(request_token, paramMap).getBody(); + } catch (HttpRequestMethodNotSupportedException e) { + throw new ApiException(APPLICATION_GET_TOKEN_ERROR.formatDefaultMessage(tokenReqVO.getUserName(), tokenReqVO.getClientId()), e); + } + } + + /** + * 移除当前登录账户token + */ + public void removeCurrentAccessToken() { + String account = UserContextUtils.getAccount(); + Boolean isExitSystem = PropertyEnum.CHANGE_PWD_iS_Exit_SYSTEM.getPropertyValue(Boolean.class); + if (StrUtil.isNotEmpty(account) && isExitSystem) { + removeAccessToken(UserContextUtils.getAccount()); + } else { + tokenStore.removeAccessToken(getCurrentOAuth2AccessToken()); + } + } + + + /** + * 获取当前线程token串 + */ + public String getCurrentAccessTokenStr() { + OAuth2AuthenticationDetails details = (OAuth2AuthenticationDetails) SecurityContextHolder.getContext().getAuthentication().getDetails(); + return details.getTokenValue(); + } + + /** + * 获取当前线程token对象 + */ + public OAuth2AccessToken getCurrentOAuth2AccessToken() { + return tokenStore.readAccessToken(getCurrentAccessTokenStr()); + } + + /** + * 刷新token + * + * @param refreshTokenReqVO + * @return + */ + public ApiResponse refreshToken(RefreshTokenReqVO refreshTokenReqVO) { + Map paramMap = new HashMap<>(); + paramMap.put(OAUTH_TOKEN_CLIENT_KEY, refreshTokenReqVO.getClientId()); + paramMap.put(OAUTH_TOKEN_CLIENT_SECRET, refreshTokenReqVO.getClientSecret()); + paramMap.put(OAUTH_TOKEN_GRANT_TYPE, OAUTH_TOKEN_REFRESH_TOKEN); + paramMap.put(OAUTH_TOKEN_REFRESH_TOKEN, refreshTokenReqVO.getRefreshToken()); + + try { + User userDetail = new User(refreshTokenReqVO.getClientId(), refreshTokenReqVO.getClientSecret(), new ArrayList<>()); + Authentication request_token = new UsernamePasswordAuthenticationToken(userDetail, null, new ArrayList<>()); + return (ApiResponse) tokenEndpoint.postAccessToken(request_token, paramMap).getBody(); + } catch (HttpRequestMethodNotSupportedException e) { + throw new ApiException(APPLICATION_REFRESH_TOKEN_ERROR.formatDefaultMessage(refreshTokenReqVO.getClientId()), e); + } + } + + /** + * 根据应用获取token + * + * @param sysApplication + * @return + */ + private ApiResponse getToken(ISysApplication sysApplication) { + //根据用户账户获取用户信息 + UserDetails userDetails = userDetailsService.loadUserByUsername(UserContextUtils.getAccount()); + TokenReqVO tokenReqVO = new TokenReqVO(userDetails.getUsername(), userDetails.getPassword(), OAUTH_TOKEN_PASSWORD, sysApplication.getCode(), sysApplication.getSecret()); + return getToken(tokenReqVO); + } + + /** + * 根据token获取OAuth2Authentication + * + * @param token + * @return OAuth2Authentication + */ + public OAuth2Authentication getOAuth2AuthenticationByToken(String token) { + //token放入缓存 + return tokenStore.readAuthentication(token); + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/CustomPwdEncoder.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/CustomPwdEncoder.java new file mode 100644 index 00000000..268f9915 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/CustomPwdEncoder.java @@ -0,0 +1,67 @@ +package com.dstz.auth.login; + +import cn.hutool.core.util.StrUtil; +import org.apache.commons.codec.binary.Base64; +import org.springframework.security.crypto.password.PasswordEncoder; + +import java.nio.charset.StandardCharsets; +import java.security.MessageDigest; +/** + * 密码加密 + * + * @author lightning + */ +public class CustomPwdEncoder implements PasswordEncoder { + + private static ThreadLocal ingorePwd = new ThreadLocal<>(); + + public static void setIngore(boolean ingore) { + ingorePwd.set(ingore); + } + + public static void removeIngore(){ + ingorePwd.remove(); + } + + public final int MAX_LENGTH=25; + /** + * Encode the raw password. + * Generally, a good encoding algorithm applies a SHA-1 or greater hash combined with an 8-byte or greater randomly + * generated salt. + */ + @Override + public String encode(CharSequence rawPassword) { + String pwd = rawPassword.toString(); + try { + if(StrUtil.isNotBlank(pwd)&&pwd.length()>MAX_LENGTH){ + return pwd; + } + MessageDigest md = MessageDigest.getInstance("SHA-256"); + byte[] digest = md.digest(pwd.getBytes(StandardCharsets.UTF_8.displayName())); + return new String(Base64.encodeBase64(digest)); + } catch (Exception e) { + e.printStackTrace(); + } + return ""; + + } + + /** + * Verify the encoded password obtained from storage matches the submitted raw password after it too is encoded. + * Returns true if the passwords match, false if they do not. + * The stored password itself is never decoded. + * + * @param rawPassword the raw password to encode and match + * @param encodedPassword the encoded password from storage to compare with + * @return true if the raw password, after encoding, matches the encoded password from storage + */ + @Override + public boolean matches(CharSequence rawPassword, String encodedPassword) { + if (ingorePwd.get() == null || !ingorePwd.get()) { + String enc = this.encode(rawPassword); + return enc.equals(encodedPassword); + } + return true; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/UserDetailsServiceImpl.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/UserDetailsServiceImpl.java new file mode 100644 index 00000000..0df6c616 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/UserDetailsServiceImpl.java @@ -0,0 +1,53 @@ +package com.dstz.auth.login; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.auth.model.LoginUser; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.authority.SimpleGrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.core.userdetails.UsernameNotFoundException; + +import java.util.Collection; +import java.util.Collections; +import java.util.stream.Collectors; + +/** + * 实现UserDetailsService 接口获取UserDetails 接口实例对象。 + * + * @author lightning + */ +public class UserDetailsServiceImpl implements UserDetailsService { + + + @Autowired + private GroupApi groupApi; + + @Autowired + private UserApi userApi; + + @Override + public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { + IUser user = userApi.getByUsername(username); + if (user == null) { + throw new UsernameNotFoundException(String.format("%s does not exist", username)); + } + // 获取角色 + Collection authorities = ObjectUtil.defaultIfNull(groupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), user.getUserId()), Collections.emptyList()) + .stream().map(IGroup::getGroupCode).map(SimpleGrantedAuthority::new).collect(Collectors.toSet()); + + LoginUser loginUser = new LoginUser(); + loginUser.setDelegateUser(user); + loginUser.setAuthorities(authorities); + loginUser.setSuperAdmin(ArrayUtil.contains(PropertyEnum.ADMIN_ACCOUNTS.getPropertyValue(String[].class), user.getUsername())); + return loginUser; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/context/AbUserContext.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/context/AbUserContext.java new file mode 100644 index 00000000..7901bba6 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/context/AbUserContext.java @@ -0,0 +1,142 @@ +package com.dstz.auth.login.context; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.auth.model.LoginUser; +import com.dstz.base.common.context.UserContext; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.common.utils.ContextCleanUtils; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * Ab 用户上下文 + * + * @author wacxhs + */ +@Component("abUserContext") +public class AbUserContext implements UserContext, InitializingBean { + + private static final ThreadLocal THREAD_LOCAL = new ThreadLocal<>(); + + private GroupApi groupApi; + + + @Autowired + public void setGroupApi(GroupApi groupApi) { + this.groupApi = groupApi; + } + + private static class ThreadLocalVariable { + Optional user; + Optional org; + Collection authorizes; + Boolean superAdmin; + } + + private static ThreadLocalVariable getThreadLocalVariable() { + ThreadLocalVariable threadLocalVariable = THREAD_LOCAL.get(); + if (threadLocalVariable == null) { + THREAD_LOCAL.set(threadLocalVariable = new ThreadLocalVariable()); + LoginUser loginUser = Optional.ofNullable(SecurityContextHolder.getContext()).map(SecurityContext::getAuthentication).map(Authentication::getPrincipal).filter(o -> o instanceof LoginUser).map(LoginUser.class::cast).orElse(null); + if (loginUser != null) { + threadLocalVariable.user = Optional.of(loginUser.delegateUser()); + threadLocalVariable.superAdmin = loginUser.getSuperAdmin(); + threadLocalVariable.authorizes = ObjectUtil.defaultIfNull(loginUser.getAuthorities(), Collections.emptyList()).stream().map(GrantedAuthority::getAuthority).collect(Collectors.toSet()); + } else { + threadLocalVariable.user = Optional.empty(); + threadLocalVariable.org = Optional.empty(); + threadLocalVariable.superAdmin = Boolean.FALSE; + } + } + return threadLocalVariable; + } + + @Override + public Object duplicate() { + return THREAD_LOCAL.get(); + } + + @Override + public void fill(Object duplicate) { + THREAD_LOCAL.set((ThreadLocalVariable) duplicate); + } + + @Override + public Optional getUser() { + ThreadLocalVariable threadLocalVariable = getThreadLocalVariable(); + return threadLocalVariable.user; + } + + @Override + public void setUser(IUser user) { + getThreadLocalVariable().user = Optional.of(user); + } + + @Override + public Optional getOrg() { + ThreadLocalVariable threadLocalVariable = getThreadLocalVariable(); + if (Objects.isNull(threadLocalVariable.org)) { + threadLocalVariable.org = Optional.empty(); + IGroup org = null; + if (Objects.nonNull(threadLocalVariable.user) && threadLocalVariable.user.isPresent()) { + org = CollUtil.getFirst(groupApi.getByGroupTypeAndUserId(GroupType.ORG.getType(), threadLocalVariable.user.get().getUserId())); + } + threadLocalVariable.org = Optional.ofNullable(org); + } + return threadLocalVariable.org; + } + + @Override + public void setOrg(IGroup org) { + getThreadLocalVariable().org = Optional.ofNullable(org); + } + + @Override + public Collection getAuthorities() { + return THREAD_LOCAL.get().authorizes; + } + + @Override + public void setAuthorities(Collection authorities) { + THREAD_LOCAL.get().authorizes = authorities; + } + + @Override + public boolean isSuperAdmin() { + ThreadLocalVariable threadLocalVariable = getThreadLocalVariable(); + if (threadLocalVariable.superAdmin == null) { + String username = getUser().map(IUser::getUsername).orElseThrow(() -> new IllegalArgumentException("Current username null")); + threadLocalVariable.superAdmin = ArrayUtil.contains(PropertyEnum.ADMIN_ACCOUNTS.getPropertyValue(String[].class), username); + } + return threadLocalVariable.superAdmin; + } + + @Override + public void setSuperAdmin(boolean superAdmin) { + THREAD_LOCAL.get().superAdmin = superAdmin; + } + + @Override + public void clear() { + THREAD_LOCAL.remove(); + } + + @Override + public void afterPropertiesSet() { + ContextCleanUtils.register(this::clear, ContextCleanUtils.Phase.values()); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/listener/AbUserListener.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/listener/AbUserListener.java new file mode 100644 index 00000000..e2f1ce2f --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/listener/AbUserListener.java @@ -0,0 +1,69 @@ +package com.dstz.auth.login.listener; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.auth.login.AbTokenService; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.events.AbUserEvent; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationListener; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.stereotype.Component; + +import java.util.List; + +@Component +public class AbUserListener implements ApplicationListener { + + @Autowired + private UserDetailsService userDetailsService; + + @Autowired + private ICache cache; + + @Autowired + private TokenStore tokenStore; + + @Autowired + private AbTokenService abTokenService; + + @Override + public void onApplicationEvent(AbUserEvent event) { + //更新用户 + if (AbUserEvent.EventType.UPDATE_USER.equals(event.getEventType())) { + if (CollectionUtil.isNotEmpty(event.getUserAccountList())) { + event.getUserAccountList().forEach(e -> { + UserDetails userDetails = userDetailsService.loadUserByUsername(e); + List auth2AccessTokenList = abTokenService.getOAuth2AccessTokenByAccount(e); + auth2AccessTokenList.forEach(o ->{ + if (ObjectUtil.isNotEmpty(o)) { + OAuth2Authentication oAuth2Authentication = tokenStore.readAuthentication(o); + UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(userDetails, userDetails.getUsername(), userDetails.getAuthorities()); + // 拷贝登录时的变量信息 + authenticationToken.setDetails(oAuth2Authentication.getUserAuthentication().getDetails()); + tokenStore.storeAccessToken(o, new OAuth2Authentication(oAuth2Authentication.getOAuth2Request(), authenticationToken)); + } + }); + + }); + } + } + + //删除用户 + if (AbUserEvent.EventType.DELETE_USER.equals(event.getEventType())) { + if (CollectionUtil.isNotEmpty(event.getUserAccountList())) { + event.getUserAccountList().forEach(e -> { + abTokenService.removeAccessToken(e); + }); + } + } + } + + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/AbSessionInformationExpiredStrategy.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/AbSessionInformationExpiredStrategy.java new file mode 100644 index 00000000..f48fbdca --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/AbSessionInformationExpiredStrategy.java @@ -0,0 +1,30 @@ +package com.dstz.auth.login.logout; + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; +import org.springframework.security.web.session.SessionInformationExpiredEvent; +import org.springframework.security.web.session.SessionInformationExpiredStrategy; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * 会话过期策略 + * + * @author lightning + */ +public class AbSessionInformationExpiredStrategy implements SessionInformationExpiredStrategy { + + @Override + public void onExpiredSessionDetected(SessionInformationExpiredEvent event) throws IOException, ServletException { + byte[] body = JsonUtils.toJSONString(ApiResponse.fail(GlobalApiCodes.INTERNAL_ERROR.getCode(),"多地登录被迫下线")).getBytes(); + + HttpServletResponse response = event.getResponse(); + response.setContentType(MediaType.APPLICATION_JSON_VALUE); + response.getOutputStream().write(body, NumberPool.INTEGER_ZERO, body.length); + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/DefualtLogoutSuccessHandler.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/DefualtLogoutSuccessHandler.java new file mode 100644 index 00000000..a0ccd4b5 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/logout/DefualtLogoutSuccessHandler.java @@ -0,0 +1,43 @@ +package com.dstz.auth.login.logout; + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.utils.JsonUtils; +import org.springframework.http.MediaType; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.web.authentication.logout.LogoutSuccessHandler; + +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +import static com.dstz.auth.authentication.api.constant.AuthApiConstant.AUTHORIZATION; + +/** + * 默认退出登录 + * + * @author lightning + */ +public class DefualtLogoutSuccessHandler implements LogoutSuccessHandler { + + @Override + public void onLogoutSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { + TokenStore tokenStore = SpringUtil.getBean(TokenStore.class); + String token = ServletUtil.getHeader(request, AUTHORIZATION, ""); + if (StrUtil.isNotBlank(token)) { + OAuth2AccessToken accessToken = tokenStore.readAccessToken(token.replace(OAuth2AccessToken.BEARER_TYPE, "").trim()); + if (accessToken != null) { + tokenStore.removeAccessToken(accessToken); + } + } + response.setCharacterEncoding(StandardCharsets.UTF_8.displayName()); + ServletUtil.write(response, JsonUtils.toJSONString(ApiResponse.success("退出登录成功")), MediaType.APPLICATION_JSON_VALUE); + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/RefreshTokenReqVO.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/RefreshTokenReqVO.java new file mode 100644 index 00000000..077c1755 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/RefreshTokenReqVO.java @@ -0,0 +1,57 @@ +package com.dstz.auth.login.model; + +import javax.validation.constraints.NotBlank; +import java.io.Serializable; + +/** + * @author lightning + */ +public class RefreshTokenReqVO implements Serializable { + + @NotBlank(message = "应用id不能为空!") + private String clientId; + + @NotBlank(message = "应用密钥不能为空!") + private String clientSecret; + + + @NotBlank(message = "刷新token不能为空!") + private String refreshToken; + + + public RefreshTokenReqVO() { + } + + public RefreshTokenReqVO(@NotBlank(message = "应用id不能为空!") String clientId, @NotBlank(message = "应用密钥不能为空!") String clientSecret, @NotBlank(message = "刷新token不能为空!") String refreshToken) { + this.clientId = clientId; + this.clientSecret = clientSecret; + this.refreshToken = refreshToken; + } + + public String getClientId() { + return clientId; + } + + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + + public String getRefreshToken() { + return refreshToken; + } + + public void setRefreshToken(String refreshToken) { + this.refreshToken = refreshToken; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/TokenReqVO.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/TokenReqVO.java new file mode 100644 index 00000000..2b075955 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/login/model/TokenReqVO.java @@ -0,0 +1,87 @@ +package com.dstz.auth.login.model; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author lightning + */ +public class TokenReqVO implements Serializable { + + @NotBlank(message = "用户名不能为空!") + private String userName; + + @NotBlank(message = "密码不能为空!") + private String password; + + private String grantType; + + @NotBlank(message = "应用id不能为空!") + private String clientId; + + @NotBlank(message = "应用密钥不能为空!") + private String clientSecret; + + //验证码 + private String captcha; + + public TokenReqVO() { + } + + public TokenReqVO(@NotBlank(message = "用户名不能为空!") String userName, @NotBlank(message = "密码不能为空!") String password, @NotBlank(message = "验证类型不能为空!") String grantType, @NotBlank(message = "应用id不能为空!") String clientId, @NotBlank(message = "应用密钥不能为空!") String clientSecret) { + this.userName = userName; + this.password = password; + this.grantType = grantType; + this.clientId = clientId; + this.clientSecret = clientSecret; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getGrantType() { + return grantType; + } + + public void setGrantType(String grantType) { + this.grantType = grantType; + } + + public String getClientId() { + return clientId; + } + + public void setClientId(String clientId) { + this.clientId = clientId; + } + + public String getClientSecret() { + return clientSecret; + } + + public void setClientSecret(String clientSecret) { + this.clientSecret = clientSecret; + } + + public String getCaptcha() { + return captcha; + } + + public void setCaptcha(String captcha) { + this.captcha = captcha; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/LoginUser.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/LoginUser.java new file mode 100644 index 00000000..c8895be6 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/LoginUser.java @@ -0,0 +1,186 @@ +package com.dstz.auth.model; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.SystemClock; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IUser; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.springframework.security.core.GrantedAuthority; +import org.springframework.security.core.userdetails.UserDetails; +import org.springframework.util.function.SingletonSupplier; + +import java.beans.PropertyDescriptor; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.Objects; + +/** + * 登录用户 + * + * 如需获取AbUser 更多属性 请参考 getPhoto 方法,代理获取 + * + * @author lightning + */ +public class LoginUser implements UserDetails { + + private static final long serialVersionUID = 4962121675615445357L; + + private String username; + + private String userId; + + private String fullName; + + private transient volatile IUser delegateUser; + + private Boolean superAdmin; + + private Collection authorities; + + public void setDelegateUser(IUser delegateUser) { + this.delegateUser = delegateUser; + this.username = delegateUser.getUsername(); + this.userId = delegateUser.getUserId(); + this.fullName = delegateUser.getFullName(); + } + + public void setUsername(String username) { + this.username = username; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + @Override + public Collection getAuthorities() { + return ObjectUtil.defaultIfNull(authorities, Collections.emptyList()); + } + + public void setAuthorities(Collection authorities) { + this.authorities = authorities; + } + + @Override + public String getUsername() { + return username; + } + + @Override + public boolean isAccountNonExpired() { + Date expireDate = delegateUser().getAttrValue("expireDate", Date.class); + return expireDate == null || SystemClock.now() <= expireDate.getTime(); + } + + @Override + public boolean isAccountNonLocked() { + return true; + } + + @Override + public boolean isCredentialsNonExpired() { + return true; + } + + @JsonIgnore + @Override + public String getPassword() { + return delegateUser().getAttrValue("password", String.class); + } + + @Override + public boolean isEnabled() { + Integer status = delegateUser().getAttrValue("status", Integer.class); + // 1:启用 + return NumberPool.INTEGER_ONE.equals(status); + } + + /** + * 获取用户来源 + * + * @return 用户来源 + */ + public String getFrom() { + return delegateUser().getAttrValue("from", String.class); + } + + public Boolean getSuperAdmin() { + return superAdmin; + } + + public void setSuperAdmin(Boolean superAdmin) { + this.superAdmin = superAdmin; + } + + /** + * 拿到代理用户 + * + * @return 代理用户 + */ + public IUser delegateUser() { + if (Objects.isNull(delegateUser)) { + synchronized (this) { + if (Objects.isNull(delegateUser)) { + this.delegateUser = new ProxyUser(this.username, this.userId, this.fullName); + } + } + } + return delegateUser; + } + + private static final class ProxyUser implements IUser { + + private static final long serialVersionUID = -6505859170491860133L; + + private final String username; + + private final String userId; + + private final String fullName; + + private final SingletonSupplier attributes; + + public ProxyUser(String username, String userId, String fullName) { + this.username = username; + this.userId = userId; + this.fullName = fullName; + this.attributes = SingletonSupplier.of(() -> SpringUtil.getBean(UserApi.class).getByUsername(username)); + } + + @Override + public String getUsername() { + return username; + } + + @Override + public String getUserId() { + return userId; + } + + + @Override + public String getFullName() { + return fullName; + } + + @Override + public T getAttrValue(String attrName, Class tClass) { + PropertyDescriptor propertyDescriptor = BeanUtil.getPropertyDescriptor(getClass(), attrName); + if (propertyDescriptor != null) { + return Convert.convert(tClass, ReflectUtil.invoke(this, propertyDescriptor.getReadMethod())); + } + IUser attributes = this.attributes.get(); + return attributes == null ? null : attributes.getAttrValue(attrName, tClass); + } + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxDepartmentDTO.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxDepartmentDTO.java new file mode 100644 index 00000000..b2d06164 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxDepartmentDTO.java @@ -0,0 +1,58 @@ +package com.dstz.auth.model.dto; + +/** + *
+ * 描述:企业微信部门
+ * 
+ * + * @author lightning + */ +public class QyWxDepartmentDTO { + + + private int id; //创建的部门id + private String name; //部门名称 + private String name_en; //英文名称 + private int parentid; //父部门id + private int order; //在父部门中的次序值。order值大的排序靠前。值范围是[0, 2^32) + + public int getId() { + return id; + } + + public void setId(int id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getName_en() { + return name_en; + } + + public void setName_en(String name_en) { + this.name_en = name_en; + } + + public int getParentid() { + return parentid; + } + + public void setParentid(int parentid) { + this.parentid = parentid; + } + + public int getOrder() { + return order; + } + + public void setOrder(int order) { + this.order = order; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxResponse.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxResponse.java new file mode 100644 index 00000000..46dcc320 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxResponse.java @@ -0,0 +1,40 @@ +package com.dstz.auth.model.dto; + +public class QyWxResponse { + private String errcode; + private String errmsg; + private String msgid; + private String access_token; + + public String getAccess_token() { + return access_token; + } + + public void setAccess_token(String access_token) { + this.access_token = access_token; + } + + public String getErrcode() { + return errcode; + } + + public void setErrcode(String errcode) { + this.errcode = errcode; + } + + public String getErrmsg() { + return errmsg; + } + + public void setErrmsg(String errmsg) { + this.errmsg = errmsg; + } + + public String getMsgid() { + return msgid; + } + + public void setMsgid(String msgid) { + this.msgid = msgid; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxUserDTO.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxUserDTO.java new file mode 100644 index 00000000..91a887d1 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/QyWxUserDTO.java @@ -0,0 +1,204 @@ +package com.dstz.auth.model.dto; + +import java.util.List; + +/** + *
+ * 描述:企业微信成员详情
+ * 
+ * + * @author lightning + */ +public class QyWxUserDTO { + + private String userid; //成员UserID。对应管理端的帐号 + private String name; //成员名称;第三方不可获取,调用时返回userid以代替name;对于非第三方创建的成员,第三方通讯录应用也不可获取;第三方页面需要通过通讯录展示组件来展示名字 + private String[] department; //成员所属部门id列表,仅返回该应用有查看权限的部门id + private String[] order; //部门内的排序值,默认为0。数量必须和department一致,数值越大排序越前面。值范围是[0, 2^32) + private String position; //职务信息;第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private String mobile; //手机号码 + private String gender; //性别。0表示未定义,1表示男性,2表示女性 + private String email; //邮箱,第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private List is_leader_in_dept; //表示在所在的部门内是否为上级。0-否;1-是。是一个列表,数量必须与department一致。第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private String avatar; //头像url。 第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private String thumb_avatar; //头像缩略图url。第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private String telephone; //座机。 + private String alias; //别名; + private int status; //激活状态: 1=已激活,2=已禁用,4=未激活,5=退出企业。 + private String address; //地址。 + private int hide_mobile; + private String english_name; + private String open_userid; //全局唯一。对于同一个服务商,不同应用获取到企业内同一个成员的open_userid是相同的,最多64个字节。仅第三方应用可获取 + private int main_department; //主部门 + + private String qr_code; //员工个人二维码,扫描可添加为外部联系人(注意返回的是一个url,可在浏览器上打开该url以展示二维码);第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + private String external_position; //对外职务,如果设置了该值,则以此作为对外展示的职务,否则以position来展示。第三方仅通讯录应用可获取;对于非第三方创建的成员,第三方通讯录应用也不可获取 + + public String getUserid() { + return userid; + } + + public void setUserid(String userid) { + this.userid = userid; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String[] getDepartment() { + return department; + } + + public void setDepartment(String[] department) { + this.department = department; + } + + public String[] getOrder() { + return order; + } + + public void setOrder(String[] order) { + this.order = order; + } + + public String getPosition() { + return position; + } + + public void setPosition(String position) { + this.position = position; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getGender() { + return gender; + } + + public void setGender(String gender) { + this.gender = gender; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public List getIs_leader_in_dept() { + return is_leader_in_dept; + } + + public void setIs_leader_in_dept(List is_leader_in_dept) { + this.is_leader_in_dept = is_leader_in_dept; + } + + public String getAvatar() { + return avatar; + } + + public void setAvatar(String avatar) { + this.avatar = avatar; + } + + public String getThumb_avatar() { + return thumb_avatar; + } + + public void setThumb_avatar(String thumb_avatar) { + this.thumb_avatar = thumb_avatar; + } + + public String getTelephone() { + return telephone; + } + + public void setTelephone(String telephone) { + this.telephone = telephone; + } + + public String getAlias() { + return alias; + } + + public void setAlias(String alias) { + this.alias = alias; + } + + public int getStatus() { + return status; + } + + public void setStatus(int status) { + this.status = status; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public int getHide_mobile() { + return hide_mobile; + } + + public void setHide_mobile(int hide_mobile) { + this.hide_mobile = hide_mobile; + } + + public String getEnglish_name() { + return english_name; + } + + public void setEnglish_name(String english_name) { + this.english_name = english_name; + } + + public String getOpen_userid() { + return open_userid; + } + + public void setOpen_userid(String open_userid) { + this.open_userid = open_userid; + } + + public int getMain_department() { + return main_department; + } + + public void setMain_department(int main_department) { + this.main_department = main_department; + } + + public String getQr_code() { + return qr_code; + } + + public void setQr_code(String qr_code) { + this.qr_code = qr_code; + } + + public String getExternal_position() { + return external_position; + } + + public void setExternal_position(String external_position) { + this.external_position = external_position; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxMsgDTO.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxMsgDTO.java new file mode 100644 index 00000000..ec964630 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxMsgDTO.java @@ -0,0 +1,71 @@ +package com.dstz.auth.model.dto; +/** + * 微信消息体 + * + * @author lightning + * + */ +public class WxMsgDTO { + // OPENID + private String touser ; + //更多消息类型请参考微信官方文档,这里默认只发文本消息 https://developers.weixin.qq.com/doc/offiaccount/Message_Management/Service_Center_messages.html + private String msgtype = "textcard"; + private WxTextContext wxTextContext; + private WxTextCardContext textcard; + private Integer agentid = null; + + public WxMsgDTO() { + + } + + public WxMsgDTO(String openid,String content) { + WxTextContext wxContent = new WxTextContext(content); + this.wxTextContext = wxContent; + this.touser = openid; + } + + public WxMsgDTO(String openid,String title,String description ,String url) { + WxTextCardContext wxContent = new WxTextCardContext(title,description,url); + this.textcard = wxContent; + this.touser = openid; + } + + + public String getTouser() { + return touser; + } + public void setTouser(String touser) { + this.touser = touser; + } + public String getMsgtype() { + return msgtype; + } + public void setMsgtype(String msgtype) { + this.msgtype = msgtype; + } + + public WxTextContext getWxTextContext() { + return wxTextContext; + } + + public void setWxTextContext(WxTextContext wxTextContext) { + this.wxTextContext = wxTextContext; + } + + public WxTextCardContext getTextcard() { + return textcard; + } + + public void setTextcard(WxTextCardContext textcard) { + this.textcard = textcard; + } + + public Integer getAgentid() { + return agentid; + } + + public void setAgentid(Integer agentid) { + this.agentid = agentid; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextCardContext.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextCardContext.java new file mode 100644 index 00000000..3186f65c --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextCardContext.java @@ -0,0 +1,44 @@ +package com.dstz.auth.model.dto; +/** + * 微信卡片消息dto + * + * @author lightning + */ +public class WxTextCardContext { + private String title; + private String description; + private String url; + + public WxTextCardContext() { + } + + public WxTextCardContext(String title, String description, String url) { + this.title = title; + this.description = description; + this.url = url; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getDescription() { + return description; + } + + public void setDescription(String description) { + this.description = description; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextContext.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextContext.java new file mode 100644 index 00000000..5bdc259e --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/model/dto/WxTextContext.java @@ -0,0 +1,23 @@ +package com.dstz.auth.model.dto; +/** + * 微信消息体文本dto + * + * @author lightning + */ +public class WxTextContext { + + private String content; + + public WxTextContext(String content2) { + this.content = content2; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/AuthController.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/AuthController.java new file mode 100644 index 00000000..c4c2f542 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/AuthController.java @@ -0,0 +1,66 @@ +package com.dstz.auth.rest.controller; + +import com.dstz.auth.login.AbLoginService; +import com.dstz.auth.login.AbTokenService; +import com.dstz.auth.login.model.RefreshTokenReqVO; +import com.dstz.auth.login.model.TokenReqVO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +import static com.dstz.auth.authentication.api.constant.AuthApiConstant.OAUTH_TOKEN_PASSWORD; + +/** + * 获取token + * + * @author lightning + * @since 2023-01-30 + */ +@RestController +@RequestMapping(AbAppRestConstant.AUTH_SERVICE_PREFIX) +public class AuthController { + + @Autowired + AbTokenService abTokenService; + + @Autowired + AbLoginService loginService; + + /** + * 获取token接口 + * + * @param tokenReqVO + * @return + */ + @RequestMapping("/getToken") + public ApiResponse getToken(@Valid @RequestBody TokenReqVO tokenReqVO) { + //获取token + tokenReqVO.setGrantType(OAUTH_TOKEN_PASSWORD); + ApiResponse result = abTokenService.getToken(tokenReqVO); + //缓存数据 + loginService.loginPostposition(result); + return result; + } + + + /** + * 刷新token接口 + * + * @param refreshTokenReqVO + * @return + */ + @RequestMapping("/refreshToken") + public ApiResponse refreshToken(@Valid @RequestBody RefreshTokenReqVO refreshTokenReqVO) { + //刷新token + ApiResponse result = abTokenService.refreshToken(refreshTokenReqVO); + //缓存数据 + loginService.loginPostposition(result); + return result; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/LoginController.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/LoginController.java new file mode 100644 index 00000000..0382dfe7 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/rest/controller/LoginController.java @@ -0,0 +1,112 @@ +package com.dstz.auth.rest.controller; + + +import cn.hutool.captcha.CaptchaUtil; +import cn.hutool.captcha.GifCaptcha; +import cn.hutool.core.lang.Assert; +import cn.hutool.extra.servlet.ServletUtil; +import com.dstz.auth.login.AbLoginService; +import com.dstz.auth.login.AbTokenService; +import com.dstz.auth.login.model.TokenReqVO; +import com.dstz.auth.utils.LoginUtils; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.AbRequestUtils; +import com.dstz.org.api.UserApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import javax.validation.Valid; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import static com.dstz.auth.authentication.api.constant.AuthStatusCode.PARAM_CLIENT_ID_IS_NOT_FOUND; + +/** + * 登录控制器 + * + * @author lightning + * @since 2022-02-07 + * @owner 深圳市大世同舟信息科技有限公司 + */ +@RestController +@RequestMapping(AbAppRestConstant.AUTH_SERVICE_PREFIX) +public class LoginController { + + + @Autowired + UserApi userApi; + + @Autowired + ICache cache; + + @Autowired + AbTokenService abTokenService; + + @Autowired + AbLoginService loginService; + + /** + * 登录接口 + * + * @param tokenReqVO + * @return + */ + @RequestMapping("/login") + public ApiResponse login(@Valid @RequestBody TokenReqVO tokenReqVO, HttpServletResponse response) { + //登录前验证信息 + loginService.authLoginParam(tokenReqVO); + //获取token + ApiResponse result = abTokenService.getToken(tokenReqVO); + //缓存数据 + loginService.loginPostposition(result); + return result; + } + + /** + * 退出登录 + * + * @return + */ + @RequestMapping("/logout") + public ApiResponse logout() { + abTokenService.removeCurrentAccessToken(); + return ApiResponse.success(); + } + + @RequestMapping("/login/getCode") + public void getCode() throws IOException { + String sessionId = AbRequestUtils.getHttpServletRequest().getSession().getId(); + //定义图形验证码的长、宽、验证码字符数、干扰线宽度 + GifCaptcha gifCaptcha = CaptchaUtil.createGifCaptcha(120, 40, 4); + //获取随机生成的内容 + gifCaptcha.createCode(); + String code = gifCaptcha.getCode(); + cache.put(AbCacheRegionConstant.LOGIN_CACHE_REGION, sessionId, code); + //输出流 + ServletOutputStream os = AbRequestUtils.getHttpServletResponse().getOutputStream(); + gifCaptcha.write(os); + os.flush(); + os.close(); + } + + @RequestMapping("/login/queryCaptchaSwitch") + public ApiResponse> queryCaptchaSwitch() { + List booleanList = new ArrayList<>(); + booleanList.add(0, LoginUtils.queryCaptchaSwitch()); + booleanList.add(1, LoginUtils.queryGetBackPwdSwitch()); + return ApiResponse.success(booleanList); + } + + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/IngoreChecker.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/IngoreChecker.java new file mode 100644 index 00000000..9714c011 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/IngoreChecker.java @@ -0,0 +1,37 @@ +package com.dstz.auth.utils; + +import com.dstz.base.common.constats.StrPool; +import org.springframework.util.PatternMatchUtils; + +import javax.servlet.http.HttpServletRequest; + +/** + * 忽略鉴权地址检查 + * + * @author lightning + * @since 2022-02-07 + */ +public class IngoreChecker { + + /** + * 忽略地址 + */ + private String[] ignoreUrls; + + public void setIgnoreUrls(String[] ignoreUrls) { + this.ignoreUrls = ignoreUrls; + } + + /** + * 判断当前URL是否在忽略的地址中。 + * + * @param request 请求 + * @return + */ + public boolean isIngores(HttpServletRequest request) { + String servletPath = request.getServletPath(); + // 会再跳转 index.html 所以直接忽略 + return StrPool.SLASH.equals(servletPath) || PatternMatchUtils.simpleMatch(ignoreUrls, servletPath); + } + +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/LoginUtils.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/LoginUtils.java new file mode 100644 index 00000000..91015c8b --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/LoginUtils.java @@ -0,0 +1,106 @@ + +package com.dstz.auth.utils; + + +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.property.PropertyEnum; + +import java.util.HashMap; +import java.util.Map; + +import static com.dstz.auth.authentication.api.constant.AuthCacheKeyConstant.USER_LOGIN_COUNT; + +/** + * 登录工具类 + * + * @author lightning + * @since 2022-02-07 + */ +public class LoginUtils { + + /** + * 登录失败:验证失败次数 存储缓存 + * 失败次数<=系统配置 更新缓存 删除旧的缓存 添加新的缓存 + */ + + + public static String chckLoginFailedTimes(String userName, String ip) { + ICache cache = SpringUtil.getBean(ICache.class); + String loginKey = String.format("%s%s", ip, userName); + int maxCount = PropertyEnum.LOGIN_COUNT.getPropertyValue(int.class); + String lockTimeDesc = PropertyEnum.LOGIN_FILED_LOCK_TIME_DESC.getPropertyValue(String.class); + + //默认不开启登录次数验证,如果配置次数为0,则不验证密码尝试次数 + if (maxCount == 0) { + return StrPool.EMPTY; + } + + Map map = getArgMap(userName, ip, userName); + Integer failedCount = getFailedCount(userName, ip, userName); + + if (failedCount < maxCount) { + map.put(loginKey, String.valueOf(failedCount + 1)); + cache.put(AbCacheRegionConstant.LOGIN_CACHE_REGION, USER_LOGIN_COUNT + userName, map); + } else { + throw new BusinessMessage(AuthStatusCode.DISABLE_LOGIN.formatDefaultMessage(lockTimeDesc)); + } + return String.format("账户密码错误,已经失败 %d 次,若连续失败 %d 次将禁止登录%s", failedCount + 1, maxCount, lockTimeDesc); + } + + + public static Integer getFailedCount(String userName, String ip, String name) { + String loginKey = String.format("%s%s", ip, name); + int count = 0; + Map map = getArgMap(userName, ip, name); + if (!map.isEmpty()) { + for (String key : map.keySet()) { + if (key.equals(loginKey)) { + String value = String.valueOf(map.get(key)); + int i = Integer.parseInt(value); + count = i; + } + } + } + return count; + } + + + + + + + /*查询缓存中是否存在当前用户的数据*/ + + public static Map getArgMap(String userName, String ip, String name) { + ICache cache = SpringUtil.getBean(ICache.class); + Map mapObj = new HashMap(); + if (StrUtil.isNotEmpty(ip) || StrUtil.isNotEmpty(name)) { + if (cache.exists(AbCacheRegionConstant.LOGIN_CACHE_REGION, USER_LOGIN_COUNT + userName)) { + //获取失败次数 + mapObj = (Map) cache.getIfPresent(AbCacheRegionConstant.LOGIN_CACHE_REGION, USER_LOGIN_COUNT + userName); + } + } + return mapObj; + } + + public static Boolean queryCaptchaSwitch() { + return PropertyEnum.LOGIN_CAPTCHA_KEY.getPropertyValue(Boolean.class); + } + + public static Boolean queryGetBackPwdSwitch() { + return PropertyEnum.IS_OPEN_RESET_PWD_BY_EMAIL.getPropertyValue(Boolean.class); + } + + + public static Boolean queryIsResetPwd() { + return PropertyEnum.LOGIN_RESET_PWD.getPropertyValue(Boolean.class); + } + +} + diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/RefererCsrfChecker.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/RefererCsrfChecker.java new file mode 100644 index 00000000..2bdd1e34 --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/RefererCsrfChecker.java @@ -0,0 +1,51 @@ +package com.dstz.auth.utils; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; + +import java.util.ArrayList; +import java.util.List; +import java.util.regex.Matcher; +import java.util.regex.Pattern; +/** + * 跨域地址检查 + * + * @author lightning + * @since 2022-02-07 + */ +public class RefererCsrfChecker { + private List ingores = new ArrayList(); + + public void setIngores(List urls) { + if (CollectionUtil.isEmpty(urls)){ return;} + for (String url : urls) { + // 防止配置空的错误配置 + if(StrUtil.isEmpty(url)){continue;} + + Pattern regex = Pattern.compile(url, Pattern.CASE_INSENSITIVE | Pattern.UNICODE_CASE | + Pattern.DOTALL | Pattern.MULTILINE); + ingores.add(regex); + } + } + + + /** + * 判断当前URL是否在忽略的地址中。 + * + * @param requestUrl + * @return + */ + public boolean isIngores(String requestUrl) { + // 会再跳转 index.html 所以直接忽略 + if(StrPool.SLASH.equals(requestUrl)){return true;} + + for (Pattern pattern : ingores) { + Matcher regexMatcher = pattern.matcher(requestUrl); + if (regexMatcher.find()) { + return true; + } + } + return false; + } +} diff --git a/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/VerifyCodeUtil.java b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/VerifyCodeUtil.java new file mode 100644 index 00000000..e62c02ac --- /dev/null +++ b/ab-auth/ab-auth-spring-security-oauth2/src/main/java/com/dstz/auth/utils/VerifyCodeUtil.java @@ -0,0 +1,192 @@ +package com.dstz.auth.utils; + +import cn.hutool.core.util.StrUtil; + +import javax.imageio.ImageIO; +import java.awt.*; +import java.awt.image.BufferedImage; +import java.io.IOException; +import java.io.OutputStream; +import java.util.Random; +import java.util.concurrent.ThreadLocalRandom; + +/** + * 验证码工具类 + * + * @author lightning + * @since 2022-02-07 + */ +public final class VerifyCodeUtil { + /*定义图片的width*/ + private static int WIDTH = 80; + /*定义图片的height*/ + private static int HEIGHT = 30; + /*定义图片上显示的验证码个数*/ + private static int CODECOUNT = 4; + /*字符间隔*/ + private static final int CHARACETRSPACING = 8; + /*字体大小*/ + private static final int FONTHEIGHT = 24; + /*干扰线*/ + private static final int LINENUMBER = 10; + /*字符垂直位置*/ + private static final int VERTICALPOSITION = 24; + private static final char[] CODESEQUENCE = {'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','1','2','3','4','5','6','7','8','9','0'}; + /*无参构造器*/ + private VerifyCodeUtil(){} + + /** + * 生成验证码字符串 + * @param count 验证码字符个数 + * @return 返回验证码字符串 + */ + public static String generateVerifyCode(int count){ + //产生随机数 + Random random = ThreadLocalRandom.current(); + //生成验证码 + StringBuilder randomCode = new StringBuilder();/*StringBuilder可变字符串序列*/ + for (int i = 0; i < count; i++) { + //获取一位验证码 + String code = String.valueOf(CODESEQUENCE[random.nextInt(CODESEQUENCE.length)]); + randomCode.append(code);/*字符串拼接*/ + } + return randomCode.toString(); + } + + /** + * 生成验证码字符串 + * @return 返回验证码字符串 + */ + public static String generateVerifyCode(){//方法的重载 + return generateVerifyCode(CODECOUNT);//静态方法中只能调用静态方法 + } + + /** + * 生成验证码图片 + * @param width 验证码图片的宽度,默认95 + * @param height 验证码图片的高度,默认30 + * @param code 验证码字符串 + * @return 返回创建好的画布对象 + */ + public static BufferedImage outputImage(Integer width, Integer height, String code){ + if(!StrUtil.isEmpty(String.valueOf(width))){//Spring工具包中的方法 + WIDTH = width;//为图片设置宽度 + } + if(!StrUtil.isEmpty(String.valueOf(height))){ + HEIGHT = height;//为图片设置高度 + } + if(StrUtil.isEmpty(code)){ + return null;//做这些判断都是为了防止出现空指针异常 + } + //创建画布对象(不带透明色) + BufferedImage buffImg = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + //创建画笔对象 + Graphics gd = buffImg.getGraphics(); + //创建生成随机数对象 + Random random = ThreadLocalRandom.current(); + //设置画布背景色(白色) + gd.setColor(Color.WHITE); + //根据设置的背景色填充画布 + gd.fillRect(0, 0, WIDTH, HEIGHT); + //创建字体对象 + Font font = new Font("Arial", Font.BOLD, FONTHEIGHT); + //给画笔对象设置字体 + gd.setFont(font); + //给画笔对象设置颜色 + gd.setColor(Color.BLACK); + //给画布绘制边框 + gd.drawRect(0, 0, WIDTH - 1, HEIGHT - 1); + //生成验证码 + char[] codes = code.toCharArray(); + for (int i = 0; i < codes.length; i++) { + //设置颜色 + gd.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255))); + //绘制验证码 + gd.drawString(String.valueOf(codes[i]), CHARACETRSPACING + i * 2 * CHARACETRSPACING, VERTICALPOSITION); + } + //设置干扰线颜色(深灰色) + gd.setColor(Color.DARK_GRAY); + //干扰线 + for (int i = 0; i < LINENUMBER; i++) { + //线的起始X坐标 + int startX = random.nextInt(WIDTH); + //线的起始Y坐标 + int startY = random.nextInt(HEIGHT); + //线的结束X坐标 + int endX = random.nextInt(VERTICALPOSITION); + //线的结束Y坐标 + int endY = random.nextInt(VERTICALPOSITION); + //绘制直线 + gd.drawLine(startX, startY, startX + endX, startY + endY); + } + return buffImg; + } + /** + * 输出验证码图片流 + * @param w 宽度 + * @param h 高度 + * @param code 验证码字符串 + * @param os 输出流 + */ + public static void outputImage(Integer w, Integer h, String code, OutputStream os) throws IOException { + if(StrUtil.isEmpty(code)){ + return; + } + // 创建画布对象 + BufferedImage bi = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB); + // 创建画笔对象 + Graphics gd = bi.getGraphics(); + // 创建生成随机数对象 + Random random = ThreadLocalRandom.current(); + // 设置画布背景色 + gd.setColor(Color.DARK_GRAY); + // 根据设置的背景色填充画布 + gd.fillRect(0, 0, WIDTH, HEIGHT); + // 创建字体对象 + Font font = new Font("Arial", Font.BOLD, FONTHEIGHT); + // 给画笔对象设置字体 + gd.setFont(font); + // 给画笔设置颜色 + gd.setColor(Color.BLACK); + // 给画布绘制边框 + //gd.drawRect(0, 0, WIDTH - 1, HEIGHT - 1); + // 设置干扰线颜色 + gd.setColor(Color.BLACK); + // 干扰线 + for (int i = 0; i < LINENUMBER; i++) { + // 线的起始X坐标 + int startX = random.nextInt(WIDTH); + // 线的起始Y坐标 + int startY = random.nextInt(HEIGHT); + // 线的结束X坐标 + int endX = random.nextInt(VERTICALPOSITION); + // 线的结束Y坐标 + int endY = random.nextInt(VERTICALPOSITION); + // 绘制直线 + gd.drawLine(startX, startY, startX + endX, startY + endY); + } + // 生成验证码 + char[] codes = code.toCharArray(); + for (int i = 0; i < codes.length; i++) { + // 设置颜色 + gd.setColor(new Color(random.nextInt(255), random.nextInt(255), random.nextInt(255))); + // 绘制验证码 + gd.drawString(String.valueOf(codes[i]), CHARACETRSPACING + i * 2 * CHARACETRSPACING, VERTICALPOSITION); + } + gd.dispose(); + ImageIO.write(bi, "png", os); + } + /** + * 输出验证码图片流 + * @param code 验证码 + * @param os 创建的输出流 + */ + public static void outputImage(String code, OutputStream os) throws IOException { + outputImage(WIDTH, HEIGHT, code, os); + } + + + +} diff --git a/ab-auth/pom.xml b/ab-auth/pom.xml new file mode 100644 index 00000000..da008c4b --- /dev/null +++ b/ab-auth/pom.xml @@ -0,0 +1,23 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-auth + pom + + ab-auth-api + ab-auth-core + ab-auth-spring-security-oauth2 + + + + 5.2.2.RELEASE + + \ No newline at end of file diff --git a/ab-base/ab-base-api/pom.xml b/ab-base/ab-base-api/pom.xml new file mode 100644 index 00000000..a53fd1cf --- /dev/null +++ b/ab-base/ab-base-api/pom.xml @@ -0,0 +1,19 @@ + + + 4.0.0 + + com.dstz + ab-base + 2.5.0 + + ab-base-api + + + + jakarta.validation + jakarta.validation-api + + + \ No newline at end of file diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdBatchDTO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdBatchDTO.java new file mode 100644 index 00000000..321636ae --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdBatchDTO.java @@ -0,0 +1,38 @@ +package com.dstz.base.api.dto; + +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; +import java.util.List; + +/** + * 批量数据传输对象 + * + * @author wacxhs + * @since 2022-01-24 + */ +public class IdBatchDTO implements java.io.Serializable { + + + private static final long serialVersionUID = 2403594070347388576L; + + /** + * 批量ID + */ + @NotEmpty(message = "批量ID不能为空") + private List ids; + + public List getIds() { + return ids; + } + + public void setIds(List ids) { + this.ids = ids; + } + + @Override + public String toString() { + return "IdBatchDTO{" + + "ids=" + ids + + '}'; + } +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdDTO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdDTO.java new file mode 100644 index 00000000..6c7aac19 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/IdDTO.java @@ -0,0 +1,37 @@ +package com.dstz.base.api.dto; + +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.Size; + +/** + * ID 数据传输对象 + * + * @author wacxhs + */ +public class IdDTO implements java.io.Serializable { + + + private static final long serialVersionUID = -1631823931282821679L; + + /** + * id + */ + @NotEmpty(message = "id不能为空") + @Size(max = 64, message = "id过长") + private String id; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + @Override + public String toString() { + return "IdDTO{" + + "id='" + id + '\'' + + '}'; + } +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/PageListDTO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/PageListDTO.java new file mode 100644 index 00000000..a51ff3c4 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/PageListDTO.java @@ -0,0 +1,189 @@ +package com.dstz.base.api.dto; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; +/** + *
+ * 分页响应定义
+ * 作者:jeff
+ * 邮箱:jeff@agilebpm.cn
+ * 日期:2022-02-02
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class PageListDTO implements List{ + /** + * 分页大小 + */ + private long pageSize = 0; + /** + * 当前页 + */ + private long page = 1; + /** + * 总条数 + */ + private long total = 0L; + /** + * 分页列表数据 + */ + private List rows = null; + + public PageListDTO() { + } + + public PageListDTO(long pageSize, long page, long total, List rows) { + super(); + this.pageSize = pageSize; + this.page = page; + this.total = total; + this.rows = rows; + if(rows == null) { + this.rows = Collections.emptyList(); + } + } + + public long getPageSize() { + return pageSize; + } + public void setPageSize(long pageSize) { + this.pageSize = pageSize; + } + public long getPage() { + return page; + } + public void setPage(long page) { + this.page = page; + } + public long getTotal() { + return total; + } + public void setTotal(long total) { + this.total = total; + } + public List getRows() { + return rows; + } + public void setRows(List rows) { + this.rows = rows; + } + + public int size() { + return rows.size(); + } + + public boolean isEmpty() { + return rows.isEmpty(); + } + + + public boolean contains(Object o) { + return rows.contains(o); + } + + + public Iterator iterator() { + return rows.iterator(); + } + + + public Object[] toArray() { + return rows.toArray(); + } + + + public T[] toArray(T[] a) { + return rows.toArray( a); + } + + + public boolean add(T e) { + return rows.add(e); + } + + + public boolean remove(Object o) { + return rows.remove(o); + } + + + public boolean containsAll(Collection c) { + return rows.containsAll(c); + } + + + public boolean addAll(Collection c) { + return rows.addAll(c); + } + + + public boolean addAll(int index, Collection c) { + return rows.addAll(index, c); + } + + + public boolean removeAll(Collection c) { + return rows.removeAll(c); + } + + + public boolean retainAll(Collection c) { + return rows.retainAll(c); + } + + + public void clear() { + rows.clear(); + } + + + public T get(int index) { + return rows.get(index); + } + + + public T set(int index, T element) { + return rows.set(index, element); + } + + + public void add(int index, T element) { + rows.add(index,element); + } + + + public T remove(int index) { + return rows.remove(index); + } + + + public int indexOf(Object o) { + return rows.indexOf(o); + } + + + public int lastIndexOf(Object o) { + return rows.lastIndexOf(o); + } + + + public ListIterator listIterator() { + return rows.listIterator(); + } + + + public ListIterator listIterator(int index) { + return rows.listIterator(index); + } + + + public List subList(int fromIndex, int toIndex) { + return rows.subList(fromIndex, toIndex); + } + + + +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/QueryParamDTO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/QueryParamDTO.java new file mode 100644 index 00000000..fe2ef438 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/QueryParamDTO.java @@ -0,0 +1,157 @@ +package com.dstz.base.api.dto; + +import javax.validation.constraints.Max; +import javax.validation.constraints.Min; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +/** + * 查询条件的入参 + * @author Jeff + * + */ +public class QueryParamDTO { + + /** + * 每页显示条数,默认 10 + */ + @Max(1000)@Min(1) + protected long pageSize = 10; + + /** + * 当前页 + */ + protected long currentPage = 1; + + /** + * 排序字段 + */ + private String sortColumn; + + /** + * 排序 DESC ASC + */ + private String sortOrder; + + /** + * 是否查询CountSQL + */ + private Boolean searchCount = true; + + /** + * 是否启用分页 + */ + private Boolean enablePage = true; + + /** + * bootrap 默认用的类型 + */ + private Integer offset; + + @Max(1000)@Min(1) + private Integer limit; + + /** + * 列字段 + */ + private Set columnNames; + + private Map queryParam = new HashMap<>(); + + public Map getQueryParam() { + return queryParam; + } + + public void setQueryParam(Map queryParam) { + this.queryParam = queryParam; + } + + public String getSortColumn() { + return sortColumn; + } + + public void setSortColumn(String sortColumn) { + this.sortColumn = sortColumn; + } + + public String getSortOrder() { + return sortOrder; + } + + public void setSortOrder(String sortOrder) { + this.sortOrder = sortOrder; + } + + public long getPageSize() { + return pageSize; + } + + public void setPageSize(long pageSize) { + this.pageSize = pageSize; + } + + public long getCurrentPage() { + return currentPage; + } + + public void setCurrentPage(long currentPage) { + this.currentPage = currentPage; + } + + public Integer getOffset() { + return offset; + } + + public void setOffset(Integer offset) { + this.offset = offset; + } + + public Integer getLimit() { + return limit; + } + + public void setLimit(Integer limit) { + this.limit = limit; + } + + public Boolean getSearchCount() { + return searchCount; + } + + public void setSearchCount(Boolean searchCount) { + this.searchCount = searchCount; + } + + public Set getColumnNames() { + return columnNames; + } + + public void setColumnNames(Set columnNames) { + this.columnNames = columnNames; + } + + public Boolean getEnablePage() { + return enablePage; + } + + public void setEnablePage(Boolean enablePage) { + this.enablePage = enablePage; + } + + @Override + public QueryParamDTO clone() { + QueryParamDTO queryParamDTO = new QueryParamDTO(); + queryParamDTO.setQueryParam(getQueryParam()); + queryParamDTO.setSortColumn(getSortColumn()); + queryParamDTO.setSortOrder(getSortOrder()); + queryParamDTO.setPageSize(getPageSize()); + queryParamDTO.setCurrentPage(getCurrentPage()); + queryParamDTO.setOffset(getOffset()); + queryParamDTO.setLimit(getLimit()); + queryParamDTO.setSearchCount(getSearchCount()); + queryParamDTO.setColumnNames(getColumnNames()); + queryParamDTO.setEnablePage(getEnablePage()); + return queryParamDTO; + } +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/UserDTO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/UserDTO.java new file mode 100644 index 00000000..6252f5e3 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/dto/UserDTO.java @@ -0,0 +1,59 @@ +package com.dstz.base.api.dto; + +/** + * 用户数据 + * + * @author wacxhs + */ +public class UserDTO implements java.io.Serializable { + + private static final long serialVersionUID = 687720125614093757L; + + /** + * 用户ID + */ + private String id; + + /** + * 用户名 + */ + private String username; + + /** + * 用户姓名 + */ + private String fullName; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + @Override + public String toString() { + return "UserDTO{" + + "id='" + id + '\'' + + ", username='" + username + '\'' + + ", fullName='" + fullName + '\'' + + '}'; + } +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/model/Tree.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/model/Tree.java new file mode 100644 index 00000000..2627fef6 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/model/Tree.java @@ -0,0 +1,45 @@ +package com.dstz.base.api.model; + +import java.util.List; + +/** + *
+ * 描述:树结构对象,用于将列表数据转换成树结构。
+ * 作者:aschs
+ * 邮箱:aschs@agilebpm.cn
+ * 日期:2020年3月13日 下午6:00:56
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ * + * @param 声明子数据的类型,那么在实现类中就能直接使用实现类作为children。eg:SysDataDict + */ +public interface Tree> { + + /** + * 主键ID + * + * @return + */ + String getId(); + + /** + * 父ID + * + * @return + */ + String getParentId(); + + /** + * 子对象。 + * + * @return + */ + List getChildren(); + + /** + * 设置子对象。 + * + * @param list + */ + void setChildren(List list); +} \ No newline at end of file diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/service/Executable.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/service/Executable.java new file mode 100644 index 00000000..06bf34e1 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/service/Executable.java @@ -0,0 +1,8 @@ +package com.dstz.base.api.service; + +@FunctionalInterface +public interface Executable { + + void execute(); +} + diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/ApiResponse.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/ApiResponse.java new file mode 100644 index 00000000..4a0b8ea9 --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/ApiResponse.java @@ -0,0 +1,202 @@ +package com.dstz.base.api.vo; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.service.Executable; + +/** + *
+ * 全局接口响应定义
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2022-01-22
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class ApiResponse { + + /** + * 状态码-成功 + */ + public static final String CODE_SUCCESS = "Success"; + + /** + * 状态吗-成功-消息 + */ + public static final String CODE_SUCCESS_MESSAGE = "操作成功"; + + /** + * 是否成功 + */ + private Boolean isOk; + + /** + * 状态码 + */ + private String code; + + /** + * 信息 + */ + private String message; + + /** + * 响应体 + */ + private T data; + + public Boolean getIsOk() { + return isOk; + } + + public void setIsOk(Boolean ok) { + isOk = ok; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getMsg() { + return message; + } + + public void setMessage(String msg) { + this.message = msg; + } + + public String getMessage() { + return message; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + /** + * 使用是否成功 + * + * @param isOk 是否成功 + * @return 接口响应 + */ + public ApiResponse withIsOk(Boolean isOk) { + this.isOk = isOk; + return this; + } + + /** + * 使用状态码 + * + * @param code 状态码 + * @return 接口响应 + */ + public ApiResponse withCode(String code) { + this.code = code; + return this; + } + + /** + * 使用状态消息 + * + * @param msg 状态消息 + * @return 接口响应 + */ + public ApiResponse withMessage(String msg) { + this.message = msg; + return this; + } + + /** + * 使用数据 + * + * @param data 数据 + * @return 接口响应 + */ + public ApiResponse withData(T data) { + this.data = data; + return this; + } + + /** + * 成功响应 + * + * @param T + * @return 接口响应 + */ + public static ApiResponse success() { + return success((T) null); + } + + /** + * 成功响应 + * + * @param data 数据 + * @param T + * @return 接口响应 + */ + public static ApiResponse success(T data) { + return new ApiResponse().withIsOk(Boolean.TRUE).withCode(CODE_SUCCESS).withMessage(CODE_SUCCESS_MESSAGE).withData(data); + } + + public static ApiResponse> success(PageListDTO data) { + return new ApiResponse>() + .withIsOk(Boolean.TRUE) + .withCode(CODE_SUCCESS) + .withMessage(CODE_SUCCESS_MESSAGE) + .withData(new PageListVO(data)); + } + + /** + * 成功响应 (没有返回值) + * + * @param executable 执行者,不返回任何数据(例如:增删改) + * @return ApiResponse + */ + public static ApiResponse success(Executable executable) { + executable.execute(); + //因操作返回从英文换成中文,提升体验 + return ApiResponse.success().withMessage(CODE_SUCCESS_MESSAGE); + } + + /** + * 失败响应 + * + * @param code 响应码 + * @param message 响应信息 + * @param T + * @return 接口响应 + */ + public static ApiResponse fail(String code, String message) { + return fail(code, message, null); + } + + /** + * 失败响应 + * + * @param code 响应码 + * @param message 响应信息 + * @param body 内容体 + * @param T + * @return 接口响应 + */ + public static ApiResponse fail(String code, String message, T body) { + return new ApiResponse().withIsOk(Boolean.FALSE).withCode(code).withMessage(message).withData(body); + } + + @Override + public String toString() { + return "ApiResponse{" + + "isOk=" + isOk + + ", code='" + code + '\'' + + ", message='" + message + '\'' + + ", data=" + data + + '}'; + } +} diff --git a/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/PageListVO.java b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/PageListVO.java new file mode 100644 index 00000000..95b5dcde --- /dev/null +++ b/ab-base/ab-base-api/src/main/java/com/dstz/base/api/vo/PageListVO.java @@ -0,0 +1,73 @@ +package com.dstz.base.api.vo; + +import java.util.Collections; +import java.util.List; + +import com.dstz.base.api.dto.PageListDTO; + +/** + * 由于pageListDTO 为list 实现,导致他反序列化存在一些问题 + * @author Jeff + * + * @param + */ +public class PageListVO { + /** + * 分页大小 + */ + private long pageSize = 0; + /** + * 当前页 + */ + private long page = 1; + /** + * 总条数 + */ + private long total = 0L; + /** + * 分页列表数据 + */ + private List rows = null; + + public PageListVO() { + } + + public PageListVO(PageListDTO listDTO) { + super(); + this.pageSize = listDTO.getPageSize(); + this.page = listDTO.getPage(); + this.total = listDTO.getTotal(); + this.rows = listDTO.getRows(); + if(rows == null) { + this.rows = Collections.emptyList(); + } + } + + public long getPageSize() { + return pageSize; + } + public void setPageSize(long pageSize) { + this.pageSize = pageSize; + } + public long getPage() { + return page; + } + public void setPage(long page) { + this.page = page; + } + public long getTotal() { + return total; + } + + public void setTotal(long total) { + this.total = total; + } + + public List getRows() { + return rows; + } + public void setRows(List rows) { + this.rows = rows; + } + +} diff --git a/ab-base/ab-base-common/pom.xml b/ab-base/ab-base-common/pom.xml new file mode 100644 index 00000000..3e4cefac --- /dev/null +++ b/ab-base/ab-base-common/pom.xml @@ -0,0 +1,127 @@ + + + + ab-base + com.dstz + 2.5.0 + + 4.0.0 + + ab-base-common + + + + com.dstz + ab-base-api + + + com.dstz + ab-org-api + + + org.slf4j + slf4j-api + + + cn.hutool + hutool-core + + + cn.hutool + hutool-extra + + + org.apache.poi + poi-ooxml + + + cn.hutool + hutool-poi + + + org.springframework + spring-core + + + org.springframework + spring-context-support + + + com.fasterxml.jackson.core + jackson-databind + + + javax.xml.bind + jaxb-api + compile + + + com.google.guava + guava + + + junit + junit + test + + + io.jsonwebtoken + jjwt + 0.9.1 + + + cn.hutool + hutool-crypto + + + commons-codec + commons-codec + + + com.belerweb + pinyin4j + + + commons-io + commons-io + + + org.apache.commons + commons-lang3 + + + org.springframework.boot + spring-boot-starter-data-redis + provided + + + org.freemarker + freemarker + + + jakarta.servlet + jakarta.servlet-api + + + org.springframework + spring-web + provided + + + io.projectreactor + reactor-core + + + dom4j + dom4j + + + xml-apis + xml-apis + + + + + diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduled.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduled.java new file mode 100644 index 00000000..1da957a6 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduled.java @@ -0,0 +1,21 @@ +package com.dstz.base.common.aop.annotion; + +import java.lang.annotation.*; + +/** + * ab 调度 + * + * @author wacxhs + */ +@Retention(RetentionPolicy.RUNTIME) +@Documented +@Target(ElementType.METHOD) +public @interface AbScheduled { + + /** + * 调度任务KEY,也可用于其它中间件 + * + * @return 调度任务KEY + */ + String jobKey(); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduledMethodProcessor.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduledMethodProcessor.java new file mode 100644 index 00000000..163e865c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/aop/annotion/AbScheduledMethodProcessor.java @@ -0,0 +1,125 @@ +package com.dstz.base.common.aop.annotion; + +import cn.hutool.core.date.StopWatch; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.framework.autoproxy.AutoProxyUtils; +import org.springframework.aop.scope.ScopedObject; +import org.springframework.aop.scope.ScopedProxyUtils; +import org.springframework.beans.factory.BeanInitializationException; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.core.MethodIntrospector; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.util.CollectionUtils; + +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.concurrent.TimeUnit; + +/** + *
+ * 全局定时器拦截AOP处理,详见http://www.agilebpm.cn/guide/system-setting/crontab.html
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2021-11-13
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class AbScheduledMethodProcessor { + + private static final Logger logger = LoggerFactory.getLogger(AbScheduledMethodProcessor.class); + + + public static List process(ConfigurableListableBeanFactory beanFactory) { + StopWatch stopWatch = new StopWatch(); + stopWatch.start(); + logger.debug("开始解析@AbScheduled方法标记注解"); + List definitions = new ArrayList<>(); + String[] beanNames = beanFactory.getBeanNamesForType(Object.class); + for (String beanName : beanNames) { + if (!ScopedProxyUtils.isScopedTarget(beanName)) { + Class type = null; + try { + type = AutoProxyUtils.determineTargetClass(beanFactory, beanName); + } catch (Throwable ex) { + // An unresolvable bean type, probably from a lazy bean - let's ignore it. + if (logger.isDebugEnabled()) { + logger.debug("Could not resolve target class for bean with name '" + beanName + "'", ex); + } + } + if (type != null) { + if (ScopedObject.class.isAssignableFrom(type)) { + try { + Class targetClass = AutoProxyUtils.determineTargetClass( + beanFactory, ScopedProxyUtils.getTargetBeanName(beanName)); + if (targetClass != null) { + type = targetClass; + } + } catch (Throwable ex) { + // An invalid scoped proxy arrangement - let's ignore it. + if (logger.isDebugEnabled()) { + logger.debug("Could not resolve target bean for scoped proxy '" + beanName + "'", ex); + } + } + } + try { + processBean(definitions, beanName, type); + } catch (Throwable ex) { + throw new BeanInitializationException("Failed to process @EventListener " + + "annotation on bean with name '" + beanName + "'", ex); + } + } + } + } + + stopWatch.stop(); + logger.debug("解析@AbScheduled方法标记注解结束, {}", stopWatch.shortSummary(TimeUnit.MILLISECONDS)); + return definitions; + } + + private static void processBean(List definitions, String beanName, Class type) { + try { + Map annotatedMethods = MethodIntrospector.selectMethods(type, (MethodIntrospector.MetadataLookup) method -> AnnotatedElementUtils.findMergedAnnotation(method, AbScheduled.class)); + if (!CollectionUtils.isEmpty(annotatedMethods)) { + for (Map.Entry entry : annotatedMethods.entrySet()) { + AbScheduled abScheduled = entry.getValue(); + definitions.add(new AbScheduleDefinition(abScheduled.jobKey(), beanName, entry.getKey())); + } + } + } catch (Throwable ex) { + // An unresolvable type in a method signature, probably from a lazy bean - let's ignore it. + if (logger.isDebugEnabled()) { + logger.debug("Could not resolve methods for bean with name '" + beanName + "'", ex); + } + } + } + + public static final class AbScheduleDefinition { + + private final String jonKey; + + private final String beanName; + + private final Method method; + + public AbScheduleDefinition(String jonKey, String beanName, Method method) { + this.jonKey = jonKey; + this.beanName = beanName; + this.method = method; + } + + public String getJonKey() { + return jonKey; + } + + public String getBeanName() { + return beanName; + } + + public Method getMethod() { + return method; + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextCleanTaskDecorator.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextCleanTaskDecorator.java new file mode 100644 index 00000000..04a35221 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextCleanTaskDecorator.java @@ -0,0 +1,27 @@ +package com.dstz.base.common.async; + +import com.dstz.base.common.utils.ContextCleanUtils; +import org.springframework.core.task.TaskDecorator; + +/** + * 上下文清理装饰器 + * + * @author wacxhs + */ +public class ContextCleanTaskDecorator implements TaskDecorator { + + public static final ContextCleanTaskDecorator INSTANCE = new ContextCleanTaskDecorator(); + + @Override + public Runnable decorate(Runnable runnable) { + return () -> { + ContextCleanUtils.executeAll(); + try { + // 复制对象填充 + runnable.run(); + } finally { + ContextCleanUtils.executeAll(); + } + }; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextDuplicationTaskDecorator.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextDuplicationTaskDecorator.java new file mode 100644 index 00000000..bf3aa3d3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/async/ContextDuplicationTaskDecorator.java @@ -0,0 +1,23 @@ +package com.dstz.base.common.async; + +import com.dstz.base.common.utils.ContextDuplicationUtils; +import org.springframework.core.task.TaskDecorator; + +/** + * 上下文传递装饰器 + * + * @author wacxhs + */ +public class ContextDuplicationTaskDecorator implements TaskDecorator { + + public static final ContextDuplicationTaskDecorator INSTANCE = new ContextDuplicationTaskDecorator(); + + @Override + public Runnable decorate(Runnable target) { + final Object[] duplicateObjects = ContextDuplicationUtils.duplicateObjects(); + return () -> { + ContextDuplicationUtils.fillDuplicateObjects(duplicateObjects); + target.run(); + }; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCache.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCache.java new file mode 100644 index 00000000..ef5ba56f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCache.java @@ -0,0 +1,77 @@ +package com.dstz.base.common.cache; + +import org.springframework.cache.support.AbstractValueAdaptingCache; + +import java.util.Objects; +import java.util.concurrent.Callable; + +/** + *
+ * 系统缓存注解,支持Spring缓存注解,内部支持多种缓存切换,如redis,二级缓存j2cache 
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2021-11-13
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class AbSpringCache extends AbstractValueAdaptingCache { + + private final ICache cache; + + private final String region; + + public AbSpringCache(ICache cache, String region) { + super(true); + this.cache = cache; + this.region = region; + } + + private String serialString(Object key) { + return (String) key; + } + + @Override + protected Object lookup(Object key) { + return cache.getIfPresent(region, serialString(key)); + } + + @Override + public String getName() { + return region; + } + + @Override + public Object getNativeCache() { + return cache; + } + + @SuppressWarnings("unchecked") + @Override + public T get(Object key, Callable valueLoader) { + return (T) cache.get(region, serialString(key), (Callable) valueLoader); + } + + @Override + public void put(Object key, Object value) { + cache.put(region, serialString(key), value); + } + + @Override + public ValueWrapper putIfAbsent(Object key, Object value) { + Object storeValue = toStoreValue(value); + if (!Objects.isNull(lookup(key))) { + put(key, storeValue); + } + return toValueWrapper(storeValue); + } + + @Override + public void evict(Object key) { + cache.invalidate(region, serialString(key)); + } + + @Override + public void clear() { + cache.invalidateRegion(region); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCacheManager.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCacheManager.java new file mode 100644 index 00000000..50d3229a --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/AbSpringCacheManager.java @@ -0,0 +1,37 @@ +package com.dstz.base.common.cache; + +import cn.hutool.core.util.StrUtil; +import org.springframework.cache.Cache; +import org.springframework.cache.CacheManager; +import org.springframework.cache.transaction.TransactionAwareCacheDecorator; + +import java.util.Collection; +import java.util.concurrent.ConcurrentHashMap; + +/** + * ab spring cache manager + * + * @author wacxhs + */ +public class AbSpringCacheManager implements CacheManager { + + private final ConcurrentHashMap regionCacheMap = new ConcurrentHashMap<>(); + + private final ICache abCache; + + public AbSpringCacheManager(ICache abCache) { + this.abCache = abCache; + } + + @Override + public Cache getCache(String name) { + Cache cache = regionCacheMap.computeIfAbsent(StrUtil.nullToEmpty(name), region -> new AbSpringCache(abCache, region)); + return new TransactionAwareCacheDecorator(cache); + } + + @Override + public Collection getCacheNames() { + return regionCacheMap.keySet(); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/CacheRegion.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/CacheRegion.java new file mode 100644 index 00000000..22b13152 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/CacheRegion.java @@ -0,0 +1,68 @@ +package com.dstz.base.common.cache; + +import java.time.Duration; + +/** + * 缓存区域 + * + * @author wacxhs + */ +public class CacheRegion { + + /** + * 区域 + */ + private String region; + + /** + * 过期时间 + */ + private Duration expiration; + + /** + * 缓存大小 + */ + private Long size; + + public CacheRegion(String region, Duration expiration) { + this(region, expiration, null); + } + + public CacheRegion(String region, Duration expiration, Long size) { + this.region = region; + this.expiration = expiration; + this.size = size; + } + + public String getRegion() { + return region; + } + + public void setRegion(String region) { + this.region = region; + } + + public Duration getExpiration() { + return expiration; + } + + public void setExpiration(Duration expiration) { + this.expiration = expiration; + } + + public Long getSize() { + return size; + } + + public void setSize(Long size) { + this.size = size; + } + + @Override + public String toString() { + return "CacheRegion{" + + "region='" + region + '\'' + + ", expiration=" + expiration + + '}'; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/ICache.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/ICache.java new file mode 100644 index 00000000..7c45b3b7 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/ICache.java @@ -0,0 +1,71 @@ +package com.dstz.base.common.cache; + +import java.util.concurrent.Callable; +/** + *
+ * 系统缓存
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2021-12-10
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public interface ICache { + + /** + * 获取缓存,如果不存在返回null + * + * @param region 缓存区域 + * @param key 缓存键 + * @return 缓存值 + */ + V getIfPresent(String region, String key); + + /** + * 获取缓存,如果缓存不存在从loader中加载 + * + * @param region 缓存区域 + * @param key 缓存键 + * @param loader 缓存加载器 + * @return 缓存值 + */ + V get(String region, String key, Callable loader); + + /** + * 放置缓存 + * + * @param region 缓存区域 + * @param key 缓存键 + * @param value 缓存值 + */ + void put(String region, String key, Object value); + + /** + * 缓存失效 + * + * @param region 缓存区域 + * @param key 缓存键 + */ + void invalidate(String region, String ...key); + + /** + * 缓存区域失效 + * + * @param region 缓存区域 + */ + void invalidateRegion(String region); + + /** + * 所有缓存失效 + */ + void invalidateAll(); + + /** + * 缓存是否存在 + * + * @param region 缓存区域 + * @param key 缓存键 + * @return 是否存在 + */ + boolean exists(String region, String key); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/MemoryCache.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/MemoryCache.java new file mode 100644 index 00000000..d2f7958a --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/MemoryCache.java @@ -0,0 +1,86 @@ +package com.dstz.base.common.cache; + +import cn.hutool.core.lang.Assert; +import com.dstz.base.common.utils.CastUtils; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import org.springframework.util.ReflectionUtils; + +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.stream.Collectors; + +/** + * 内存缓存,基于Guava实现,带有缓存过期性 + * + * @author wacxhs + */ +public class MemoryCache implements ICache { + + private final Map> cacheRegion; + + public MemoryCache(List cacheRegionList) { + cacheRegion = cacheRegionList.stream().collect(Collectors.toMap(CacheRegion::getRegion, this::newCache)); + } + + private Cache newCache(CacheRegion cacheRegion) { + CacheBuilder cacheBuilder = CacheBuilder.newBuilder().expireAfterAccess(cacheRegion.getExpiration()); + if (cacheRegion.getSize() != null) { + cacheBuilder.maximumSize(cacheRegion.getSize()); + } + return cacheBuilder.build(); + } + + private Cache getRegionCache(String region) { + Cache cache = cacheRegion.get(region); + Assert.notNull(cache, () -> new IllegalArgumentException(String.format("Cache Region %s is not configured", region))); + return cache; + } + + @Override + public V getIfPresent(String region, String key) { + return CastUtils.cast(getRegionCache(region).getIfPresent(key)); + } + + @Override + public V get(String region, String key, Callable loader) { + try { + return CastUtils.cast(getRegionCache(region).get(key, loader)); + } catch (ExecutionException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + @Override + public void put(String region, String key, Object value) { + // 值为空不放入到缓存中 + if(Objects.nonNull(value)) { + getRegionCache(region).put(key, value); + } + } + + @Override + public void invalidate(String region, String... keys) { + getRegionCache(region).invalidateAll(Arrays.asList(keys)); + } + + @Override + public void invalidateRegion(String region) { + getRegionCache(region).invalidateAll(); + } + + @Override + public void invalidateAll() { + cacheRegion.values().forEach(Cache::invalidateAll); + } + + @Override + public boolean exists(String region, String key) { + return Objects.nonNull(getRegionCache(region).getIfPresent(key)); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/RegionCacheUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/RegionCacheUtil.java new file mode 100644 index 00000000..2ca9efc8 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/cache/RegionCacheUtil.java @@ -0,0 +1,42 @@ +package com.dstz.base.common.cache; + +import java.util.concurrent.Callable; + +import cn.hutool.extra.spring.SpringUtil; +/** + *
+ * 便捷使用分区缓存的工具
+ * 
+ * @author aschs + * @date 2022年4月7日 + * @owner 深圳市大世同舟信息科技有限公司 + * @param + */ +public class RegionCacheUtil { + private String region; + + public RegionCacheUtil(String region) { + super(); + this.region = region; + } + + public void put(String key, T obj) { + SpringUtil.getBean(ICache.class).put(region, key, obj); + } + + public T get(String key) { + return (T) SpringUtil.getBean(ICache.class).getIfPresent(region, key); + } + + public T get(String key, Callable loader) { + return (T) SpringUtil.getBean(ICache.class).get(region, key, loader); + } + + public void remove(String key) { + SpringUtil.getBean(ICache.class).invalidate(region, key); + } + + public void removeAll() { + SpringUtil.getBean(ICache.class).invalidateRegion(region); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/ConfigRestClientInfoTransform.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/ConfigRestClientInfoTransform.java new file mode 100644 index 00000000..f27a38b2 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/ConfigRestClientInfoTransform.java @@ -0,0 +1,63 @@ +package com.dstz.base.common.client; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.servlet.ServletUtil; +import com.dstz.base.common.utils.AbRequestUtils; + +import javax.servlet.http.Cookie; +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * 配置式客户端请求信息转发 + * + * @author wacxhs + */ +public class ConfigRestClientInfoTransform implements RestClientInfoTransform { + + private final List> cookies; + private final List> headers; + + public ConfigRestClientInfoTransform(List> cookies, List> headers) { + this.cookies = cookies; + this.headers = headers; + } + + @Override + public Map getCookies() { + if (CollUtil.isEmpty(cookies)) { + return Collections.emptyMap(); + } + Map cookieMap = Optional.ofNullable(AbRequestUtils.getHttpServletRequest()).map(ServletUtil::readCookieMap).orElse(Collections.emptyMap()); + Map returnCookieMap = MapUtil.newHashMap(cookies.size()); + Cookie cookie; + for (Pair cookieDefinition : cookies) { + if (StrUtil.isNotEmpty(cookieDefinition.getValue())) { + returnCookieMap.put(cookieDefinition.getKey(), cookieDefinition.getValue()); + } else if (Objects.nonNull(cookie = cookieMap.get(cookieDefinition.getKey()))) { + returnCookieMap.put(cookieDefinition.getKey(), cookie.getValue()); + } + } + return returnCookieMap; + } + + @Override + public Map getHeaders() { + if (CollUtil.isEmpty(headers)) { + return Collections.emptyMap(); + } + HttpServletRequest request = AbRequestUtils.getHttpServletRequest(); + Map returnHeaderMap = MapUtil.newHashMap(headers.size()); + for (Pair headerDefinition : headers) { + if (StrUtil.isNotEmpty(headerDefinition.getValue())) { + returnHeaderMap.put(headerDefinition.getKey(), headerDefinition.getValue()); + } else if (request != null) { + returnHeaderMap.put(headerDefinition.getKey(), request.getHeader(headerDefinition.getKey())); + } + } + return returnHeaderMap; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransform.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransform.java new file mode 100644 index 00000000..e62ce1d6 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransform.java @@ -0,0 +1,31 @@ +package com.dstz.base.common.client; + +import java.util.Collections; +import java.util.Map; + +/** + * rest client 请求信息交换,用于透传token及信息 + * + * @author wacxhs + */ +public interface RestClientInfoTransform { + + /** + * 获取Cookie + * + * @return cookie + */ + default Map getCookies() { + return Collections.emptyMap(); + } + + /** + * 获取头信息 + * + * @return 头信息 + */ + default Map getHeaders() { + return Collections.emptyMap(); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransformComposite.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransformComposite.java new file mode 100644 index 00000000..83ecc565 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/client/RestClientInfoTransformComposite.java @@ -0,0 +1,71 @@ +package com.dstz.base.common.client; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.constats.StrPool; + +import java.util.Collection; +import java.util.Map; +import java.util.stream.Collectors; + + +/** + * rest 请求信息转发组合 + * + * @author wacxhs + */ +public class RestClientInfoTransformComposite implements RestClientInfoTransform { + + private final Collection transforms; + + public RestClientInfoTransformComposite(Collection transforms) { + this.transforms = transforms; + } + + @Override + public Map getCookies() { + Map mergeCookies = MapUtil.newHashMap(); + for (RestClientInfoTransform transform : transforms) { + Map cookies = transform.getCookies(); + if (MapUtil.isNotEmpty(cookies)) { + mergeCookies.putAll(cookies); + } + } + return mergeCookies; + } + + @Override + public Map getHeaders() { + Map mergeHeaders = MapUtil.newHashMap(); + for (RestClientInfoTransform transform : transforms) { + Map headers = transform.getHeaders(); + if (MapUtil.isNotEmpty(headers)) { + mergeHeaders.putAll(headers); + } + } + return mergeHeaders; + } + + /** + * 获取cookie string + * + * @return cookie string + */ + public String getCookiesString() { + return getCookies().entrySet().stream().map(o -> StrUtil.join("=", o.getKey(), o.getValue())).collect(Collectors.joining(StrPool.SEMICOLON)); + } + + /** + * 获取实例 + * + * @return 实例 + */ + public static RestClientInfoTransformComposite getInstance() { + return Holder.INSTANCE; + } + + private static class Holder { + static final RestClientInfoTransformComposite INSTANCE = new RestClientInfoTransformComposite(SpringUtil.getBeansOfType(RestClientInfoTransform.class).values()); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/BaseCodeImpl.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/BaseCodeImpl.java new file mode 100644 index 00000000..5a18a9ef --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/BaseCodeImpl.java @@ -0,0 +1,50 @@ +package com.dstz.base.common.codes; + +/** + * base code 实现 + * + * @author wacxhs + * @since 2022-01-22 + */ +class BaseCodeImpl implements IBaseCode { + + private String code; + + private String message; + + public BaseCodeImpl() { + super(); + } + + public BaseCodeImpl(String code, String message) { + super(); + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public String getMessage() { + return message; + } + + public void setMessage(String message) { + this.message = message; + } + + @Override + public String toString() { + return "BaseCodeImpl{" + + "code='" + code + '\'' + + ", message='" + message + '\'' + + '}'; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/IBaseCode.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/IBaseCode.java new file mode 100644 index 00000000..eeb89266 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/codes/IBaseCode.java @@ -0,0 +1,131 @@ +package com.dstz.base.common.codes; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.vo.ApiResponse; +import org.slf4j.helpers.MessageFormatter; + +/** + *
+ * 系统接口响应码 定义
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2022-01-22
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public interface IBaseCode { + + /** + * 获取状态码 + * + * @return 状态码 + */ + String getCode(); + + /** + * 获取消息,支持参数格式化,用法上与slf4j相同 + * + * @return 状态信息模式 + */ + String getMessage(); + + /** + * 格式化默认消息 + * + * @param arguments 参数项 + * @return 新BaseCode + */ + default IBaseCode formatDefaultMessage(Object... arguments) { + return formatMessage(getMessage(), arguments); + } + + /** + * 格式化消息,参数支持格式化模式,用法上与slf4j相同 + * + * @param messagePattern 获取消息模式,可做参数格式化,用法上与slf4j相同 + * @param arguments 参数项 + * @return 新BaseCode + */ + @Deprecated + default IBaseCode formatMessage(String messagePattern, Object... arguments) { + return newBuilder().withCode(getCode()).withMessage(messagePattern, arguments).build(); + } + + /** + *
+     * 利用hutool的StrUtil.format格式化信息
+     * eg:
+     * xxx.formatHutoolMessage("a={},b={}", a, b)
+     * 
+ * + * @param messagePattern + * @param arguments + * @return + */ + default IBaseCode formatHutoolMessage(String messagePattern, Object... arguments) { + return formatDefaultMessage(StrUtil.format(messagePattern, arguments)); + } + + /** + * 新建构建器 + * + * @return 构建器 + */ + static Builder newBuilder() { + return new Builder(); + } + + /** + * 构建器 + */ + class Builder { + + private final BaseCodeImpl baseCode = new BaseCodeImpl(); + + /** + * 使用状态码 + * + * @param code 状态码 + * @return 构建器 + */ + public Builder withCode(String code) { + baseCode.setCode(code); + return this; + } + + /** + * 使用消息 + * + * @param format 格式化,与slf4j logger使用方式一致 + * @param arguments 格式化参数 + * @return 构建器 + */ + public Builder withMessage(String format, Object... arguments) { + if (arguments == null || arguments.length == 0) { + baseCode.setMessage(format); + } else { + baseCode.setMessage(MessageFormatter.arrayFormat(format, arguments).getMessage()); + } + return this; + } + + /** + * 构建出响应码 + * + * @return 响应码 + */ + public IBaseCode build() { + return baseCode; + } + } + + /** + * 构建接口响应 + * + * @param 接口响应体 + * @return 接口响应 + */ + default ApiResponse buildApiResponse() { + return ApiResponse.fail(getCode(), getMessage()); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbAppRestConstant.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbAppRestConstant.java new file mode 100644 index 00000000..74dd75dc --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbAppRestConstant.java @@ -0,0 +1,50 @@ +package com.dstz.base.common.constats; + +/** + * AB 模块REST常量 + * + * @author wacxhs + * @since 2022-01-22 + */ +public class AbAppRestConstant { + + private AbAppRestConstant() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + + /** + * ORG 服务前缀(目前的基础微服务) + */ + public static final String ORG_SERVICE_PREFIX = "${ab.org-rest-prefix:/ab-org}"; + + /** + * BPM 服务前缀(目前的基础微服务) + */ + public static final String BPM_SERVICE_PREFIX = "${ab.bpm-rest-prefix:/ab-bpm}"; + + /** + * DEMO 服务前缀(目前依附在bpm微服务下) + */ + public static final String DEMO_SERVICE_PREFIX = BPM_SERVICE_PREFIX + "/demo"; + + /** + * AUTH 服务前缀(目前依附在org微服务下) + */ + public static final String AUTH_SERVICE_PREFIX = ORG_SERVICE_PREFIX + "/auth"; + + /** + * CMS CMS前缀(目前依附在bpm微服务下) + */ + public static final String CMS_SERVICE_PREFIX = BPM_SERVICE_PREFIX + "/cms"; + + /** + * BIZ前缀(目前依附在bpm微服务下) + */ + public static final String BIZ_SERVICE_PREFIX = BPM_SERVICE_PREFIX + "/biz"; + + /** + * SYS前缀(目前依附在bpm微服务下) + */ + public static final String SYS_SERVICE_PREFIX = BPM_SERVICE_PREFIX + "/sys"; +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbCacheRegionConstant.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbCacheRegionConstant.java new file mode 100644 index 00000000..1f3b066d --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/AbCacheRegionConstant.java @@ -0,0 +1,94 @@ +package com.dstz.base.common.constats; + +/** + * 缓存区域常量 + * + * @author wacxhs + */ +public class AbCacheRegionConstant { + + private AbCacheRegionConstant() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + + /** + *登录相关token + */ + public static final String LOGIN_CACHE_REGION = "LOGIN_TOKEN"; + + /** + *用户找回密码相关 + */ + public static final String LOGIN_PWD_REGION = "PWD_TOKEN"; + + /** + * 系统资源相关 + */ + public static final String SYS_RESOURCE = "SYS_RESOURCE"; + + /** + * 工作交接相关 + */ + public static final String WORK_HANDOVER_REGION = "WORK_HANDOVER"; + + /** + * 系统属性想关 + */ + public static final String PROPERTIES_CACHE_REGION = "SYSPROPETIES"; + + /** + * 字典相关 + */ + public static final String DICT_CACHE_REGION = "DICT"; + + /** + * 审计日志元信息 + */ + public static final String AUDIT_LOG_META = "AUDIT_LOG_META"; + + + /** + * 消息相关 + */ + public static final String MSG_REGION = "MSG_REGION"; + + /** + * 第三方登录token + */ + public static final String THIRD_TOKEN = "THIRD_TOKEN"; + /** + * agileBPM 流程定义 + */ + public static final String BPM_PROCESS_DEF = "BPM_PROCESS_DEF"; + + /** + * activiti ProcessDefinitionEntity + */ + public static final String BPM_ACT_PROCESS_DEF = "BPM_ACT_PROCESS_DEF"; + + /** + * 数据权限 + */ + public static final String DATA_PRIVILEGE = "DATA_PRIVILEGE"; + + /** + * 业务对象的缓存 + */ + public static final String BIZ_OBJECT_REGION = "BIZ_OBJECT"; + + /** + * 系统应用 + */ + public static final String SYS_APPLICATION = "SYS_APPLICATION"; + + /** + * 系统会话属性 + */ + public static final String SYS_SESSION_ATTRIBUTE = "SYS_SESSION_ATTRIBUTE"; + + /** + * + */ + public static final String OAUTH_REGION = ""; +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/InnerMsgEnum.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/InnerMsgEnum.java new file mode 100644 index 00000000..2ad92fb3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/InnerMsgEnum.java @@ -0,0 +1,115 @@ +package com.dstz.base.common.constats; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author niu + * @description CMS站内信的枚举常量 + */ +public enum InnerMsgEnum { + /** + * 站内信类型-新闻 + */ + NEWS("news", "新闻", 0), + + /** + * 站内信类型-公告 + */ + NOTIFY("notify", "公告", 0), + + /** + * 站内信类型-行程安排 + */ + SCHEDULE("schedule", "行程安排", 0), + + /** + * 站内信类型-流程的评论 + */ + WF_COMMENT("wfComment", "流程评论", 0), + + /** + * 站内信类型-流程待办 + */ + WF_TODO("wfTodo", "流程待办", 1), + + /** + * 站内信类型-流程催办 + */ + WF_URGE("wfUrge", "流程催办", 0), + + /** + * 站内信类型-流程抄送 + */ + WF_COPY("wfCopy", "流程抄送", 0), + + /** + * 站内信类型-消息通知 + */ + WF_NODE_MESSAGE("wfNodeMessage", "节点消息通知", 1), + + /** + * 站内信类型-流程驳回 + */ + WF_REJECT("wfReject", "流程驳回", 1), + + /** + * 站内信类型-流程结束 + */ + WF_OVER("wfOver", "流程结束", 1), + + /** + * 站内信类型-流程任务转办 + */ + WF_TASK_TURN("taskTurn", "任务转办", 1), + + WF_CARBON_COPY("carbonCopy", "流程传阅", 0), + + WF_RECALL("wfRecall", "流程撤回", 0), + ; + + private final String key; + private final String value; + private final int type; + + /** + * 构造方法 + * + * @param key 类型编码(数据库存储的值) + * @param value 类型文字描述 + * @param type 类型的通知分类(对七个小类型又进行了两种大类: 0通知 1待办) + */ + InnerMsgEnum(String key, String value, int type) { + this.key = key; + this.value = value; + this.type = type; + } + + public String getValue() { + return value; + } + + public String getKey() { + return key; + } + + public int getType() { + return type; + } + + public static InnerMsgEnum getByKey(String key) { + for (InnerMsgEnum e : InnerMsgEnum.values()) { + if (key.equals(e.key + "")) { + return e; + } + } + return null; + } + + public static List getKeyListByType(int type) { + return Arrays.stream(InnerMsgEnum.values()).filter(s -> s.getType() == type) + .map(InnerMsgEnum::getKey).collect(Collectors.toList()); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/MDCConstant.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/MDCConstant.java new file mode 100644 index 00000000..93ba7f02 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/MDCConstant.java @@ -0,0 +1,15 @@ +package com.dstz.base.common.constats; + +/** + * MDC 常量定义 + * + * @author wacxhs + */ +public class MDCConstant { + + /** + * 请求追踪 ID + */ + public static String TRACE_ID = "traceId"; + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/NumberPool.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/NumberPool.java new file mode 100644 index 00000000..eca071c3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/NumberPool.java @@ -0,0 +1,115 @@ +package com.dstz.base.common.constats; + +/** + * 数字常量池 + * + * @author wacxhs + */ +public class NumberPool { + + private NumberPool() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * Reusable Long constant for zero. + */ + public static final Long LONG_ZERO = 0L; + /** + * Reusable Long constant for one. + */ + public static final Long LONG_ONE = 1L; + /** + * Reusable Long constant for minus one. + */ + public static final Long LONG_MINUS_ONE = -1L; + /** + * Reusable Integer constant for zero. + */ + public static final Integer INTEGER_ZERO = 0; + /** + * Reusable Integer constant for one. + */ + public static final Integer INTEGER_ONE = 1; + /** + * Reusable Integer constant for two + */ + public static final Integer INTEGER_TWO = 2; + /** + * Reusable Integer constant for minus one. + */ + public static final Integer INTEGER_MINUS_ONE = -1; + /** + * Reusable Short constant for zero. + */ + public static final Short SHORT_ZERO = (short) 0; + /** + * Reusable Short constant for one. + */ + public static final Short SHORT_ONE = (short) 1; + /** + * Reusable Short constant for minus one. + */ + public static final Short SHORT_MINUS_ONE = (short) -1; + /** + * Reusable Byte constant for zero. + */ + public static final Byte BYTE_ZERO = (byte) 0; + /** + * Reusable Byte constant for one. + */ + public static final Byte BYTE_ONE = (byte) 1; + /** + * Reusable Byte constant for minus one. + */ + public static final Byte BYTE_MINUS_ONE = (byte) -1; + /** + * Reusable Double constant for zero. + */ + public static final Double DOUBLE_ZERO = 0.0d; + /** + * Reusable Double constant for one. + */ + public static final Double DOUBLE_ONE = 1.0d; + /** + * Reusable Double constant for minus one. + */ + public static final Double DOUBLE_MINUS_ONE = -1.0d; + /** + * Reusable Float constant for zero. + */ + public static final Float FLOAT_ZERO = 0.0f; + /** + * Reusable Float constant for one. + */ + public static final Float FLOAT_ONE = 1.0f; + /** + * Reusable Float constant for minus one. + */ + public static final Float FLOAT_MINUS_ONE = -1.0f; + + /** + * {@link Integer#MAX_VALUE} as a {@link Long}. + * + * @since 3.12.0 + */ + public static final Long LONG_INT_MAX_VALUE = (long) Integer.MAX_VALUE; + + /** + * {@link Integer#MIN_VALUE} as a {@link Long}. + * + * @since 3.12.0 + */ + public static final Long LONG_INT_MIN_VALUE = (long) Integer.MIN_VALUE; + + /** + * integer中当作false的值 + */ + public static final Integer BOOLEAN_FALSE = 0; + + /** + * integer中当作true的值 + */ + public static final Integer BOOLEAN_TRUE = 1; + +} \ No newline at end of file diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/StrPool.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/StrPool.java new file mode 100644 index 00000000..99347be2 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/StrPool.java @@ -0,0 +1,93 @@ +package com.dstz.base.common.constats; + +/** + * 字符串常量池 + * + * @author Jeff + */ +public interface StrPool extends cn.hutool.core.text.StrPool { + /** + * 字符常量:$ + */ + String DOLLAR = "$"; + + /** + * 取余,字符常量:% + */ + String MOD = "%"; + + /** + * 左括号,字符常量:( + */ + String LPAREN = "("; + + /** + * 右括号,字符常量:) + */ + String RPAREN = ")"; + /** + * String中当作true的值 + */ + String BOOLEAN_FALSE = "0"; + + /** + * String中当作false的值 + */ + String BOOLEAN_TRUE = "1"; + + /** + * 分号,字符常量:; + */ + String SEMICOLON = ";"; + + /** + * 空字符串,字符常量: + */ + String EMPTY = ""; + + /** + * 数字0 + */ + String NUMBER_ZERO = "0"; + + /** + * #号 + */ + String HASH = "#"; + + /** + * *号 + */ + String BIT = "*"; + + String FALSE = "false"; + + String TRUE = "true"; + + String FORMATSTR = "%s_%s"; + /** + * 默认多数据的分格符号 , + */ + String SPLIT = C_COMMA + ""; + + String FROM = "system"; + + String REGEX = "\\."; + + String STRING_PWD_ONZ = "1"; + // 英文 冒号 + String COLON = ":"; + + // 中文 冒号 + String COLON_ZN = ":"; + + /** + * 未知 + */ + String UNKNOWN = "unknown"; + + /** + * 默认数据源别名 + */ + String DEFAULT_DATASOURCE_ALIAS = "datasourceDefault"; +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/ThreadMapKeyConstant.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/ThreadMapKeyConstant.java new file mode 100644 index 00000000..ecfd6ea4 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/constats/ThreadMapKeyConstant.java @@ -0,0 +1,51 @@ +package com.dstz.base.common.constats; + +/** + * 线程变量KEY常量定义 + * + * @author wacxhs + */ +public abstract class ThreadMapKeyConstant { + + public static class DataPrivilege { + + public static final String PREFIX = "DataPrivilege"; + + /** + * 当前用户数据权限 + */ + public static final String CURRENT_USER = PREFIX + ".currentUser"; + } + + public static class QueryFilter { + + /** + * + */ + public static final String PREFIX = "QueryFilter"; + + /** + * 允许参数过滤 + */ + public static final String ACCESS_QUERY_FILTERS = PREFIX + ".accessQueryFilters"; + } + + public static class BizFormDesign { + public static final String PREFIX = "BizFormDesign"; + + public static final String BO = PREFIX + ".bo"; + + public static final String BOS = PREFIX + ".bos"; + + public static final String TABLES = PREFIX + ".tables"; + + } + + public static class BizData { + public static final String PREFIX = "BizData"; + + public static final String IS_FLOW = PREFIX + ".isFlow"; + + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/ContextDuplication.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/ContextDuplication.java new file mode 100644 index 00000000..38af7a1f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/ContextDuplication.java @@ -0,0 +1,25 @@ +package com.dstz.base.common.context; + + +/** + * 上下文副本,用于多线程下的数据传递 + * + * @author wacxhs + */ +public interface ContextDuplication { + + /** + * 从Context制作出一个副本 + * + * @return 副本 + */ + Object duplicate(); + + /** + * 复制出的副本填充当前Context + * + * @param duplicate 副本 + */ + void fill(Object duplicate); + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/UserContext.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/UserContext.java new file mode 100644 index 00000000..6a93e06c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/context/UserContext.java @@ -0,0 +1,82 @@ +package com.dstz.base.common.context; + +import java.util.Collection; +import java.util.Optional; + +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; + +/** + *
+ * 用户上下文,从当前线程中获取用户、用户所在组织及权限信息
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2022-03-22
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public interface UserContext extends ContextDuplication { + + /** + * 获取用户 + * + * @return 用户 + */ + Optional getUser(); + + /** + * 设置当前用户 + * + * @param user 用户 + * @return 用户 + */ + void setUser(IUser user); + + /** + * 获取当前组织 + * + * @return 当前组织 + */ + Optional getOrg(); + + /** + * 设置当前组织 + * + * @param org 组织 + * @return 当前组织 + */ + void setOrg(IGroup org); + + /** + * 获取权限 + * + * @return 权限 + */ + Collection getAuthorities(); + + /** + * 设置权限 + * + * @param authorities 权限 + */ + void setAuthorities(Collection authorities); + + /** + * 是否超级管理员 + * + * @return 是否超级管理员 + */ + boolean isSuperAdmin(); + + /** + * 设置超级管理员 + * + * @param superAdmin 超级管理员 + */ + void setSuperAdmin(boolean superAdmin); + + /** + * 清理 + */ + void clear(); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/Base64.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/Base64.java new file mode 100644 index 00000000..d7262a89 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/Base64.java @@ -0,0 +1,40 @@ +package com.dstz.base.common.encrypt; + +import java.io.UnsupportedEncodingException; + +/** + * Base64类
+ * 功能:字符串的BASE64编码解码。 + */ +public class Base64 { + /** + * 将字符串转化为base64编码 + * + * @param s + * @return + * @throws UnsupportedEncodingException + */ + public static String getBase64(String s) + throws UnsupportedEncodingException { + byte[] bytes = org.apache.commons.codec.binary.Base64.encodeBase64(s + .getBytes("utf-8")); + return new String(bytes, "utf-8"); + + } + + /** + * 将 BASE64 编码的字符串 s 进行解码 + * + * @param s + * @return + * @throws UnsupportedEncodingException + */ + public static String getFromBase64(String s) + throws UnsupportedEncodingException { + byte[] bytes = s.getBytes("GBK"); + byte[] convertBytes = org.apache.commons.codec.binary.Base64 + .decodeBase64(bytes); + return new String(convertBytes, "GBK"); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/EncryptUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/EncryptUtil.java new file mode 100644 index 00000000..bd9ce3b5 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/encrypt/EncryptUtil.java @@ -0,0 +1,97 @@ +package com.dstz.base.common.encrypt; + +import cn.hutool.crypto.CryptoException; +import cn.hutool.crypto.digest.DigestUtil; +import cn.hutool.crypto.symmetric.SymmetricAlgorithm; +import cn.hutool.crypto.symmetric.SymmetricCrypto; + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import org.apache.commons.codec.binary.Base64; +import javax.crypto.KeyGenerator; +import javax.crypto.SecretKey; +import java.security.NoSuchAlgorithmException; +import java.security.SecureRandom; + +/** + * 加密算法。
+ * 2.SHA-256
+ */ +public class EncryptUtil { + + /** + * 密钥 + */ + private static final String DEFAULT_K = "@#$%^6a7"; + + /** + * 输出明文按sha-256加密后的密文 + * + * @param inputStr 明文 + * @return + */ + public static String encryptSha256(String inputStr) { + try { + byte[] digest = DigestUtil.sha256(inputStr); + return new String(Base64.encodeBase64(digest)); + } catch (Exception e) { + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR,e); + } + } + + /** + * 对称解密算法 + * + * @param message + * @return + * @throws Exception + */ + public static String decrypt(String message) { + return aesDecrypt(message, DEFAULT_K); + } + + /** + * 对称加密算法 + * + * @param message + * @return + * @throws Exception + */ + public static String encrypt(String message) { + return aesEncrypt(message, DEFAULT_K); + } + + private static SecretKey getSecretKey(String password) throws NoSuchAlgorithmException { + KeyGenerator keyGen = KeyGenerator.getInstance(SymmetricAlgorithm.AES.getValue()); + SecureRandom secureRandom = SecureRandom.getInstance("SHA1PRNG"); + secureRandom.setSeed(password.getBytes()); + keyGen.init(128, secureRandom); + return keyGen.generateKey(); + } + + /** + * aes解密-128位 + */ + public static String aesDecrypt(String encryptContent, String password) { + try { + SecretKey secretKey = getSecretKey(password); + SymmetricCrypto symmetricCrypto = new SymmetricCrypto(SymmetricAlgorithm.AES, secretKey); + return symmetricCrypto.decryptStr(encryptContent); + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } + } + + /** + * aes加密-128位 + */ + public static String aesEncrypt(String content, String password) { + try { + SecretKey secretKey = getSecretKey(password); + SymmetricCrypto symmetricCrypto = new SymmetricCrypto(SymmetricAlgorithm.AES, secretKey); + return symmetricCrypto.encryptHex(content); + } catch (NoSuchAlgorithmException e) { + throw new CryptoException(e); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/DataType.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/DataType.java new file mode 100644 index 00000000..6779393b --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/DataType.java @@ -0,0 +1,78 @@ +package com.dstz.base.common.enums; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.exceptions.BusinessException; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import java.util.Arrays; +import java.util.Date; + +public enum DataType { + /** + * 浮点型 + */ + DOUBLE("number","数字", Double.class), + + /** + * 字符串 + */ + STRING("varchar","字符串", String.class), + + /** + * 布尔 + */ + BOOL("bool","布尔", Boolean.class), + + /** + * 日期 + */ + DATE("date","日期", Date.class); + + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + /** + * java type + */ + @JsonIgnore + private final Class javaType; + + DataType(String type, String desc, Class javaType) { + this.type = type; + this.desc = desc; + this.javaType = javaType; + } + + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + public Class getJavaType() { + return javaType; + } + + public static Object parseValueByType(String dataType, String value) { + if (StrUtil.isBlank(value)) { + return null; + } + return Arrays.stream(values()).filter(o -> o.getType().equals(dataType)) + .findFirst() + .map(o -> Convert.convert(o.getJavaType(), value)) + .orElseThrow(() -> new BusinessException(GlobalApiCodes.PARAMETER_INVALID.formatDefaultMessage(value))); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/EnvironmentConstants.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/EnvironmentConstants.java new file mode 100644 index 00000000..3729a5bd --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/EnvironmentConstants.java @@ -0,0 +1,57 @@ +package com.dstz.base.common.enums; + +import cn.hutool.core.util.StrUtil; + +/** + * @author jinxia.hou + * @Name EnvironmentConstant + * @description: 环境常量 + * @date 2022/2/1416:14 + */ +public enum EnvironmentConstants { + + DEV("DEV", ",开发-默认"), + SIT("SIT", "测试"), + DEMO("DEMO", "案例"), + UAT("UAT", "用户测试"), + GRAY("GRAY", "灰度"), + PROD("PROD", "生产"); + + + private final String key; + private final String value; + + EnvironmentConstants(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + public static String getKes() { + StringBuilder sb = new StringBuilder(); + for (EnvironmentConstants e : EnvironmentConstants.values()) { + sb.append("[").append(e.key).append("]"); + } + return sb.toString(); + } + + public static boolean contain(String key) { + if (StrUtil.isEmpty(key)) { + return false; + } + + for (EnvironmentConstants e : EnvironmentConstants.values()) { + if (key.equals(e.key)) { + return true; + } + } + return false; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/GlobalApiCodes.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/GlobalApiCodes.java new file mode 100644 index 00000000..2f3cbc5c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/GlobalApiCodes.java @@ -0,0 +1,113 @@ +package com.dstz.base.common.enums; + +import com.dstz.base.common.codes.IBaseCode; + + +/** + * 全局接口响应码 + * + * @author wacxhs + * @since 2022-01-22 + */ +public enum GlobalApiCodes implements IBaseCode { + + /** + * 操作成功 + */ + SUCCESS("Success", "操作成功"), + + /** + * Illegal parameters. + */ + PARAMETER_INVALID("ParameterInvalid", "Illegal parameters {}"), + + /** + * Parameter unallowed. + */ + PARAMETER_UNALLOWED("ParameterUnallowed", "Parameter not allowed {}"), + + /** + * User not authorized to operate on the specified resource. + */ + ACCESS_FORBIDDEN("AccessForbidden", "User not authorized to operate on the specified resource."), + + /** + * The specified resource is not found. + */ + RESOURCE_NOT_FOUND("ResourceNotFound", "The specified resource is not found."), + + /** + * 解析失败 + */ + PARSE_ERROR ("parseError", "{}解析失败!"), + + /** + * {}数据已经存在{} + */ + DATA_DUPLICATION ("DataDuplication", "{}数据已经存在{}"), + + DATA_NOT_FOUND ("DataDuplication", "{}数据不存在{}"), + + + NO_LOGIN_USER ("noLoginUser", "登录用户不存在!"), + + /** + * 删除失败,存在级联数据 + */ + DELETE_FAILED_HAS_ASSOCIATED_DATA ("deleteFailedHasAssociatedData", "删除失败,存在相关联的数据:{}"), + + /** + * 通用异常 + */ + BASE_COMMON_ERROR ("baseCommonError", "base模块通用异常"), + + /** + * The request processing has failed due to some unknown error. + */ + INTERNAL_ERROR("InternalError", "系统内部出错"), + + /** + * 远程调用错误 + */ + REMOTE_CALL_ERROR("RemoteCallError", "{}"), + + /** + * 登录会话超时 + */ + LOGIN_INVALID("LoginValid", "登录会话超时,请重新登录!"), + + /** + * 请求限流 + */ + REQUEST_FLOW_LIMITING("RequestFlowLimiting", "服务器忙,请稍后再试!"), + + /** + * 服务降级 + */ + SERVICE_DEGRADE("ServiceDegrade", "服务不可用,请稍后再试!"), + + /** + * 数据已被他人更新(乐观锁) + */ + DATA_VERSION_OLD("DataVersionOld", "数据已被更新,请刷新页面重新编辑保存"); + + + private final String code; + + private final String message; + + GlobalApiCodes(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IEnumEqual.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IEnumEqual.java new file mode 100644 index 00000000..52eee6be --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IEnumEqual.java @@ -0,0 +1,22 @@ +package com.dstz.base.common.enums; + + +import cn.hutool.core.util.ReflectUtil; + +public interface IEnumEqual { + /** + *
+	 * 【注意这不是取值】枚举KEY 的的字段名字的定义,
+	 * 用于通用接口,统一所有枚举取key对比逻辑使用
+	 * 默认是"key",也可以是"type","name"等
+	 * 
+ * @return + */ + default String getKeyName() { + return "key"; + } + + default boolean equalsWithKey(String key) { + return key.equals(ReflectUtil.getFieldValue(this, getKeyName())); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IdentityType.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IdentityType.java new file mode 100644 index 00000000..23a9322c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/IdentityType.java @@ -0,0 +1,55 @@ +package com.dstz.base.common.enums; + +/** + * 人员类型 + * 可以根据情况扩展更多类型,目前AB用到的 user ,role ,org ,post + * + */ +public enum IdentityType { + USER("user", "用户"), + ROLE("role", "角色"), + ORG("org", "组织"), + GROUP("group", "小组"), + POST("post", "岗位"); + + /** + * 键 + */ + private final String key; + /** + * 值 + */ + private final String value; + + IdentityType(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + + @Override + public String toString() { + return key; + } + + /** + * 通过key获取对象 + * + * @param key + * @return + */ + public static IdentityType fromKey(String key) { + for (IdentityType c : IdentityType.values()) { + if (c.getKey().equalsIgnoreCase(key)) + return c; + } + throw new IllegalArgumentException(key); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/LabelCss.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/LabelCss.java new file mode 100644 index 00000000..229fc117 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/enums/LabelCss.java @@ -0,0 +1,49 @@ +package com.dstz.base.common.enums; + +public enum LabelCss { + /** + * 白色 + */ + DEFAULT("default","默认"), + /** + * 蓝色 + */ + PRIMARY("primary","主色调"), + /** + * 绿色 + */ + SUCCESS("success","成功"), + /** + * 黄色 + */ + WARNING("warning","警告"), + /** + * 红色 + */ + ERROR("error","错误"), + ; + + /** + * 键 + */ + private final String key; + + /** + * 值 + */ + private final String value; + + LabelCss(String key, String value) { + this.key = key; + this.value = value; + } + + public String getKey() { + return key; + } + + public String getValue() { + return value; + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbRequestLogEvent.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbRequestLogEvent.java new file mode 100644 index 00000000..0bfc81ca --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbRequestLogEvent.java @@ -0,0 +1,81 @@ +package com.dstz.base.common.events; + +import com.dstz.base.common.requestlog.AbRequestLog; +import com.dstz.base.common.utils.ToStringUtils; +import org.springframework.context.ApplicationEvent; + +/** + * 请求日志事件 + * + * @author wacxhs + */ +public class AbRequestLogEvent extends ApplicationEvent { + + private static final long serialVersionUID = 2467689461606762863L; + + /** + * 事件类型 + */ + public enum EventType { + /** + * + */ + PRE_PROCESS, + + /** + * 请求处理后 + */ + POST_PROCESS + } + + private final EventType eventType; + + + private AbRequestLogEvent(AbRequestLog abRequestLog, EventType eventType) { + super(abRequestLog); + this.eventType = eventType; + } + + /** + * 创建请求处理前事件 + * + * @param abRequestLog 请求日志 + * @return 请求日志事件 + */ + public static AbRequestLogEvent createPreProcess(AbRequestLog abRequestLog) { + return new AbRequestLogEvent(abRequestLog, EventType.PRE_PROCESS); + } + + /** + * 创建请求处理后置事件 + * + * @param abRequestLog 请求日志 + * @return 请求日志事件 + */ + public static AbRequestLogEvent createPostProcess(AbRequestLog abRequestLog) { + return new AbRequestLogEvent(abRequestLog, EventType.POST_PROCESS); + } + + /** + * 获取事件类型 + * + * @return 事件类型 + */ + public EventType getEventType() { + return eventType; + } + + /** + * 获取请求日志 + * + * @return 请求日志 + */ + public AbRequestLog getRequestLog() { + return (AbRequestLog) getSource(); + } + + @Override + public String toString() { + return ToStringUtils.toString(this); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbUserEvent.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbUserEvent.java new file mode 100644 index 00000000..817e441f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/events/AbUserEvent.java @@ -0,0 +1,45 @@ +package com.dstz.base.common.events; + +import org.springframework.context.ApplicationEvent; + +import java.util.List; +import java.util.Objects; + +/** + * 用户更新缓存事件event + * + * @author lightning + */ +public class AbUserEvent extends ApplicationEvent { + public AbUserEvent(List userAccount, EventType eventType) { + super(userAccount); + this.eventType = Objects.requireNonNull(eventType); + } + + /** + * 事件类型 + */ + public enum EventType { + /** + * 更新用户 + */ + UPDATE_USER, + + /** + * 删除用户 + */ + DELETE_USER, + + + } + + private final EventType eventType; + + public List getUserAccountList() { + return (List) source; + } + + public EventType getEventType() { + return eventType; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/ApiException.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/ApiException.java new file mode 100644 index 00000000..4ba2326f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/ApiException.java @@ -0,0 +1,41 @@ +package com.dstz.base.common.exceptions; + +import com.dstz.base.common.codes.IBaseCode; + +/** + *
+ * 接口通用异常定义,用于异常时包装异常信息
+ * 作者:wacxhs
+ * 邮箱:wacxhs@agilebpm.cn
+ * 日期:2022-03-22
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class ApiException extends RuntimeException { + + private static final long serialVersionUID = -254913157871507053L; + + /** + * 基础响应码 + */ + private final IBaseCode baseCode; + + public ApiException(IBaseCode baseCode) { + super(baseCode.getMessage()); + this.baseCode = baseCode; + } + + public ApiException(IBaseCode baseCode, Throwable throwable) { + super(baseCode.getMessage(), throwable); + this.baseCode = baseCode; + } + + protected ApiException(IBaseCode baseCode, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { + super(baseCode.getMessage(), cause, enableSuppression, writableStackTrace); + this.baseCode = baseCode; + } + + public IBaseCode getBaseCode() { + return baseCode; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessException.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessException.java new file mode 100644 index 00000000..4a6e6223 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessException.java @@ -0,0 +1,29 @@ +package com.dstz.base.common.exceptions; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * 业务逻辑异常,常常为可预料异常,此异常常常是开发时,非法操作信息提示。比如 流程表单丢失! + * + * @author wacxhs + */ +public class BusinessException extends ApiException { + + private static final long serialVersionUID = 4851159171511226800L; + + public BusinessException(IBaseCode baseCode) { + super(baseCode); + } + + public BusinessException(IBaseCode baseCode, Throwable throwable) { + super(baseCode, throwable); + } + + public static void te(IBaseCode baseCode, Throwable throwable) { + throw new BusinessException(baseCode,throwable); + } + + public static void te(IBaseCode baseCode) { + throw new BusinessException(baseCode); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessMessage.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessMessage.java new file mode 100644 index 00000000..17e59e5d --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/BusinessMessage.java @@ -0,0 +1,17 @@ +package com.dstz.base.common.exceptions; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * 业务消息,反馈给请求端,后端不记录 + * + * @author wacxhs + */ +public class BusinessMessage extends ApiException { + + private static final long serialVersionUID = -3468412951817107639L; + + public BusinessMessage(IBaseCode baseCode) { + super(baseCode, null, false, false); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/IllegalLicenseException.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/IllegalLicenseException.java new file mode 100644 index 00000000..3947de46 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/IllegalLicenseException.java @@ -0,0 +1,13 @@ +package com.dstz.base.common.exceptions; + +/** + * @author wacxhs + */ +public class IllegalLicenseException extends RuntimeException { + + private static final long serialVersionUID = 3877439584893020602L; + + public IllegalLicenseException(String message) { + super(message, null, false, false); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/OptimisticLockingFieldValueException.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/OptimisticLockingFieldValueException.java new file mode 100644 index 00000000..f5b5c6e0 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/exceptions/OptimisticLockingFieldValueException.java @@ -0,0 +1,15 @@ +package com.dstz.base.common.exceptions; + +/** + * 乐观锁字段值异常 + * + * @author wacxhs + */ +public class OptimisticLockingFieldValueException extends RuntimeException{ + + private static final long serialVersionUID = 8448037384631122436L; + + public OptimisticLockingFieldValueException(String message) { + super(message, null, false, false); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/EnumExtraData.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/EnumExtraData.java new file mode 100644 index 00000000..7d9373b0 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/EnumExtraData.java @@ -0,0 +1,29 @@ +package com.dstz.base.common.ext; +/** + * 枚举扩展数据,用于扩展系统已定义枚举,用于引入后自定义相关数据处理实现 + * + * @author wacxhs + */ +public interface EnumExtraData { + + /** + * 获取枚举定义名字 + * + * @return 枚举定义名称 + */ + String getName(); + + /** + * 获取定义KEY + * + * @return 枚举定义键 + */ + String getKey(); + + /** + * 获取定义描述 + * + * @return 枚举定义描述 + */ + String getDesc(); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/IEnumExtraDataLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/IEnumExtraDataLoader.java new file mode 100644 index 00000000..5bc64972 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/ext/IEnumExtraDataLoader.java @@ -0,0 +1,25 @@ +package com.dstz.base.common.ext; + +import java.util.List; + +/** + * 枚举扩展数据加载 + * + * @author wacxhs + */ +public interface IEnumExtraDataLoader { + + /** + * 标记,用于区分具体枚举下的扩展 + * + * @return 扩展枚举类 + */ + Class tag(); + + /** + * 加载扩展枚举数据 + * + * @return 扩展枚举数据 + */ + List load(); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkScript.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkScript.java new file mode 100644 index 00000000..7d4b0342 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkScript.java @@ -0,0 +1,12 @@ +package com.dstz.base.common.freemark; + +/** + * Freemarker接口。
+ *

+ * 实现了该接口的spring bean 将会被注入到Freemarker引擎 + * + * @author lightning + */ +public interface IFreemarkScript { + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkerEngine.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkerEngine.java new file mode 100644 index 00000000..ffb5611c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/IFreemarkerEngine.java @@ -0,0 +1,27 @@ +package com.dstz.base.common.freemark; + +import java.io.IOException; + +import freemarker.template.TemplateException; +/** + *

+ * Freemarker接口 定义
+ * 作者:lightning
+ * 日期:2022-01-02
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public interface IFreemarkerEngine { + + /** + * 根据字符串模版解析出内容 + * + * @param templateSource 字符串模版。 + * @param model 环境参数。 + * @return 解析后的文本 + * @throws TemplateException + * @throws IOException + */ + String parseByString(String templateSource, Object model); + +} \ No newline at end of file diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/impl/FreemarkerEngine.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/impl/FreemarkerEngine.java new file mode 100644 index 00000000..4cbe187a --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/freemark/impl/FreemarkerEngine.java @@ -0,0 +1,75 @@ +package com.dstz.base.common.freemark.impl; + +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.freemark.IFreemarkScript; +import com.dstz.base.common.freemark.IFreemarkerEngine; +import com.dstz.base.common.property.PropertyEnum; +import freemarker.cache.StringTemplateLoader; +import freemarker.template.Configuration; +import freemarker.template.Template; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.io.StringWriter; +import java.util.Map; +import java.util.Map.Entry; + +/** + *
+ * Freemarker接口 定义
+ * 作者:lightning
+ * 日期:2022-01-02
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +@Component +public class FreemarkerEngine implements IFreemarkerEngine { + private Configuration formTemplateConfig; + protected Logger LOG = LoggerFactory.getLogger(getClass()); + + public Configuration getFormTemplateConfiguration() { + try { + if (formTemplateConfig == null) { + String templatePath = PropertyEnum.FORM_TEMPLATE_URL.getPropertyValue(String.class); + formTemplateConfig = new Configuration(); + formTemplateConfig.setDefaultEncoding("UTF-8"); + formTemplateConfig.setDirectoryForTemplateLoading(new File(templatePath)); + } + return formTemplateConfig; + } catch (Exception e) { + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR, e); + } + + } + + @Override + public String parseByString(String templateSource, Object model) { + if (model != null && model instanceof Map) { + //将所有表单生成器的实现类注入到模板引擎中 + Map scirptImpls = SpringUtil.getBeansOfType(IFreemarkScript.class); + for (Entry scriptMap : scirptImpls.entrySet()) { + ((Map) model).put(scriptMap.getKey(), scriptMap.getValue()); + } + } + + try { + Configuration cfg = new Configuration(); + StringTemplateLoader loader = new StringTemplateLoader(); + cfg.setTemplateLoader(loader); + cfg.setClassicCompatible(true); + loader.putTemplate("freemaker", templateSource); + Template template = cfg.getTemplate("freemaker"); + StringWriter writer = new StringWriter(); + template.process(model, writer); + return writer.toString(); + } catch (Exception e) { + LOG.error(String.format("freemaker模板【%s】解析失败:%s", templateSource, e.getMessage())); + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR.formatMessage("模板解析失败,可能原因为:{}", e.getMessage()), e); + } + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/IdentityConvert.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/IdentityConvert.java new file mode 100644 index 00000000..ff0f9476 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/IdentityConvert.java @@ -0,0 +1,69 @@ +package com.dstz.base.common.identityconvert; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import com.dstz.base.common.enums.IdentityType; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IUser; +import org.apache.commons.collections4.iterators.IteratorChain; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户转换 + * + * @author lightning + */ +@Component +public class IdentityConvert { + private static final Logger LOGGER = LoggerFactory.getLogger(IdentityConvert.class); + + @Autowired + GroupApi groupApi; + + @Autowired + UserApi userApi; + + public IUser convert2User(SysIdentity identity) { + List users = convert2Users(identity); + + if (CollectionUtil.isNotEmpty(users)) { + return users.get(0); + } + + return null; + + } + + public List convert2Users(SysIdentity identity) { + //如果为用户 + if (IdentityType.USER.getKey().equals(identity.getType())) { + IUser user = userApi.getByUserId(identity.getId()); + if (Objects.isNull(user)) { + LOGGER.error("identity convert 2 users error id[{}],name[{}] not found! ", identity.getId(), identity.getName()); + return new ArrayList<>(); + } + return CollUtil.newArrayList(user); + } + //目前其他均为组类型 + return CollUtil.newArrayList(userApi.getByGroupTypeAndGroupIds(identity.getType(), Collections.singleton(identity.getId()))); + } + + public Iterable convert2Users(List identities) { + // 按类型分组出id + Map> identityTypeGroupMap = identities.parallelStream().collect(Collectors.groupingBy(SysIdentity::getType, HashMap::new, Collectors.mapping(SysIdentity::getId, Collectors.toSet()))); + List> iteratorList = new ArrayList<>(identityTypeGroupMap.size()); + // 获取用户 + Optional.ofNullable(identityTypeGroupMap.remove(IdentityType.USER.getKey())).filter(CollUtil::isNotEmpty).map(userApi::getByUserIds).filter(IterUtil::isNotEmpty).ifPresent(iteratorList::add); + // 其他类型解析出用户 + CollUtil.forEach(identityTypeGroupMap, (k, v, index) -> Optional.ofNullable(userApi.getByGroupTypeAndGroupIds(k, v)).filter(CollUtil::isNotEmpty).ifPresent(iteratorList::add)); + return () -> new IteratorChain<>(iteratorList); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/SysIdentity.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/SysIdentity.java new file mode 100644 index 00000000..85bf641e --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/identityconvert/SysIdentity.java @@ -0,0 +1,115 @@ +package com.dstz.base.common.identityconvert; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.enums.IdentityType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; + +import java.io.Serializable; + +/** + * 描述:流程与组织挂接实体接口 type : user / 其他group 类型 + * + * @author jeff + */ +public class SysIdentity implements Serializable { + + private static final long serialVersionUID = 1L; + private String id; + private String name; + private String type; + + public SysIdentity() { + } + + public SysIdentity(String id, String name, String type) { + this.id = id; + this.name = name; + this.type = type; + } + + public SysIdentity(IUser user) { + this.id = user.getUserId(); + this.name = user.getFullName(); + this.type = IdentityType.USER.getKey(); + } + + public void setId(String id) { + this.id = id; + } + + public String getId() { + return this.id; + } + + public void setName(String name) { + this.name = name; + } + + public String getName() { + return this.name; + } + + public void setType(String type) { + this.type = type; + } + + public String getType() { + return this.type; + } + + public int hashCode() { + return this.id.hashCode() + this.type.hashCode(); + } + + public boolean equals(Object obj) { + if (!(obj instanceof SysIdentity)) { + return false; + } + + if (StrUtil.isEmpty(id) || StrUtil.isEmpty(name)) { + return false; + } + + SysIdentity identity = (SysIdentity) obj; + + if (this.type.equals(identity.getType()) && this.id.equals(identity.getId())) { + return true; + } + + return false; + } + + /** + * 从group创建SysIdentity实例 + * + * @param group group + * @return SysIdentity 实例 + */ + public static SysIdentity of(IGroup group) { + return new SysIdentity(group.getGroupId(), group.getGroupName(), group.getGroupType()); + } + + /** + *
+	 * 返回封装了type和name的显示用的信息
+	 * 
+ * @return + */ + public String getAssign() { + String str = "("; + if ("role".equals(type)) { + str += "角色"; + } else if ("org".equals(type)) { + str += "组织"; + } else if ("group".equals(type)) { + str += "小组"; + } else if ("post".equals(type)) { + str += "岗位"; + } else if ("user".equals(type)) { + str += "用户"; + } + return str + ")" + name; + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/idgen/IdGenerator.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/idgen/IdGenerator.java new file mode 100644 index 00000000..cc82cf1f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/idgen/IdGenerator.java @@ -0,0 +1,16 @@ +package com.dstz.base.common.idgen; + +/** + * ID生成器 + * + * @author wacxhs + */ +public interface IdGenerator { + + /** + * 唯一ID + * + * @return 唯一ID + */ + String nextId(); +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/OnlyFilterProvider.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/OnlyFilterProvider.java new file mode 100644 index 00000000..de65a1aa --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/OnlyFilterProvider.java @@ -0,0 +1,30 @@ +package com.dstz.base.common.jackson; + +import com.fasterxml.jackson.databind.ser.BeanPropertyFilter; +import com.fasterxml.jackson.databind.ser.FilterProvider; +import com.fasterxml.jackson.databind.ser.PropertyFilter; + +/** + * 属性过滤器 + * + * @author wacxhs + */ +public class OnlyFilterProvider extends FilterProvider { + + private final PropertyFilter propertyFilter; + + public OnlyFilterProvider(PropertyFilter propertyFilter) { + this.propertyFilter = propertyFilter; + } + + @Deprecated + @Override + public BeanPropertyFilter findFilter(Object filterId) { + throw new UnsupportedOperationException("Access to deprecated filters not supported"); + } + + @Override + public PropertyFilter findPropertyFilter(Object filterId, Object valueToFilter) { + return propertyFilter; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/PropertyFilterAdapter.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/PropertyFilterAdapter.java new file mode 100644 index 00000000..f3cfb6d0 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/jackson/PropertyFilterAdapter.java @@ -0,0 +1,37 @@ +package com.dstz.base.common.jackson; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.jsonFormatVisitors.JsonObjectFormatVisitor; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.ser.PropertyFilter; +import com.fasterxml.jackson.databind.ser.PropertyWriter; + +/** + * 过滤适配 + * + * @author wacxhs + */ +public abstract class PropertyFilterAdapter implements PropertyFilter { + + @Override + public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception { + + } + + @Override + public void serializeAsElement(Object elementValue, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception { + + } + + @Override + public void depositSchemaProperty(PropertyWriter writer, ObjectNode propertiesNode, SerializerProvider provider) throws JsonMappingException { + + } + + @Override + public void depositSchemaProperty(PropertyWriter writer, JsonObjectFormatVisitor objectVisitor, SerializerProvider provider) throws JsonMappingException { + + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/map/GetAttributeMapProxy.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/map/GetAttributeMapProxy.java new file mode 100644 index 00000000..7ddcc894 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/map/GetAttributeMapProxy.java @@ -0,0 +1,83 @@ +package com.dstz.base.common.map; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapProxy; +import cn.hutool.core.util.ReflectUtil; +import com.dstz.base.common.utils.CastUtils; + +import java.lang.reflect.Method; +import java.util.Map; +import java.util.Objects; +import java.util.function.Function; + +/** + * 获取属性MapProxy,用于代理 getAttribute(String key, Class<T> toClass); + * + * @author wacxhs + */ +public class GetAttributeMapProxy extends MapProxy { + + private static final long serialVersionUID = -7468987711284643178L; + + private final Function, Object> loadFunc; + + private volatile boolean lazyLoaded; + + public GetAttributeMapProxy(Map map) { + super(map); + this.lazyLoaded = true; + this.loadFunc = null; + } + + /** + * 构造 + * + * @param map 被代理的Map + */ + public GetAttributeMapProxy(Map map, Function, Object> loadFunc) { + super(map); + this.loadFunc = loadFunc; + } + + + @Override + public Object invoke(Object proxy, Method method, Object[] args) { + // 兼容Map + if (Map.class.isAssignableFrom(method.getDeclaringClass())) { + doLazyLoad(); + return ReflectUtil.invoke(this, method, args); + } + + Object value; + + // 获取属性方法 + if ("getAttrValue".equals(method.getName()) && method.getParameterCount() == 2) { + value = Convert.convert(CastUtils.>cast(args[1]), get(args[0])); + } else { + value = super.invoke(proxy, method, args); + } + + if (value == null && !lazyLoaded) { + doLazyLoad(); + // 加载完之后重新再获取一次值 + value = invoke(proxy, method, args); + } + + return value; + } + + private synchronized void doLazyLoad() { + if (lazyLoaded) { + return; + } + lazyLoaded = true; + Object value = loadFunc.apply(CastUtils.cast(this)); + if (Objects.nonNull(value)) { + if (value instanceof Map) { + super.putAll(CastUtils.cast(value)); + } else { + throw new UnsupportedOperationException("Lazy loading function only supports return value Map"); + } + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/org/SimpleUser.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/org/SimpleUser.java new file mode 100644 index 00000000..abf085b3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/org/SimpleUser.java @@ -0,0 +1,102 @@ +package com.dstz.base.common.org; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReflectUtil; +import com.dstz.org.api.model.IUser; + +import java.util.Map; + +/** + * 简单用户,用于支持系统中线程临时设置 + * + * @author wacxhs + */ +public class SimpleUser implements IUser { + + private static final long serialVersionUID = 2166641057456880889L; + + /** + * 用户ID + */ + private String userId; + + /** + * 用户名 + */ + private String username; + + /** + * 用户名称 + */ + private String fullName; + + /** + * 属性 + */ + private Map attributes; + + @Override + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @Override + public String getUsername() { + return username; + } + + public void setUsername(String username) { + this.username = username; + } + + @Override + public String getFullName() { + return fullName; + } + + public void setFullName(String fullName) { + this.fullName = fullName; + } + + public Map getAttributes() { + return attributes; + } + + public void setAttributes(Map attributes) { + this.attributes = attributes; + } + + @Override + public T getAttrValue(String attrName, Class tClass) { + Object attrValue = ReflectUtil.getFieldValue(this, attrName); + if (attrValue != null) { + return Convert.convert(tClass, attrValue); + } + return MapUtil.get(attributes, attrName, tClass); + } + + public SimpleUser withUserId(String userId) { + this.userId = userId; + return this; + } + + public SimpleUser withUsername(String username) { + this.username = username; + return this; + } + + public SimpleUser withFullName(String fullName) { + this.fullName = fullName; + return this; + } + + public SimpleUser withAttributes(Map attributes) { + this.attributes = attributes; + return this; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/IBaseProperty.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/IBaseProperty.java new file mode 100644 index 00000000..03c887fb --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/IBaseProperty.java @@ -0,0 +1,61 @@ +package com.dstz.base.common.property; + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; + +import java.util.Objects; + +/** + * @author jinxia.hou + * @Name IProperty + * @description: 系统属性接口 + * @date 2022/2/2311:31 + */ +public interface IBaseProperty { + + + /** + * 获取属性key + * @param + * @return + */ + String getKey(); + + /** + * 获取属性描述 + * @return + */ + String getDesc(); + + /** + * 获取默认值 + * @return + */ + Object getDefaultValue(); + + /** + * 获取Yaml文件配置属性值 + * + * @param typeClass 属性值类型 + * @param 属性类 + * @return 属性值 + */ + default T getYamlValue(Class typeClass) { + Object value = ObjectUtil.defaultIfNull(SpringUtil.getProperty(getKey()), getDefaultValue()); + return Objects.isNull(value) ? null : Convert.convert(typeClass, value); + } + + /** + * 获取系统属性值 + * + * @param typeClass 属性类型 + * @param 属性类 + * @return 属性值 + */ + default T getPropertyValue(Class typeClass) { + SysPropertyService sysPropertyService = SpringUtil.getBean(SysPropertyService.class); + Object value = ObjectUtil.defaultIfNull(sysPropertyService.getValByCode(getKey()), getDefaultValue()); + return Objects.isNull(value) ? null : Convert.convert(typeClass, value); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/PropertyEnum.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/PropertyEnum.java new file mode 100644 index 00000000..91dd4464 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/PropertyEnum.java @@ -0,0 +1,101 @@ +package com.dstz.base.common.property; + +/** + * @author jinxia.hou + * @Name PropertyEnum + * @description: 属性管理枚举 + * @date 2022/3/915:12 + */ +public enum PropertyEnum implements IBaseProperty { + + ADMIN_ACCOUNTS("admin.accounts", "admin", "admin账户"), + JDBC_TYPE("spring.jdbc.dbType", "", "数据库类型"), + FORM_DEF_BACKUP_PATH("formDefBackupPath", "", "表单备份地址"), + FORM_TEMPLATE_URL("formTemplateUrl", "src/main/resources/templates", "表单模板URL地址"), + + // 流程相关 + + TASK_REMOVE_MESSAGE_PUSH("task_remove_message_push", false, "任务删除消息推送"), + DO_TASK_SUCCESSIONAL("doTaskSuccessional", false, "任务后面节点还是自己任务时,连续处理"), + BPM_NOT_DEFAULT_BUTTONS("bpmNotDefaultButtons", "oppose,reject2Start,custMultiExecution,addSign,manualEnd", "流程非默认按钮"), + + // 上传相关 + UPLOADER_DEFAULT("uploader.default", "", "默认的文件上传器"), + MINIO_BUCKET_NAME("ab.minio.bucketName", "", "minio bucketName"), + UPLOADER_ORDINARY_PATH("uploader.ordinary.path", "", ""), + BATCH_INSERT_EXCL_DATA_NUM("batch_insert_excl_data_num", 80, "导入excl数据时单次批量插入行数"), + EXPORT_EXCL_MAX_NUM("export_excl_max_num", 50000, "导入excl最大行数"), + // 用户登录 + LOGIN_COUNT("loginCount", 5, "连续登录验证失败最大次数"), + LOGIN_FILED_LOCK_TIME_DESC("loginFailedlockTimeDesc", "24小时", "登录失败锁定账户的时间描述,请自行和缓存时间配置匹配,缓存配置24h,这里建议配置24小时,默认为24小时"), + PWD_LOSE_COUNT("pwdLose", 180, "修改密码下次到期时间"), + PWD_CHECK_RULE_KEY("checkingRuleKey", "^[A-Za-z0-9_!@#$%&*]{6,20}$", "密码规则"), + PWD_CHECK_RULE_TXT("checkingRuleTxt", "密码长度在6-20位之间由数字、字母组合", "密码规则提示"), + IS_OPEN_RESET_PWD_BY_EMAIL("isOpenResetPwdByEmail", false, "是否开启邮件找回密码功能"), + LOGIN_CAPTCHA_KEY("captchaSwitch", false, "用户登陆是否校验验证码"), + LOGIN_RESET_PWD("isResetPwd", false, "初始化密码是否必须重置后登录"), + CHANGE_PWD_iS_LOG_OUT("changePwdIsLogOut", false, "修改密码是否退出登录"), + CHANGE_PWD_iS_Exit_SYSTEM("changePwdIsExitSystem", false, "修改密码是否强制退出系统"), + + // 企业微信 + WX_QY_APP_SECRET("wxqy_appsecret", "", ""), + + //邮箱配置 + EMAIL_HOST("email_host", "", "email_host"), + EMAIL_PORT("email_port", "", "email_port"), + EMAIL_SSL("email_ssl", "", "email_ssl"), + EMAIL_NICKNAME("email_nickname", "", "email_nickname"), + EMAIL_ADDRESS("email_address", "", "email_address"), + EMAIL_PASSWORD("email_password", "", "email_password"), + + + IS_INTERFACE_AUTH("is_interface_auth", false, "是否开启接口级别鉴权"), + + /** + * 事务消息重试次数 + */ + TRXM_RETRY_TIMES("ab.trxm.retry-times", 3, "事务消息重试次数"), + + /** + * 百度SDK APPID + */ + BAIDU_SDK_APPID("baidu.sdk.app-id", "", "百度SDK APPID"), + + /** + * 百度SDK 密钥 + */ + BAIDU_SDK_SECRET("baidu.sdk.secret", "", "百度SDK 密钥"), + + + ONLINE_DOC_SERVICE_URL("online_doc_service_url","http://192.168.1.141:18080","在线文档服务地址"), + AGILEBPM_SERVICE_URL("agilebpm_service_url","http://192.168.1.6:8080","agilebpm服务地址"), + ; + + + private final String key; + + private final Object defaultValue; + + private final String desc; + + PropertyEnum(String key, Object defaultValue, String desc) { + this.key = key; + this.defaultValue = defaultValue; + this.desc = desc; + } + + @Override + public String getKey() { + return key; + } + + @Override + public String getDesc() { + return desc; + } + + @Override + public Object getDefaultValue() { + return defaultValue; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/SysPropertyService.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/SysPropertyService.java new file mode 100644 index 00000000..8940c74f --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/property/SysPropertyService.java @@ -0,0 +1,44 @@ +package com.dstz.base.common.property; + +/** + * @author jinxia.hou + * @Name SysPropertyService + * @description: 系统配置服务 + * @date 2022/2/1415:53 + */ +public interface SysPropertyService { + + /** + * 根据别名返回字符串参数值。 + * + * @param code 参数编码 + * @return 参数值 + */ + String getValByCode(String code); + + + /** + * 根据别名返回整型参数值。 + * + * @param code 参数编码 + * @return 参数值 + */ + Integer getIntByCode(String code); + + /** + * 根据别名返回长整型参数值。 + * + * @param code 参数编码 + * @return 参数值 + */ + Long getLongByCode(String code); + + /** + * 根据别名返回布尔参数值。 + * + * @param code 参数编码 + * @return 参数值 + */ + Boolean getBooleanByCode(String code); + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/queue/QueueFile.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/queue/QueueFile.java new file mode 100644 index 00000000..aedefa86 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/queue/QueueFile.java @@ -0,0 +1,657 @@ +package com.dstz.base.common.queue; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.io.*; +import java.nio.channels.FileChannel; +import java.nio.channels.FileLock; +import java.util.Objects; + +import static java.lang.Math.min; + +/** + * 基于文件队列, 线程安全 + * + * @author wacxhs + */ +public class QueueFile implements AutoCloseable { + + private static final Logger logger = LoggerFactory.getLogger(QueueFile.class); + + /** + * Initial file size in bytes. + *

+ * one file system block + *

+ */ + private static final int INITIAL_LENGTH = 4096; + + /** + * A block of nothing to write over old data. + */ + private static final byte[] ZEROES = new byte[INITIAL_LENGTH]; + + /** + * Length of header in bytes. + */ + static final int HEADER_LENGTH = 16; + + /** + * The underlying file. Uses a ring buffer to store entries. Designed so that + * a modification isn't committed or visible until we write the header. The + * header is much smaller than a segment. So long as the underlying file + * system supports atomic segment writes, changes to the queue are atomic. + * Storing the file length ensures we can recover from a failed expansion + * (i.e. if setting the file length succeeds but the process dies before the + * data can be copied). + *

+ *

+     *   Format:
+     *     Header              (16 bytes)
+     *     Element Ring Buffer (File Length - 16 bytes)
+     * 

+ * Header: + * File Length (4 bytes) + * Element Count (4 bytes) + * First Element Position (4 bytes, =0 if null) + * Last Element Position (4 bytes, =0 if null) + *

+ * Element: + * Length (4 bytes) + * Data (Length bytes) + *

+ *

+ * Visible for testing. + */ + final RandomAccessFile raf; + + /** + * file lock + */ + final FileLock fileLock; + + /** + * Cached file length. Always a power of 2. + */ + int fileLength; + + /** + * Number of elements. + */ + private int elementCount; + + /** + * Pointer to first (or eldest) element. + */ + private Element first; + + /** + * Pointer to last (or newest) element. + */ + private Element last; + + /** + * In-memory buffer. Big enough to hold the header. + */ + private final byte[] buffer = new byte[16]; + + /** + * Constructs a new queue backed by the given file. Only one {@code QueueFile} + * instance should access a given file at a time. + */ + public QueueFile(File file) throws IOException { + if (!file.exists()) { + Object[] arr = initialize(file); + raf = (RandomAccessFile) arr[0]; + this.fileLock = (FileLock) Objects.requireNonNull(arr[1], "未获取到文件锁"); + } else { + raf = open(file); + this.fileLock = Objects.requireNonNull(raf.getChannel().tryLock(), "未获取到文件锁"); + } + readHeader(); + } + + /** + * Stores int in buffer. The behavior is equivalent to calling {@link + * RandomAccessFile#writeInt}. + */ + private static void writeInt(byte[] buffer, int offset, int value) { + buffer[offset] = (byte) (value >> 24); + buffer[offset + 1] = (byte) (value >> 16); + buffer[offset + 2] = (byte) (value >> 8); + buffer[offset + 3] = (byte) value; + } + + /** + * Stores int values in buffer. The behavior is equivalent to calling {@link + * RandomAccessFile#writeInt} for each value. + */ + private static void writeInts(byte[] buffer, int... values) { + int offset = 0; + for (int value : values) { + writeInt(buffer, offset, value); + offset += 4; + } + } + + /** + * Reads an int from a byte[]. + */ + private static int readInt(byte[] buffer, int offset) { + return ((buffer[offset] & 0xff) << 24) + + ((buffer[offset + 1] & 0xff) << 16) + + ((buffer[offset + 2] & 0xff) << 8) + + (buffer[offset + 3] & 0xff); + } + + /** + * Reads the header. + */ + private void readHeader() throws IOException { + raf.seek(0); + raf.readFully(buffer); + fileLength = readInt(buffer, 0); + if (fileLength > raf.length()) { + throw new IOException("File is truncated. Expected length: " + fileLength + ", Actual length: " + raf.length()); + } else if (fileLength == 0) { + throw new IOException("File is corrupt; length stored in header is 0."); + } + elementCount = readInt(buffer, 4); + int firstOffset = readInt(buffer, 8); + int lastOffset = readInt(buffer, 12); + first = readElement(firstOffset); + last = readElement(lastOffset); + } + + /** + * Writes header atomically. The arguments contain the updated values. The + * class member fields should not have changed yet. This only updates the + * state in the file. It's up to the caller to update the class member + * variables *after* this call succeeds. Assumes segment writes are atomic in + * the underlying file system. + */ + private void writeHeader(int fileLength, int elementCount, int firstPosition, int lastPosition) throws IOException { + writeInts(buffer, fileLength, elementCount, firstPosition, lastPosition); + raf.seek(0); + raf.write(buffer); + } + + /** + * Returns the Element for the given offset. + */ + private Element readElement(int position) throws IOException { + if (position == 0) { + return Element.NULL; + } + ringRead(position, buffer, 0, Element.HEADER_LENGTH); + int length = readInt(buffer, 0); + return new Element(position, length); + } + + /** + * Atomically initializes a new file. + */ + private static Object[] initialize(File file) throws IOException { + RandomAccessFile raf = open(file); + FileLock fileLock = Objects.requireNonNull(raf.getChannel().tryLock(), "未获取到文件锁"); + raf.setLength(INITIAL_LENGTH); + raf.seek(0); + byte[] headerBuffer = new byte[16]; + writeInts(headerBuffer, INITIAL_LENGTH, 0, 0, 0); + raf.write(headerBuffer); + return new Object[]{raf, fileLock}; + } + + /** + * Opens a random access file that writes synchronously. + */ + private static RandomAccessFile open(File file) throws FileNotFoundException { + return new RandomAccessFile(file, "rwd"); + } + + /** + * Wraps the position if it exceeds the end of the file. + */ + private int wrapPosition(int position) { + return position < fileLength ? position + : HEADER_LENGTH + position - fileLength; + } + + /** + * Writes count bytes from buffer to position in file. Automatically wraps + * write if position is past the end of the file or if buffer overlaps it. + * + * @param position in file to write to + * @param buffer to write from + * @param count # of bytes to write + */ + private void ringWrite(int position, byte[] buffer, int offset, int count) throws IOException { + position = wrapPosition(position); + if (position + count <= fileLength) { + raf.seek(position); + raf.write(buffer, offset, count); + } else { + // The write overlaps the EOF. + // # of bytes to write before the EOF. + int beforeEof = fileLength - position; + raf.seek(position); + raf.write(buffer, offset, beforeEof); + raf.seek(HEADER_LENGTH); + raf.write(buffer, offset + beforeEof, count - beforeEof); + } + } + + private void ringErase(int position, int length) throws IOException { + while (length > 0) { + int chunk = min(length, ZEROES.length); + ringWrite(position, ZEROES, 0, chunk); + length -= chunk; + position += chunk; + } + } + + /** + * Reads count bytes into buffer from file. Wraps if necessary. + * + * @param position in file to read from + * @param buffer to read into + * @param count # of bytes to read + */ + private void ringRead(int position, byte[] buffer, int offset, int count) throws IOException { + position = wrapPosition(position); + if (position + count <= fileLength) { + raf.seek(position); + raf.readFully(buffer, offset, count); + } else { + // The read overlaps the EOF. + // # of bytes to read before the EOF. + int beforeEof = fileLength - position; + raf.seek(position); + raf.readFully(buffer, offset, beforeEof); + raf.seek(HEADER_LENGTH); + raf.readFully(buffer, offset + beforeEof, count - beforeEof); + } + } + + /** + * Adds an element to the end of the queue. + * + * @param data to copy bytes from + */ + public void add(byte[] data) throws IOException { + add(data, 0, data.length); + } + + /** + * Adds an element to the end of the queue. + * + * @param data to copy bytes from + * @param offset to start from in buffer + * @param count number of bytes to copy + * @throws IndexOutOfBoundsException if {@code offset < 0} or {@code count < 0}, or if {@code offset + count} is + * bigger than the length of {@code buffer}. + */ + public synchronized void add(byte[] data, int offset, int count) throws IOException { + nonNull(data, "buffer"); + if ((offset | count) < 0 || count > data.length - offset) { + throw new IndexOutOfBoundsException(); + } + + expandIfNecessary(count); + + // Insert a new element after the current last element. + boolean wasEmpty = isEmpty(); + int position = wasEmpty ? HEADER_LENGTH : wrapPosition(last.position + Element.HEADER_LENGTH + last.length); + Element newLast = new Element(position, count); + + // Write length. + writeInt(buffer, 0, count); + ringWrite(newLast.position, buffer, 0, Element.HEADER_LENGTH); + + // Write data. + ringWrite(newLast.position + Element.HEADER_LENGTH, data, offset, count); + + // Commit the addition. If wasEmpty, first == last. + int firstPosition = wasEmpty ? newLast.position : first.position; + writeHeader(fileLength, elementCount + 1, firstPosition, newLast.position); + last = newLast; + elementCount++; + // first element + if (wasEmpty) { + first = last; + } + } + + /** + * Returns the number of used bytes. + */ + private int usedBytes() { + if (elementCount == 0) { + return HEADER_LENGTH; + } + + if (last.position >= first.position) { + // Contiguous queue. + return (last.position - first.position) // all but last entry + + Element.HEADER_LENGTH + last.length // last entry + + HEADER_LENGTH; + } else { + // tail < head. The queue wraps. + return last.position // buffer front + header + + Element.HEADER_LENGTH + last.length // last entry + + fileLength - first.position; // buffer end + } + } + + /** + * Returns number of unused bytes. + */ + private int remainingBytes() { + return fileLength - usedBytes(); + } + + /** + * Returns true if this queue contains no entries. + */ + public synchronized boolean isEmpty() { + return elementCount == 0; + } + + /** + * If necessary, expands the file to accommodate an additional element of the + * given length. + * + * @param dataLength length of data being added + */ + private void expandIfNecessary(int dataLength) throws IOException { + int elementLength = Element.HEADER_LENGTH + dataLength; + int remainingBytes = remainingBytes(); + if (remainingBytes >= elementLength) { + return; + } + + // Expand. + int previousLength = fileLength; + int newLength; + // Double the length until we can fit the new data. + do { + remainingBytes += previousLength; + newLength = previousLength << 1; + previousLength = newLength; + } while (remainingBytes < elementLength); + + setLength(newLength); + + // Calculate the position of the tail end of the data in the ring buffer + int endOfLastElement = wrapPosition(last.position + Element.HEADER_LENGTH + last.length); + + // If the buffer is split, we need to make it contiguous + if (endOfLastElement <= first.position) { + FileChannel channel = raf.getChannel(); + channel.position(fileLength); // destination position + int count = endOfLastElement - HEADER_LENGTH; + if (channel.transferTo(HEADER_LENGTH, count, channel) != count) { + throw new AssertionError("Copied insufficient number of bytes!"); + } + ringErase(HEADER_LENGTH, count); + } + + // Commit the expansion. + if (last.position < first.position) { + int newLastPosition = fileLength + last.position - HEADER_LENGTH; + writeHeader(newLength, elementCount, first.position, newLastPosition); + last = new Element(newLastPosition, last.length); + } else { + writeHeader(newLength, elementCount, first.position, last.position); + } + + fileLength = newLength; + } + + /** + * Sets the length of the file. + */ + private void setLength(int newLength) throws IOException { + // Set new file length (considered metadata) and sync it to storage. + raf.setLength(newLength); + raf.getChannel().force(true); + } + + /** + * Reads the eldest element. Returns null if the queue is empty. + */ + public synchronized byte[] peek() throws IOException { + if (isEmpty()) { + return null; + } + int length = first.length; + byte[] data = new byte[length]; + ringRead(first.position + Element.HEADER_LENGTH, data, 0, length); + return data; + } + + /** + * Invokes the given reader once for each element in the queue, from eldest to + * most recently added. + */ + public synchronized void forEach(ElementReader reader) throws IOException { + int position = first.position; + for (int i = 0; i < elementCount; i++) { + Element current = readElement(position); + reader.read(new ElementInputStream(current), current.length); + position = wrapPosition(current.position + Element.HEADER_LENGTH + current.length); + } + } + + /** + * Returns t unless it's null. + * + * @throws NullPointerException if t is null + */ + private static T nonNull(T t, String name) { + if (t == null) { + throw new NullPointerException(name); + } + return t; + } + + /** + * Reads a single element. + */ + private final class ElementInputStream extends InputStream { + private int position; + private int remaining; + + private ElementInputStream(Element element) { + position = wrapPosition(element.position + Element.HEADER_LENGTH); + remaining = element.length; + } + + @Override + public int read(byte[] buffer, int offset, int length) throws IOException { + nonNull(buffer, "buffer"); + if ((offset | length) < 0 || length > buffer.length - offset) { + throw new ArrayIndexOutOfBoundsException(); + } + if (remaining > 0) { + if (length > remaining) { + length = remaining; + } + ringRead(position, buffer, offset, length); + position = wrapPosition(position + length); + remaining -= length; + return length; + } else { + return -1; + } + } + + @Override + public int read() throws IOException { + if (remaining == 0) { + return -1; + } + raf.seek(position); + int b = raf.read(); + position = wrapPosition(position + 1); + remaining--; + return b; + } + } + + /** + * Returns the number of elements in this queue. + */ + public synchronized int size() { + return elementCount; + } + + /** + * Removes the eldest element. + * + * @throws java.util.NoSuchElementException if the queue is empty + */ + public synchronized byte[] poll() throws IOException { + byte[] data = peek(); + if (Objects.isNull(data) || data.length == 0) { + return null; + } + if (elementCount == 1) { + clear(); + } else { + // assert elementCount > 1 + int firstTotalLength = Element.HEADER_LENGTH + first.length; + + ringErase(first.position, firstTotalLength); + + int newFirstPosition = wrapPosition(first.position + firstTotalLength); + ringRead(newFirstPosition, buffer, 0, Element.HEADER_LENGTH); + int length = readInt(buffer, 0); + writeHeader(fileLength, elementCount - 1, newFirstPosition, last.position); + elementCount--; + first = new Element(newFirstPosition, length); + } + return data; + } + + /** + * Clears this queue. Truncates the file to the initial size. + */ + public synchronized void clear() throws IOException { + raf.seek(0); + raf.write(ZEROES); + writeHeader(INITIAL_LENGTH, 0, 0, 0); + elementCount = 0; + first = Element.NULL; + last = Element.NULL; + if (fileLength > INITIAL_LENGTH) { + setLength(INITIAL_LENGTH); + } + fileLength = INITIAL_LENGTH; + } + + /** + * Closes the underlying file. + */ + @Override + public synchronized void close() throws IOException { + fileLock.close(); + raf.close(); + } + + @Override + public String toString() { + final StringBuilder builder = new StringBuilder(); + builder.append(getClass().getSimpleName()).append('['); + builder.append("fileLength=").append(fileLength); + builder.append(", size=").append(elementCount); + builder.append(", first=").append(first); + builder.append(", last=").append(last); + builder.append(", element lengths=["); + try { + forEach(new ElementReader() { + boolean first = true; + + @Override + public void read(InputStream in, int length) { + if (first) { + first = false; + } else { + builder.append(", "); + } + builder.append(length); + } + }); + } catch (IOException e) { + logger.warn("read error", e); + } + builder.append("]]"); + return builder.toString(); + } + + /** + * A pointer to an element. + */ + static class Element { + + /** + * Length of element header in bytes. + */ + static final int HEADER_LENGTH = 4; + + /** + * Null element. + */ + static final Element NULL = new Element(0, 0); + + /** + * Position in file. + */ + final int position; + + /** + * The length of the data. + */ + final int length; + + /** + * Constructs a new element. + * + * @param position within file + * @param length of data + */ + Element(int position, int length) { + this.position = position; + this.length = length; + } + + @Override + public String toString() { + return getClass().getSimpleName() + "[" + + "position = " + position + + ", length = " + length + "]"; + } + } + + /** + * Reads queue elements. Enables partial reads as opposed to reading all of + * the bytes into a byte[]. + */ + public interface ElementReader { + + /* + * TODO: Support remove() call from read(). + */ + + /** + * Called once per element. + * + * @param in stream of element data. Reads as many bytes as requested, + * unless fewer than the request number of bytes remains, in + * which case it reads all the remaining bytes. Not buffered. + * @param length of element data in bytes + */ + void read(InputStream in, int length) throws IOException; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLog.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLog.java new file mode 100644 index 00000000..8869b6ff --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLog.java @@ -0,0 +1,322 @@ +package com.dstz.base.common.requestlog; + +import cn.hutool.core.map.MapUtil; +import com.dstz.base.common.utils.JsonUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.springframework.data.annotation.Transient; + +import java.util.Date; +import java.util.Map; +import java.util.Objects; + +/** + * 请求日志 + * + * @author wacxhs + */ +public class AbRequestLog implements java.io.Serializable { + + private static final long serialVersionUID = -3755749914936860032L; + + /** + * 原始请求体 + */ + public static final String REQUEST_BODY_RAW = "requestBodyRaw"; + + /** + * trace id + */ + private String traceId; + + /** + * 操作用户ID + */ + private String userId; + + /** + * 操作用户名 + */ + private String username; + + /** + * 操作部门名称 + */ + private String groupName; + + /** + * 操作部门Id + */ + private String groupId; + + /** + * 客户端IP + */ + private String clientIp; + + /** + * 请求方法 + */ + private String requestMethod; + + /** + * 请求地址 + */ + private String url; + + /** + * 后端路径 + */ + private String pathPattern; + + /** + * 请求时间 + */ + private Date requestTime; + + /** + * 请求头 + */ + private Map requestHeaderMap; + + /** + * 请求参数 + */ + private Map requestParameterMap; + + /** + * 请求体 + */ + private Object requestBody; + + /** + * 响应体 + */ + private Object responseBody; + + /** + * 响应时间 + */ + private Date responseTime; + + /** + * 耗时毫秒 + */ + private Long durationMs; + + /** + * 异常信息 + */ + private Throwable exception; + + /** + * 绑定属性,用于前置事件属性绑定 + */ + @JsonIgnore + @Transient + private Map attributeMap; + + public String getTraceId() { + return traceId; + } + + public String getUserId() { + return userId; + } + + public String getUsername() { + return username; + } + + public String getGroupName() { + return groupName; + } + + public String getGroupId() { + return groupId; + } + + public String getClientIp() { + return clientIp; + } + + public String getRequestMethod() { + return requestMethod; + } + + public String getUrl() { + return url; + } + + public String getPathPattern() { + return pathPattern; + } + + public Date getRequestTime() { + return requestTime; + } + + public Map getRequestHeaderMap() { + return requestHeaderMap; + } + + public Map getRequestParameterMap() { + return requestParameterMap; + } + + public Object getRequestBody() { + return requestBody; + } + + public Object getResponseBody() { + return responseBody; + } + + public Date getResponseTime() { + return responseTime; + } + + public Long getDurationMs() { + return durationMs; + } + + public Throwable getException() { + return exception; + } + + /** + * 绑定属性 + * + * @param key 属性KEY + * @param value 属性值 + */ + public void bindAttribute(Object key, Object value) { + if (attributeMap == null) { + attributeMap = MapUtil.newHashMap(); + } + attributeMap.put(key, value); + } + + /** + * 获取属性值 + * + * @param key 属性KEY + * @return 属性值 + */ + public Object getAttribute(Object key) { + return Objects.isNull(attributeMap) ? null : attributeMap.get(key); + } + + @Override + public String toString() { + return ToStringBuilder.reflectionToString(this); + } + + /** + * 实例化Builder + * + * @return Builder + */ + public static Builder newBuilder() { + return new Builder(); + } + + + public static final class Builder { + + private final AbRequestLog abRequestLog = new AbRequestLog(); + + private Builder() { + } + + public Builder withTraceId(String traceId) { + this.abRequestLog.traceId = traceId; + return this; + } + + public Builder withUserId(String userId) { + this.abRequestLog.userId = userId; + return this; + } + + public Builder withUsername(String username) { + this.abRequestLog.username = username; + return this; + } + + public Builder withGroupId(String groupId) { + this.abRequestLog.groupId = groupId; + return this; + } + + public Builder withGroupName(String groupName) { + this.abRequestLog.groupName = groupName; + return this; + } + + public Builder withClientIp(String clientIp) { + this.abRequestLog.clientIp = clientIp; + return this; + } + + public Builder withRequestMethod(String requestMethod) { + this.abRequestLog.requestMethod = requestMethod; + return this; + } + + public Builder withUrl(String url) { + this.abRequestLog.url = url; + return this; + } + + + public Builder withPathPattern(String pathPattern) { + this.abRequestLog.pathPattern = pathPattern; + return this; + } + + public Builder withRequestTime(Date requestTime) { + this.abRequestLog.requestTime = requestTime; + return this; + } + + public Builder withRequestHeaderMap(Map requestHeaderMap) { + this.abRequestLog.requestHeaderMap = requestHeaderMap; + return this; + } + + public Builder withRequestParameterMap(Map requestParameterMap) { + this.abRequestLog.requestParameterMap = requestParameterMap; + return this; + } + + public Builder withRequestBody(Object requestBody) { + this.abRequestLog.requestBody = requestBody; + if(requestBody != null){ + // 复制请求体副本 + this.abRequestLog.bindAttribute(REQUEST_BODY_RAW, JsonUtils.parseObject(JsonUtils.toJSONString(requestBody), Object.class)); + } + return this; + } + + public Builder withResponseBody(Object responseBody) { + this.abRequestLog.responseBody = responseBody; + return this; + } + + public Builder withResponseTime(Date responseTime) { + this.abRequestLog.responseTime = responseTime; + this.abRequestLog.durationMs = (responseTime.getTime() - abRequestLog.getRequestTime().getTime()); + return this; + } + + public Builder withException(Throwable throwable) { + this.abRequestLog.exception = throwable; + return this; + } + + public AbRequestLog build() { + return abRequestLog; + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLogOutput.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLogOutput.java new file mode 100644 index 00000000..d445e454 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/AbRequestLogOutput.java @@ -0,0 +1,166 @@ +package com.dstz.base.common.requestlog; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.events.AbRequestLogEvent; +import com.dstz.base.common.jackson.OnlyFilterProvider; +import com.dstz.base.common.jackson.PropertyFilterAdapter; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.*; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.ser.PropertyWriter; +import com.fasterxml.jackson.databind.type.MapType; +import org.apache.commons.lang3.ObjectUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.lang3.time.DateFormatUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.EventListener; +import org.springframework.util.PatternMatchUtils; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Set; + +/** + * 请求日志输出 + * + * @author wacxhs + */ +public class AbRequestLogOutput { + + private static final Logger logger = LoggerFactory.getLogger(AbRequestLogOutput.class); + + /** + * 包含头信息名称 + */ + private final Set includeHeaderNames; + + /** + * 脱敏字段 + */ + private final Set sensitiveFields; + + /** + * 忽略地址 + */ + private final String[] excludeUrls; + + /** + * 启用输出 + */ + private final boolean enableOutput; + + /** + * object mapper + */ + private final ObjectMapper objectMapper; + + public AbRequestLogOutput(Set includeHeaderNames, Set sensitiveFields, String[] excludeUrls, boolean enableOutput) { + this.includeHeaderNames = includeHeaderNames; + this.sensitiveFields = sensitiveFields; + this.excludeUrls = excludeUrls; + this.enableOutput = enableOutput; + this.objectMapper = createObjectMapper(); + } + + private ObjectMapper createObjectMapper() { + ObjectMapper objectMapper = new ObjectMapper().findAndRegisterModules(); + objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + objectMapper.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + objectMapper.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); + objectMapper.setDateFormat(new SimpleDateFormat(DatePattern.NORM_DATETIME_MS_PATTERN)); + objectMapper.setFilterProvider(new OnlyFilterProvider(new PropertyFilterAdapter() { + @Override + public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception { + LogSensitiveField logSensitiveField = writer.getAnnotation(LogSensitiveField.class); + if (logSensitiveField != null || sensitiveFields.contains(writer.getName())) { + gen.writeStringField(writer.getName(), "***"); + } else { + writer.serializeAsField(pojo, gen, prov); + } + } + })); + // FilterId + objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer.withFilterId(ObjectUtils.NULL); + } + + @Override + public JsonSerializer modifyMapSerializer(SerializationConfig config, MapType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer.withFilterId(ObjectUtils.NULL); + } + })); + return objectMapper; + } + + + @EventListener(AbRequestLogEvent.class) + public void abRequestLogEventListener(AbRequestLogEvent abRequestLogEvent) { + // 输出日志启用和日志INFO都必须开启 + if (!(this.enableOutput && logger.isInfoEnabled())) { + return; + } + final AbRequestLog requestLog = abRequestLogEvent.getRequestLog(); + // exclude url + if (PatternMatchUtils.simpleMatch(excludeUrls, requestLog.getPathPattern())) { + return; + } + final boolean isResponse = AbRequestLogEvent.EventType.POST_PROCESS.equals(abRequestLogEvent.getEventType()); + StringBuilder append; + if (isResponse) { + append = new StringBuilder(StringUtils.center(" response log ", 30, "*")); + } else { + append = new StringBuilder(StringUtils.center(" request log ", 30, "*")); + } + append.append('\n').append("request url: ").append(requestLog.getUrl()); + append.append('\n').append("path pattern: ").append(requestLog.getPathPattern()); + append.append('\n').append("request method: ").append(requestLog.getRequestMethod()); + // 为空包含所有 + if (CollUtil.isEmpty(includeHeaderNames)) { + append.append('\n').append("request header: ").append(toJSONString(requestLog.getRequestHeaderMap())); + } else { + append.append('\n').append("request header: ").append(toJSONString(MapUtil.filter(requestLog.getRequestHeaderMap(), entry -> includeHeaderNames.contains(entry.getKey())))); + } + append.append('\n').append("request time: ").append(DateFormatUtils.format(requestLog.getRequestTime(), DatePattern.NORM_DATETIME_MS_PATTERN)); + append.append('\n').append("trace id: ").append(requestLog.getTraceId()); + append.append('\n').append("username: ").append(requestLog.getUsername()); + append.append('\n').append("client ip: ").append(requestLog.getClientIp()); + append.append('\n').append("request parameter: ").append(toJSONString(requestLog.getRequestParameterMap())); + append.append('\n').append("request body: ").append(toJSONString(requestLog.getRequestBody())); + + // response log + if (isResponse) { + append.append('\n').append("response body: ").append(toJSONString(requestLog.getResponseBody())); + append.append('\n').append("response time: ").append(DateFormatUtils.format(requestLog.getResponseTime(), DatePattern.NORM_DATETIME_MS_PATTERN)); + append.append('\n').append("exception: "); + if (requestLog.getException() != null) { + append.append(ExceptionUtil.stacktraceToString(requestLog.getException())); + } + append.append('\n').append("duration ms: ").append(requestLog.getDurationMs()); + } + logger.info("\n{}", append); + } + + + private String toJSONString(Object object) { + if (ObjectUtil.isEmpty(object)) { + return StrUtil.EMPTY; + } + try { + return objectMapper.writeValueAsString(object); + } catch (IOException e) { + logger.error(e.getMessage(), e); + } + return StrUtil.EMPTY; + } + + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/LogSensitiveField.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/LogSensitiveField.java new file mode 100644 index 00000000..98bfdd0c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/requestlog/LogSensitiveField.java @@ -0,0 +1,15 @@ +package com.dstz.base.common.requestlog; + +import java.lang.annotation.*; + +/** + * 日志脱敏字段 + * + * @author wacxhs + */ +@Target({ElementType.FIELD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface LogSensitiveField { + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConditionScript.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConditionScript.java new file mode 100644 index 00000000..1ebebde1 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConditionScript.java @@ -0,0 +1,90 @@ +package com.dstz.base.common.script; + +import cn.hutool.core.util.StrUtil; + +/** + *

+ * 前端通用条件脚本配置对象
+ * 
+ * + * @author aschs + * @date 2022年6月23日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public class ConditionScript implements java.io.Serializable{ + + private static final long serialVersionUID = 8232653926713818605L; + + /** + * 脚本类型 ScriptType + */ + private String type; + /** + * 手写脚本内容 + */ + private HandScript handScript; + /** + * 配置脚本内容 + */ + private ConfigScript configScript; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public HandScript getHandScript() { + return handScript; + } + + public void setHandScript(HandScript handScript) { + this.handScript = handScript; + } + + public ConfigScript getConfigScript() { + return configScript; + } + + public void setConfigScript(ConfigScript configScript) { + this.configScript = configScript; + } + + /** + *
+	 * 根据类型获取结果脚本
+	 * 
+ * @return + */ + public String getResultScript() { + if (ScriptType.HAND.equalsWithKey(this.type)) { + return this.handScript.getScript(); + } + if (ScriptType.CONFIG.equalsWithKey(this.type)) { + return this.configScript.getScript(); + } + return ""; + } + + /** + *
+	 * 根据类型获取结果描述
+	 * 
+ * @return + */ + public String getResultDesc() { + String desc = null; + if (ScriptType.HAND.equalsWithKey(this.type)) { + desc = this.handScript.getDesc(); + } + if (ScriptType.CONFIG.equalsWithKey(this.type)) { + desc = this.configScript.getDesc(); + } + if (StrUtil.isEmpty(desc)) { + return this.getResultScript(); + } + return desc; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConfigScript.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConfigScript.java new file mode 100644 index 00000000..9ca36d72 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ConfigScript.java @@ -0,0 +1,12 @@ +package com.dstz.base.common.script; +/** + *
+ * 配置脚本
+ * 
+ * @author aschs + * @date 2022年6月23日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public class ConfigScript extends HandScript{ + private static final long serialVersionUID = 2796492144115123313L; +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/HandScript.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/HandScript.java new file mode 100644 index 00000000..e40c4835 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/HandScript.java @@ -0,0 +1,40 @@ +package com.dstz.base.common.script; + +/** + *
+ * 手写脚本
+ * 
+ * + * @author aschs + * @date 2022年6月23日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public class HandScript implements java.io.Serializable{ + + private static final long serialVersionUID = -6112747578142353989L; + + /** + * 脚本内容 + */ + private String script; + /** + * 脚本的描述 + */ + private String desc; + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ScriptType.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ScriptType.java new file mode 100644 index 00000000..07d83810 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/ScriptType.java @@ -0,0 +1,64 @@ +package com.dstz.base.common.script; + +/** + *
+ * Script的类型枚举
+ * 
+ * @author aschs + * @date 2022年6月23日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public enum ScriptType { + CONFIG("config", "配置模式"), + HAND("hand", "手写模式"); + /** + * key + */ + private String key; + /** + * 描述 + */ + private String desc; + + private ScriptType(String key, String desc) { + this.key = key; + this.desc = desc; + } + + public String getKey() { + return key; + } + + public String getDesc() { + return desc; + } + + /** + *
+	 * 根据key来判断是否跟当前一致
+	 * 
+ * + * @param key + * @return + */ + public boolean equalsWithKey(String key) { + return this.key.equals(key); + } + + /** + *
+	 * 根据key获取
+	 * 
+ * + * @param key + * @return + */ + public static ScriptType getByKey(String key) { + for (ScriptType type : ScriptType.values()) { + if (key.equals(type.getKey())) { + return type; + } + } + return null; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/EmptyOrg.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/EmptyOrg.java new file mode 100644 index 00000000..f66936d4 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/EmptyOrg.java @@ -0,0 +1,40 @@ +package com.dstz.base.common.script.variables; + +import com.dstz.org.api.model.IGroup; + +public class EmptyOrg implements IGroup{ + + @Override + public String getGroupId() { + return ""; + } + + public String getId() { + return ""; + } + + @Override + public String getGroupName() { + return ""; + } + + @Override + public String getGroupType() { + return ""; + } + + @Override + public String getGroupCode() { + return ""; + } + + @Override + public T getAttrValue(String attrName, Class tClass) { + return null; + } + + @Override + public Integer getGroupLevel() { + return 0; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/RoleListVariable.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/RoleListVariable.java new file mode 100644 index 00000000..0374e349 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/RoleListVariable.java @@ -0,0 +1,48 @@ +package com.dstz.base.common.script.variables; + +import java.util.List; +import java.util.stream.Collectors; + +import com.dstz.org.api.model.IGroup; + +public class RoleListVariable { + + private List roleList ; + + public RoleListVariable(List roleList2) { + this.roleList = roleList2 ; + } + + public List getIdList() { + List roleList = getRoleList(); + return roleList.stream().map(IGroup::getGroupId).collect(Collectors.toList()); + } + + public List getCodeList() { + List roleList = getRoleList(); + return roleList.stream().map(IGroup::getGroupCode).collect(Collectors.toList()); + } + + /** + * 获取最大level + * @return + */ + public int getMaxLevel() { + List roleList = getRoleList(); + int level = 0; + for(IGroup group : roleList) { + Integer groupLevel = group.getAttrValue("level", Integer.class); + if(groupLevel != null && groupLevel > level ) { + level = groupLevel; + } + } + return level; + } + + public List getRoleList() { + return roleList; + } + public void setRoleList(List roleList) { + this.roleList = roleList; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/UserGroovyVariable.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/UserGroovyVariable.java new file mode 100644 index 00000000..cf3bae75 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/script/variables/UserGroovyVariable.java @@ -0,0 +1,150 @@ +package com.dstz.base.common.script.variables; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; + +import java.util.List; + +/** + * groovy 中 通过对象 懒加载 用户信息 + * 比如 脚本:return ( startUser.orgType.equals( "1" ) && startUser.username.equals( "" ) ) + * @author jeff + * + */ +public class UserGroovyVariable { + private IUser user; + private IGroup org; + private RoleListVariable role; + + // 没有设置时通过id加载 + private String orgId; + private String userId; + + + /** + * 有用户,组织信息需要懒加载 + * @param user + * @param orgId + */ + public UserGroovyVariable(IUser user, String orgId) { + this.user = user; + this.orgId = orgId; + } + /** + * 只有用户,组织取当前组织 + * @param user2 + */ + public UserGroovyVariable(IUser user2) { + this.user = user2; + } + + /** + * 用户,组织都只有id时 + * @param userId + * @param orgId + */ + public UserGroovyVariable(String userId, String orgId) { + this.userId = userId; + this.orgId = orgId; + } + + public String getId() { + return getUser().getUserId(); + } + + + private IUser getUser() { + if(user != null) return user; + + if(StrUtil.isNotBlank(userId)) { + UserApi userApi = SpringUtil.getBean(UserApi.class); + Assert.notNull(userApi, "请检查组织服务注入情况!"); + user = userApi.getByUserId(userId); + } + + return user; + } + + public String getUsername() { + return getUser().getUsername(); + } + + public IGroup getOrg() { + if(org != null)return org; + + // 指定ORG + if( StrUtil.isNotBlank(orgId)) { + GroupApi groupApi = SpringUtil.getBean(GroupApi.class); + Assert.notNull(groupApi, "请检查组织服务注入情况!"); + + org = groupApi.getByGroupId(GroupType.ORG.getType(), orgId); + return org; + } + + // 获取当前组织 + org = UserContextUtils.getGroup().orElse(null); + if(org == null) { + org = new EmptyOrg(); + } + return org; + } + + public RoleListVariable getRole() { + if(role == null) { + GroupApi groupApi = SpringUtil.getBean(GroupApi.class); + Assert.notNull(groupApi, "请检查组织服务注入情况!"); + List roleList = groupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), getUser().getUserId()); + this.role = new RoleListVariable(CastUtils.cast(roleList)); + } + + return role; + } + + + + /** + * 获取用户ID + * + * @return 用户ID + */ + String getUserId() { + return getUser().getUserId(); + } + /** + * 获取属性值 + * + * @param attrName 属性名称 + * @param tClass 属性类型 + * @param T + * @return 属性值 + */ + T getAttrValue(String attrName, Class tClass) { + return getUser().getAttrValue(attrName, tClass); + } + + + public String getOrgId() { + return getOrg().getGroupId(); + } + + + public String getOrgName() { + return getOrg().getGroupName(); + } + + public Integer getOrgType() { + return getOrg().getAttrValue("type", Integer.class); + } + + public String getOrgCode() { + return getOrg().getGroupCode(); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/AbGenericJackson2JsonRedisSerializer.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/AbGenericJackson2JsonRedisSerializer.java new file mode 100644 index 00000000..7d65b140 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/AbGenericJackson2JsonRedisSerializer.java @@ -0,0 +1,56 @@ +package com.dstz.base.common.serializer; + +import cn.hutool.core.util.ArrayUtil; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.SerializationException; + +/** + * 重写jackson序列化器包含类型,增强字节序列判断 + * + * @author wacxhs + */ +public class AbGenericJackson2JsonRedisSerializer extends GenericJackson2JsonRedisSerializer { + + /** + * 类型字节数组 + */ + private static final byte TYPE_BYTES = 0x1; + + /** + * 类型对象 + */ + private static final byte TYPE_OBJECT = 0x2; + + @Override + public byte[] serialize(Object source) throws SerializationException { + if (source == null) { + return super.serialize(source); + } + byte[] dataBytes; + byte dataType; + if (source instanceof byte[]) { + dataBytes = (byte[]) source; + dataType = TYPE_BYTES; + } else { + dataBytes = super.serialize(source); + dataType = TYPE_OBJECT; + } + // 增强数据类型 + byte[] dataBytes2 = new byte[dataBytes.length + 1]; + dataBytes2[0] = dataType; + System.arraycopy(dataBytes, 0, dataBytes2, 1, dataBytes.length); + return dataBytes2; + } + + @Override + public T deserialize(byte[] source, Class type) throws SerializationException { + if (ArrayUtil.isEmpty(source)) { + return null; + } + if (source[0] == TYPE_BYTES) { + return (T) ArrayUtil.sub(source, 1, source.length); + } else { + return super.deserialize(ArrayUtil.sub(source, 1, source.length), type); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonDeserializer.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonDeserializer.java new file mode 100644 index 00000000..115b534b --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonDeserializer.java @@ -0,0 +1,27 @@ +package com.dstz.base.common.serializer; + +import java.io.IOException; + +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.ObjectCodec; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectReader; + +/** + * 用于解决 Json 反序列化时对象 字段中存在数组或者对象 转成String 报错的问题 + * + * @author jeff + * + */ +public class RawJsonDeserializer extends JsonDeserializer { + + @Override + public String deserialize(JsonParser jp, DeserializationContext ctxt) + throws IOException, JsonProcessingException { + return jp.getCodec().readTree(jp).toString(); + } +} \ No newline at end of file diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonSerializer.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonSerializer.java new file mode 100644 index 00000000..37bde538 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/serializer/RawJsonSerializer.java @@ -0,0 +1,22 @@ +package com.dstz.base.common.serializer; + +import java.io.IOException; + +import com.dstz.base.common.utils.JsonUtils; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +/** + * 用于解决 字符串形式的对象字段 序列化成 JSON字段 + * + * @author jeff + * + */ +public class RawJsonSerializer extends JsonSerializer { + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeObject(JsonUtils.toJSONNode(value)); + } +} \ No newline at end of file diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/tree/MapTree.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/tree/MapTree.java new file mode 100644 index 00000000..6ce43439 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/tree/MapTree.java @@ -0,0 +1,84 @@ +package com.dstz.base.common.tree; + +import cn.hutool.core.lang.tree.TreeNodeConfig; +import cn.hutool.core.map.MapUtil; +import com.dstz.base.api.model.Tree; +import com.dstz.base.common.utils.CastUtils; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 以Map作为树实现 + * + * @author wacxhs + */ +public class MapTree extends HashMap implements Tree { + + private static final long serialVersionUID = -8739914867159343333L; + + private final TreeNodeConfig treeNodeConfig; + + public MapTree(TreeNodeConfig treeNodeConfig) { + this.treeNodeConfig = treeNodeConfig; + } + + public MapTree(int initialCapacity, TreeNodeConfig treeNodeConfig) { + super(initialCapacity); + this.treeNodeConfig = treeNodeConfig; + } + + public static MapTree buildDefaultConfigMapTree(String id, String parentId, String name, Map extra) { + return buildMapTree(TreeNodeConfig.DEFAULT_CONFIG, id, parentId, name, extra); + } + + public static MapTree buildMapTree(TreeNodeConfig treeNodeConfig, String id, String parentId, String name, Map extra) { + MapTree mapTree = new MapTree(treeNodeConfig); + mapTree.setId(id); + mapTree.setParentId(parentId); + mapTree.put(treeNodeConfig.getNameKey(), name); + if (MapUtil.isNotEmpty(extra)) { + mapTree.putAll(extra); + } + return mapTree; + } + + /** + * 设置ID + * + * @param id id + */ + public void setId(String id) { + put(treeNodeConfig.getIdKey(), id); + } + + @Override + public String getId() { + return MapUtil.getStr(this, treeNodeConfig.getIdKey()); + } + + /** + * 设置上级ID + * + * @param parentId 上级ID + */ + public void setParentId(String parentId) { + put(treeNodeConfig.getParentIdKey(), parentId); + } + + @Override + public String getParentId() { + return MapUtil.getStr(this, treeNodeConfig.getParentIdKey()); + } + + @Override + public List getChildren() { + return CastUtils.cast(get(treeNodeConfig.getChildrenKey())); + } + + @Override + public void setChildren(List list) { + put(treeNodeConfig.getChildrenKey(), list); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbFreemarkUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbFreemarkUtil.java new file mode 100644 index 00000000..d8328b5b --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbFreemarkUtil.java @@ -0,0 +1,43 @@ +package com.dstz.base.common.utils; + +import java.io.IOException; + +import com.dstz.base.common.freemark.IFreemarkerEngine; +import com.dstz.base.common.freemark.impl.FreemarkerEngine; + +import cn.hutool.extra.spring.SpringUtil; +import freemarker.template.TemplateException; + +/** + * Freemarker工具类 + * + * @author lightning + */ +public class AbFreemarkUtil { + + private static volatile IFreemarkerEngine freemarkerEngine; + + public static IFreemarkerEngine getFreemarkerEngine() { + if (freemarkerEngine == null) { + synchronized (AbFreemarkUtil.class) { + freemarkerEngine = SpringUtil.getBean(FreemarkerEngine.class); + } + } + return freemarkerEngine; + } + + /** + * 根据字符串模版解析出内容 + * + * @param templateSource 字符串模版。 + * @param model 环境参数。 + * @return 解析后的文本 + * @throws TemplateException + * @throws IOException + */ + public static String parseByString(String templateSource, Object model) { + return getFreemarkerEngine().parseByString(templateSource, model); + } + +} + diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRequestUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRequestUtils.java new file mode 100644 index 00000000..557dda07 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRequestUtils.java @@ -0,0 +1,58 @@ +package com.dstz.base.common.utils; + +import cn.hutool.extra.servlet.ServletUtil; +import org.springframework.web.context.request.RequestAttributes; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.util.Objects; + +/** + * 请求工具 + * + * @author wacxhs + */ +public class AbRequestUtils { + + private AbRequestUtils() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * 获取 HttpServletRequest + * + * @return HttpServletRequest + */ + public static HttpServletRequest getHttpServletRequest() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + return null; + } + return ((ServletRequestAttributes) requestAttributes).getRequest(); + } + + /** + * 获取 HttpServletResponse + * + * @return HttpServletResponse + */ + public static HttpServletResponse getHttpServletResponse() { + RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes(); + if (requestAttributes == null) { + return null; + } + return ((ServletRequestAttributes) requestAttributes).getResponse(); + } + + /** + * 获取当前ip + * @return ip + */ + public static String getRequestIp() { + String clientIp = ServletUtil.getClientIP(getHttpServletRequest()); + return "0:0:0:0:0:0:0:1".equals(clientIp) ? "127.0.0.1" : clientIp; + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRestTemplateUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRestTemplateUtil.java new file mode 100644 index 00000000..57adab64 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbRestTemplateUtil.java @@ -0,0 +1,117 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.beans.factory.NoSuchBeanDefinitionException; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.util.ClassUtils; +import org.springframework.web.client.RestTemplate; + +import java.util.Collection; +import java.util.Collections; +import java.util.Objects; + +/** + * http client + * + * @author wacxhs + */ +public class AbRestTemplateUtil { + + /** + * 微服务服务间调用协议 + */ + public static final String LB_PROTOCOL = "lb://"; + + /** + * HTTP服务调用协议 + */ + public static final String HTTP_PROTOCOL = "http://"; + + private static final class RestTemplateBeanHolder { + + /** + * 普通rest template + */ + static final RestTemplate REST_TEMPLATE; + + /** + * 微服务 + */ + static final RestTemplate SERVICE_REST_TEMPLATE; + + static Class getLoadBalancerInterceptorClass(){ + try { + return ClassUtils.forName("org.springframework.cloud.client.loadbalancer.LoadBalancerInterceptor", Thread.currentThread().getContextClassLoader()); + } catch (Exception ignored) { + return null; + } + } + + static { + Collection restTemplates = SpringUtil.getBeansOfType(RestTemplate.class).values(); + if (CollUtil.isEmpty(restTemplates)) { + throw new NoSuchBeanDefinitionException(RestTemplate.class); + } + final Class loadBalancerInterceptorClass = getLoadBalancerInterceptorClass(); + RestTemplate[] restTemplateArray = new RestTemplate[2]; + for (RestTemplate template : restTemplates) { + boolean existsLoadBalancerInterceptorFilter = !Objects.isNull(loadBalancerInterceptorClass) && ObjectUtil.defaultIfNull(template.getInterceptors(), Collections.emptyList()) + .stream() + .anyMatch(clientHttpRequestInterceptor -> ClassUtils.isAssignableValue(loadBalancerInterceptorClass, clientHttpRequestInterceptor)); + if (existsLoadBalancerInterceptorFilter) { + if (restTemplateArray[1] == null) { + restTemplateArray[1] = template; + } + } else { + if (restTemplateArray[0] == null) { + restTemplateArray[0] = template; + } + } + if (restTemplateArray[0] != null && restTemplateArray[1] != null) { + break; + } + } + REST_TEMPLATE = restTemplateArray[0]; + SERVICE_REST_TEMPLATE = restTemplateArray[1]; + } + } + + /** + * 获取rest template + * + * @param isService 是否微服务 + * @return rest template + */ + public static RestTemplate getRestTemplate(boolean isService) { + RestTemplate restTemplate = isService ? RestTemplateBeanHolder.SERVICE_REST_TEMPLATE : RestTemplateBeanHolder.REST_TEMPLATE; + Assert.notNull(restTemplate, () -> new NoSuchBeanDefinitionException(RestTemplate.class, "isService: " + isService)); + return restTemplate; + } + + /** + * 根据地址获取RestTemplate + * + * @param url 调用地址 + * @return rest template + */ + public static RestTemplate getRestTemplate(String url) { + return getRestTemplate(StrUtil.startWith(url, LB_PROTOCOL)); + } + + /** + * 根据URL识别出真实调用地址 + * + * @param url 原地址 + * @return 真实调用地址 + */ + public static String tellUrl(String url) { + if (StrUtil.startWith(url, LB_PROTOCOL)) { + return HTTP_PROTOCOL + StrUtil.removePrefix(LB_PROTOCOL, url); + } + return url; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbXmlCovertUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbXmlCovertUtil.java new file mode 100644 index 00000000..744e7bdd --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AbXmlCovertUtil.java @@ -0,0 +1,45 @@ +package com.dstz.base.common.utils; + +import javax.xml.bind.JAXBContext; +import javax.xml.bind.JAXBException; +import javax.xml.bind.Marshaller; +import javax.xml.bind.Unmarshaller; +import java.io.ByteArrayInputStream; +import java.io.InputStream; +import java.io.StringWriter; +import java.io.UnsupportedEncodingException; + +/** + * @author jinxia.hou + * @Name AbXmlCovertUtil + * @description: xml pojo转换工具类 + * @date 2022/8/1516:33 + */ +public class AbXmlCovertUtil { + /** + * XML转换为POJO类型 + */ + @SafeVarargs + public static T covert2Object(String xml,Class... classes) throws JAXBException, UnsupportedEncodingException { + JAXBContext jAXBContext = JAXBContext.newInstance(classes); + + Unmarshaller unmarshaller = jAXBContext.createUnmarshaller(); + InputStream is = new ByteArrayInputStream(xml.getBytes("utf-8")); + return (T)unmarshaller.unmarshal(is); + } + + /** + * POJO类型转换为XML + */ + public static String covert2Xml(Object serObj) throws JAXBException { + JAXBContext jc = JAXBContext.newInstance(serObj.getClass()); + + StringWriter out = new StringWriter(); + Marshaller m = jc.createMarshaller(); + m.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, true); + m.setProperty(Marshaller.JAXB_ENCODING, "utf-8"); + m.marshal(serObj, out); + String tmp = out.toString(); + return tmp; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AuditLogVariableUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AuditLogVariableUtil.java new file mode 100644 index 00000000..62c160ff --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/AuditLogVariableUtil.java @@ -0,0 +1,63 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.map.MapUtil; +import com.dstz.base.common.requestlog.AbRequestLog; + +import javax.servlet.http.HttpServletRequest; +import java.util.Collections; +import java.util.Map; + +/** + * 审计日志变量工具 + * + * @author wacxhs + */ +public class AuditLogVariableUtil { + + + private static Map getVariableMap(HttpServletRequest request) { + if (request == null) { + return null; + } + // 从线程中获取 + AbRequestLog abRequestLog = (AbRequestLog) request.getAttribute(AbRequestLog.class.getName()); + if (abRequestLog == null) { + return null; + } + Map variableMap = CastUtils.cast(abRequestLog.getAttribute(AuditLogVariableUtil.class)); + if (variableMap == null) { + abRequestLog.bindAttribute(AuditLogVariableUtil.class, variableMap = MapUtil.newHashMap()); + } + return variableMap; + } + + /** + * 从请求日志中获取到变量Map + * + * @param abRequestLog 请求日志 + * @return 变量Map + */ + public static Map getVariableMap(AbRequestLog abRequestLog) { + if (abRequestLog == null) { + return Collections.emptyMap(); + } + Object attribute = abRequestLog.getAttribute(AuditLogVariableUtil.class); + if (attribute == null) { + return Collections.emptyMap(); + } + return CastUtils.cast(attribute); + } + + /** + * 设置变量 + * + * @param name 变量名 + * @param value 变量值 + */ + public static void setVariable(String name, Object value) { + Map variableMap = getVariableMap(AbRequestUtils.getHttpServletRequest()); + if (variableMap != null) { + variableMap.put(name, value); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanConversionUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanConversionUtils.java new file mode 100644 index 00000000..17e9d323 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanConversionUtils.java @@ -0,0 +1,46 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import com.dstz.base.api.model.Tree; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * bean 转换工具类 + * + * @author jeff + * @since 2022-01-24 + */ +public class BeanConversionUtils { + + /** + * @描述 list数据转Tree,大多使用在前台json中。 + * @说明 实现接口 Tree即可 + * @扩展 可通过反射获取id, pid,目前只提供Tree接口排序的实现 + * @author jeff + */ + @SuppressWarnings({ "rawtypes", "unchecked" }) + public static List listToTree(List list) { + if (CollUtil.isEmpty(list)) { + return new ArrayList<>(); + } + Map idToTreeMap = CollUtil.toMap(list, MapUtil.newHashMap(list.size()), Tree::getId); + List rootList = new ArrayList<>(); + for (T item : list) { + T parent = idToTreeMap.get(item.getParentId()); + if (Objects.nonNull(parent)) { + if (parent.getChildren() == null) { + parent.setChildren(new ArrayList()); + } + parent.getChildren().add(item); + } else { + rootList.add(item); + } + } + return rootList; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanCopierUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanCopierUtils.java new file mode 100644 index 00000000..8eff8329 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/BeanCopierUtils.java @@ -0,0 +1,141 @@ +package com.dstz.base.common.utils; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.aop.support.AopUtils; +import org.springframework.cglib.beans.BeanCopier; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ReflectUtil; + +/** + * bean 拷贝 + * + * @author jeff + * @since 2022-01-24 + */ +public class BeanCopierUtils { + + public static final ConcurrentHashMap BEAN_COPIER_MAP = new ConcurrentHashMap<>(); + + private BeanCopierUtils() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * 属性拷贝 + * + * @param source 源对象 + * @param target 目标对象 + */ + public static Object copyProperties(Object source, Object target) { + if (source != null && target != null) { + getBeanCopier(ClassUtil.getClass(source), ClassUtil.getClass(target)).copy(source, target, null); + } + return target; + } + + /** + * 将一个类转换成另外一个类,可用于copy复制对象场景 + * + * @param source 源对象 + * @param clazz 目标类 + * @return 目标对象 + */ + public static T transformBean(Object source, Class clazz) { + if (source == null) { + return null; + } + T target = ReflectUtil.newInstance(clazz); + copyProperties(source, target); + return target; + } + + /** + * 将列表转换为另一个类对象实例列表。 + * + * @param sourceList 源数据列表 + * @param targetClass 目标类 + * @param T + * @return 目标对象列表 + */ + public static List transformList(Collection sourceList, Class targetClass) { + if (CollUtil.isEmpty(sourceList)) { + return new ArrayList<>(); + } + BeanCopier beanCopier = getBeanCopier(ClassUtil.getClass(CollUtil.getFirst(sourceList)), targetClass); + List list = new ArrayList<>(sourceList.size()); + for (Object sourceObject : sourceList) { + T targetObject = ReflectUtil.newInstance(targetClass); + beanCopier.copy(sourceObject, targetObject, null); + list.add(targetObject); + } + return list; + } + + /** + * 将bean转换为Map + * + * @param bean bean + * @param editable 限制的类或接口,必须为目标对象的实现接口或父类,用于限制拷贝的属性,例如一个类我只想复制其父类的一些属性,就可以将editable设置为父 + * @return map存放的键值数据 + */ + public static Map transformMap(Object bean, Class editable) { + PropertyDescriptor[] propertyDescriptors; + if(editable != null) { + propertyDescriptors = BeanUtil.getPropertyDescriptors(editable); + } else { + Class targetClass = AopUtils.getTargetClass(bean); + propertyDescriptors = BeanUtil.getPropertyDescriptors(targetClass); + } + if (ArrayUtil.isEmpty(propertyDescriptors)) { + return new HashMap<>(0); + } + Map dataMap = MapUtil.newHashMap(propertyDescriptors.length); + for (PropertyDescriptor propertyDescriptor : propertyDescriptors) { + Method readMethod = propertyDescriptor.getReadMethod(); + if (readMethod != null) { + dataMap.put(propertyDescriptor.getName(), ReflectUtil.invoke(bean, readMethod)); + } + } + return dataMap; + } + + private static BeanCopier getBeanCopier(Class sourceClass, Class targetClass) { + final String beanKey = sourceClass.getName() + "->" + targetClass.getName(); + return BEAN_COPIER_MAP.computeIfAbsent(beanKey, k -> { + AbBeanCopierGenerator generator = new AbBeanCopierGenerator(); + generator.setSource(sourceClass); + generator.setTarget(targetClass); + generator.setUseConverter(false); + return generator.create(); + }); + } + + /** + * 重写BeanCopier,兼容高版本JDK + *

+ * 重写原因:
+ * 1. JDK 16版本新加了ClassLoader保护,不在允许通过反射调用。
+ * 2. 设置生成名称与BeanCopier在同一包名下
+ * JEP规范:https://openjdk.java.net/jeps/396 + *

+ */ + private static class AbBeanCopierGenerator extends BeanCopier.Generator { + public AbBeanCopierGenerator() { + setContextClass(BeanCopier.class); + setNamePrefix(BeanCopier.class.getName()); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/CastUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/CastUtils.java new file mode 100644 index 00000000..168c4ac0 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/CastUtils.java @@ -0,0 +1,15 @@ +package com.dstz.base.common.utils; + +/** + * 类型转换,常用于消除列表字典转换警告 + * + * @author wacxhs + */ +public class CastUtils { + + @SuppressWarnings("unchecked") + public static T cast(Object object) { + return (T) object; + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ConstantUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ConstantUtil.java new file mode 100644 index 00000000..b640ba3d --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ConstantUtil.java @@ -0,0 +1,66 @@ +package com.dstz.base.common.utils; + +import com.fasterxml.jackson.databind.JsonNode; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.HashMap; +import java.util.Map; + +/** + * 常量的工具类 + * + * @author aschs + */ +public class ConstantUtil { + private ConstantUtil() { + + } + + /** + *
+     * 获取指定类下的某个静态常量
+     * ps:那个key 一定是final static 的才能被获取
+     * 
+ * + * @param classPath + * @param key + * @return 返回json:{key:"字段名",value:"字段值",type:"字段的类型"} + */ + public static JsonNode get(String classPath, String key) { + JsonNode json = get(classPath).get(key); + if (json != null) { + return json; + } + throw new RuntimeException("类中" + classPath + "获取不到常量[" + key + "]"); + } + + /** + *
+     * 获取指定类下的某个静态常量下所有final static的常量
+     * 
+ * + * @param classPath + * @return 返回map:{key:{key:"字段名",value:"字段值",type:"字段的类型"},key1:{key:"字段名1",value:"字段值1",type:"字段的类型1"}} + */ + public static Map get(String classPath) { + try { + Map map = new HashMap<>(); + Class clazz = Class.forName(classPath); + Field[] fileds = clazz.getFields(); + for (Field f : fileds) { + if (Modifier.isPublic(f.getModifiers()) && Modifier.isFinal(f.getModifiers()) && Modifier.isStatic(f.getModifiers())) { + Map json=new HashMap<>(); + json.put("key", f.getName()); + json.put("value", f.get(clazz)); + json.put("type", f.getType()); + map.put(f.getName(), JsonUtils.toJSONNode(json)); + } + } + return map; + } catch (Exception e) { + throw new RuntimeException(e); + } + } +} + diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextCleanUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextCleanUtils.java new file mode 100644 index 00000000..d86bfe75 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextCleanUtils.java @@ -0,0 +1,70 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ArrayUtil; +import org.springframework.util.Assert; + +import java.util.*; + +/** + * 上下文清理工具类 + * + * @author wacxhs + */ +public class ContextCleanUtils { + + private ContextCleanUtils() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * 时机 + */ + public enum Phase { + + /** + * 线程运行前后 + */ + THREAD, + + /** + * web请求后 + */ + REQUEST_COMPLETE + } + + private final static Map> CONTEXT_CLEAN_REGISTRY = MapUtil.newHashMap(); + + /** + * 注册清理方法 + * + * @param runnable 清理运行方法 + */ + public static void register(Runnable runnable, Phase... phases) { + Assert.state(ArrayUtil.isNotEmpty(phases), "phases parameter must"); + for (Phase phase : phases) { + CONTEXT_CLEAN_REGISTRY.computeIfAbsent(phase, k -> new LinkedList<>()).add(runnable); + } + } + + /** + * 执行清理,根据时机调用 + * + * @param phases 执行清理 + */ + public static void execute(Phase... phases) { + Assert.state(phases != null, "phases parameter must"); + // 调用一次 + HashSet onceHashSet = new HashSet<>(); + for (Phase phase : phases) { + CONTEXT_CLEAN_REGISTRY.getOrDefault(phase, Collections.emptyList()).stream().filter(onceHashSet::add).forEach(Runnable::run); + } + } + + /** + * 执行清理所有 + */ + public static void executeAll() { + execute(Phase.values()); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextDuplicationUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextDuplicationUtils.java new file mode 100644 index 00000000..fc31d0dc --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ContextDuplicationUtils.java @@ -0,0 +1,48 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.context.ContextDuplication; +import org.springframework.core.annotation.AnnotationAwareOrderComparator; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 上下文复制工具类 + * + * @author wacxhs + */ +public class ContextDuplicationUtils { + + private interface BeanHolder { + List CONTEXT_DUPLICATIONS = Arrays.stream(SpringUtil.getBeanNamesForType(ContextDuplication.class)) + .map(beanName -> SpringUtil.getBean(beanName, ContextDuplication.class)) + .sorted(AnnotationAwareOrderComparator.INSTANCE) + .collect(Collectors.toList()); + } + + /** + * 从上下文中复制出对象 + * + * @return 复制对象 + */ + public static Object[] duplicateObjects() { + Object[] duplicateObjects = new Object[BeanHolder.CONTEXT_DUPLICATIONS.size()]; + CollUtil.forEach(BeanHolder.CONTEXT_DUPLICATIONS, (contextDuplication, index) -> duplicateObjects[index] = contextDuplication.duplicate()); + return duplicateObjects; + } + + /** + * 填充复制对象 + * + * @param duplicateObjects 复制对象集 + */ + public static void fillDuplicateObjects(Object[] duplicateObjects) { + if (ArrayUtil.isNotEmpty(duplicateObjects)) { + CollUtil.forEach(BeanHolder.CONTEXT_DUPLICATIONS, (contextDuplication, index) -> contextDuplication.fill(duplicateObjects[index])); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/EnumUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/EnumUtil.java new file mode 100644 index 00000000..9fe173e3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/EnumUtil.java @@ -0,0 +1,168 @@ +package com.dstz.base.common.utils; + +import java.lang.reflect.Field; +import java.lang.reflect.Method; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import org.springframework.context.ApplicationContext; + +import com.dstz.base.common.ext.EnumExtraData; +import com.dstz.base.common.ext.IEnumExtraDataLoader; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; + +import cn.hutool.extra.spring.SpringUtil; + +/** + * 枚举的工具类 + * + * @author aschs + */ +public class EnumUtil { + /** + * 防止被实例化 + */ + private EnumUtil() { + + } + + /** + *
+     * 把一个枚举类转成JSON,主要是为了方便前端直接调用(以下是jsp的用法)
+     * 前端调用例子:
+     * <%@page import="com.dstz.sys.persistence.enums.FieldControlType"%>
+     * <%@page import="com.dstz.base.core.util.EnumUtil"%>
+     * 
+     * 系统内置的异步获取类:toolsControllerUtil.js
+     * 
+ * + * @param enumClass + * @return + */ + public static JsonNode toJSON(Class> enumClass) { + try { + Method method = enumClass.getMethod("values"); + Enum[] enums = (Enum[]) method.invoke(enumClass, null); + Map result = new HashMap<>(); + for (Enum e : enums) { + result.put(e.name(), toJSON(e)); + } + loadEnumExtraData(enumClass, enumExtraData -> { + result.put(enumExtraData.getName(), JsonUtils.toJSONNode(enumExtraData)); + }); + return JsonUtils.toJSONNode(result); + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + public static ArrayNode toJSONArray(Class> enumClass) { + try { + Method method = enumClass.getMethod("values"); + Enum[] enums = (Enum[]) method.invoke(enumClass, null); + ArrayNode result = JsonNodeFactory.instance.arrayNode(); + for (Enum e : enums) { + result.add(toJSON(e)); + } + loadEnumExtraData(enumClass, enumExtraData -> { + result.add(JsonUtils.toJSONNode(enumExtraData)); + }); + return result; + } catch (Exception e) { + throw new RuntimeException(e); + } + } + + /** + *
+     * 把一个枚举类的路径转成json数组
+     * 注意!!如果枚举在类中间,那么路径如下:com.dstz.base.db.model.Column$TYPE
+     * 
+ * + * @param enumClassPath 枚举路径 + * @return + * @throws Exception + */ + public static ArrayNode toJSONArray(String enumClassPath) { + try { + return toJSONArray((Class>) Class.forName(enumClassPath)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + public static JsonNode toJSON(String enumClassPath) { + try { + return toJSON((Class>) Class.forName(enumClassPath)); + } catch (ClassNotFoundException e) { + throw new RuntimeException(e); + } + } + + /** + *
+     * 把一个枚举实例转成JSON,主要是为了方便前端直接调用
+     * 
+ * + * @param e + * @return + * @throws Exception + */ + private static JsonNode toJSON(Enum e) throws Exception { + Map result = new HashMap<>(); + Field[] fields = e.getClass().getDeclaredFields(); + for (Field field : fields) { + // 让私有变量也能被访问到 + field.setAccessible(true); + // 过滤掉自身的枚举(我都想不到枚举实例为什么也算是类的字段)和枚举类必有的全部变量的ENUM$VALUES字段 + if ("ENUM$VALUES".equals(field.getName()) || field.getType().equals(e.getClass())) { + continue; + } + Object obj = field.get(e); + if (obj instanceof Enum) {// 如果类型是Enum,那刚好继续解释多一次 + result.put(field.getName(), toJSON((Enum) field.get(e))); + } else { + result.put(field.getName(), field.get(e)); + } + + } + return JsonUtils.toJSONNode(result); + } + + /** + * 加载枚举额外数据 + * + * @param clazz 枚举类 + * @param consumer 处理数据 + */ + private static void loadEnumExtraData(Class clazz, Consumer consumer) { + ApplicationContext applicationContext = SpringUtil.getApplicationContext(); + if (applicationContext == null) { + return; + } + Map beanMap = applicationContext.getBeansOfType(IEnumExtraDataLoader.class); + if (beanMap != null && !beanMap.isEmpty()) { + for (IEnumExtraDataLoader enumExtraDataLoader : beanMap.values()) { + if (!clazz.equals(enumExtraDataLoader.tag())) { + continue; + } + List enumExtraDataList = enumExtraDataLoader.load(); + if (enumExtraDataList != null && !enumExtraDataList.isEmpty()) { + for (EnumExtraData enumExtraData : enumExtraDataList) { + consumer.accept(enumExtraData); + } + } + } + } + } + + public static void main(String[] args) throws Exception { + System.out.println(EnumUtil.toJSON("com.dstz.bus.api.constant.BusinessObjectPersistenceType")); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/HanYuPinyinUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/HanYuPinyinUtil.java new file mode 100644 index 00000000..b014292d --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/HanYuPinyinUtil.java @@ -0,0 +1,136 @@ +package com.dstz.base.common.utils; + +import net.sourceforge.pinyin4j.PinyinHelper; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; +import net.sourceforge.pinyin4j.format.HanyuPinyinOutputFormat; +import net.sourceforge.pinyin4j.format.HanyuPinyinToneType; +import net.sourceforge.pinyin4j.format.HanyuPinyinVCharType; +import net.sourceforge.pinyin4j.format.exception.BadHanyuPinyinOutputFormatCombination; + +/** + * @author jinxia.hou + * @Name HanyuPinyinUtil + * @description: + * @date 2022/2/1415:15 + */ +public class HanYuPinyinUtil { + + /** + * 将文字转为汉语拼音 + * + * @param ChineseLanguage 要转成拼音的中文 + */ + public String toHanYuPinyin(String ChineseLanguage) { + char[] cl_chars = ChineseLanguage.trim().toCharArray(); + String hanyupinyin = ""; + HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); + defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部小写 + defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调 + defaultFormat.setVCharType(HanyuPinyinVCharType.WITH_V); + try { + for (int i = 0; i < cl_chars.length; i++) { + if (String.valueOf(cl_chars[i]).matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音 + hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0]; + } else {// 如果字符不是中文,则不转换 + hanyupinyin += cl_chars[i]; + } + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + System.out.println("字符不能转成汉语拼音"); + } + return hanyupinyin; + } + + public static String getFirstLettersUp(String ChineseLanguage) { + return getFirstLetters(ChineseLanguage, HanyuPinyinCaseType.UPPERCASE); + } + + public static String getFirstLettersLo(String ChineseLanguage) { + return getFirstLetters(ChineseLanguage, HanyuPinyinCaseType.LOWERCASE); + } + + public static String getFirstLetters(String ChineseLanguage, HanyuPinyinCaseType caseType) { + char[] cl_chars = ChineseLanguage.trim().toCharArray(); + String hanyupinyin = ""; + HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); + defaultFormat.setCaseType(caseType);// 输出拼音全部大写 + defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调 + try { + for (int i = 0; i < cl_chars.length; i++) { + String str = String.valueOf(cl_chars[i]); + if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母 + hanyupinyin += PinyinHelper.toHanyuPinyinStringArray(cl_chars[i], defaultFormat)[0].substring(0, 1); + } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字 + hanyupinyin += cl_chars[i]; + } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母 + hanyupinyin += cl_chars[i]; + } else {// 否则不转换 + hanyupinyin += cl_chars[i];//如果是标点符号的话,带着 + } + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + System.out.println("字符不能转成汉语拼音"); + } + return hanyupinyin; + } + + public static String getPinyinString(String ChineseLanguage) { + char[] cl_chars = ChineseLanguage.trim().toCharArray(); + String hanyupinyin = ""; + HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); + defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE);// 输出拼音全部大写 + defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调 + try { + for (int i = 0; i < cl_chars.length; i++) { + String str = String.valueOf(cl_chars[i]); + if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母 + hanyupinyin += PinyinHelper.toHanyuPinyinStringArray( + cl_chars[i], defaultFormat)[0]; + } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字 + hanyupinyin += cl_chars[i]; + } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母 + + hanyupinyin += cl_chars[i]; + } else {// 否则不转换 + } + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + System.out.println("字符不能转成汉语拼音"); + } + return hanyupinyin; + } + + /** + * 取第一个汉字的第一个字符 + * + * @return String + * @throws + * @Title: getFirstLetter + * @Description: TODO + */ + public static String getFirstLetter(String ChineseLanguage) { + char[] cl_chars = ChineseLanguage.trim().toCharArray(); + String hanyupinyin = ""; + HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); + defaultFormat.setCaseType(HanyuPinyinCaseType.UPPERCASE);// 输出拼音全部大写 + defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE);// 不带声调 + try { + String str = String.valueOf(cl_chars[0]); + if (str.matches("[\u4e00-\u9fa5]+")) {// 如果字符是中文,则将中文转为汉语拼音,并取第一个字母 + hanyupinyin = PinyinHelper.toHanyuPinyinStringArray( + cl_chars[0], defaultFormat)[0].substring(0, 1); + ; + } else if (str.matches("[0-9]+")) {// 如果字符是数字,取数字 + hanyupinyin += cl_chars[0]; + } else if (str.matches("[a-zA-Z]+")) {// 如果字符是字母,取字母 + + hanyupinyin += cl_chars[0]; + } else {// 否则不转换 + + } + } catch (BadHanyuPinyinOutputFormatCombination e) { + System.out.println("字符不能转成汉语拼音"); + } + return hanyupinyin; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/IdGeneratorUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/IdGeneratorUtils.java new file mode 100644 index 00000000..b65073af --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/IdGeneratorUtils.java @@ -0,0 +1,35 @@ +package com.dstz.base.common.utils; + +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.idgen.IdGenerator; + +/** + * ID生成器工具类 + * + * @author wacxhs + */ +public class IdGeneratorUtils { + + private static volatile IdGenerator idGenerator; + + private IdGeneratorUtils() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + + /** + * 唯一ID + * + * @return 唯一ID + */ + public static String nextId() { + if (idGenerator == null) { + synchronized (IdGeneratorUtils.class) { + if (idGenerator == null) { + idGenerator = SpringUtil.getBean(IdGenerator.class); + } + } + } + return idGenerator.nextId(); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/JsonUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/JsonUtils.java new file mode 100644 index 00000000..1e5ec12c --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/JsonUtils.java @@ -0,0 +1,320 @@ +package com.dstz.base.common.utils; + +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.List; +import java.util.Map; + +import org.springframework.util.ReflectionUtils; + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.core.json.JsonReadFeature; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.JavaType; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.ObjectWriter; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; + +import cn.hutool.core.util.StrUtil; + +/** + * JSON 常用操作工具类 + * + * @author wacxhs + */ +public class JsonUtils { + + private static final ObjectMapper OBJECT_MAPPER; + + static { + OBJECT_MAPPER = new ObjectMapper().findAndRegisterModules(); + OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL); + OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES); + OBJECT_MAPPER.disable(DeserializationFeature.FAIL_ON_IGNORED_PROPERTIES); + OBJECT_MAPPER.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss")); + OBJECT_MAPPER.configure(JsonReadFeature.ALLOW_UNESCAPED_CONTROL_CHARS.mappedFeature(), true); + } + + /** + * 将对象转换为JSON字符串 + * + * @param object + * 对象 + * @return JSON字符串 + */ + public static String toJSONString(Object object) { + try { + return OBJECT_MAPPER.writeValueAsString(object); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return StrUtil.EMPTY; + } + } + + /** + * 将对象转换为JSON字节数组 + * + * @param object + * 对象 + * @return JSON字节数组 + */ + public static byte[] toJSONBytes(Object object) { + try { + return OBJECT_MAPPER.writeValueAsBytes(object); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return new byte[0]; + } + } + + /** + * 将对象转换为JSON字符串 + * + * @param object + * 对象 + * @param feature + * 序列化特性 + * @return JSON字符串 + */ + public static String toJSONString(Object object, SerializationFeature... feature) { + try { + ObjectWriter writer = OBJECT_MAPPER.writer(); + return writer.withFeatures(feature).writeValueAsString(object); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 将对象转换为字符串并指定日期格式 + * + * @param object + * 对象 + * @param dateFormat + * 日期格式 + * @return JSON字符串 + */ + public static String toJSONStringWithDateFormat(Object object, String dateFormat) { + try { + ObjectWriter writer = OBJECT_MAPPER.writer(); + return writer.with(new SimpleDateFormat(dateFormat)).writeValueAsString(object); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 解析JSON为指定类型对象 + * + * @param json + * JSON字符串 + * @param clazz + * 类型 + * @param + * T + * @return 指定类型对象 + */ + public static T parseObject(String json, Class clazz) { + if (StrUtil.isEmpty(json)) { + return null; + } + try { + return OBJECT_MAPPER.readValue(json, clazz); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 解析JSON为指定类型对象 + * + * @param jsonBytes + * JSON字节数组 + * @param clazz + * 类型 + * @param + * T + * @return 指定类型对象 + */ + public static T parseObject(byte[] jsonBytes, Class clazz) { + try { + return OBJECT_MAPPER.readValue(jsonBytes, clazz); + } catch (IOException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 解析JSON为指定类型引用类型对象 + * + * @param json + * JSON字符串 + * @param typeReference + * 类型引用 + * @param + * T + * @return 指定类型对象 + */ + public static T parseObject(String json, TypeReference typeReference) { + try { + return OBJECT_MAPPER.readValue(json, typeReference); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 解析JSON为指定类型对象 + * + * @param jsonBytes + * JSON字节数组 + * @param typeReference + * 类型引用 + * @param + * T + * @return 指定类型对象 + */ + public static T parseObject(byte[] jsonBytes, TypeReference typeReference) { + try { + return OBJECT_MAPPER.readValue(jsonBytes, typeReference); + } catch (IOException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 解析JSON为列表 + * + * @param json + * JSON字符串 + * @param clazz + * 类型 + * @param + * T + * @return 列表 + */ + public static List parseArray(String json, Class clazz) { + if (StrUtil.isEmpty(json)) { + return null; + } + JavaType javaType = OBJECT_MAPPER.getTypeFactory().constructCollectionType(List.class, clazz); + try { + return OBJECT_MAPPER.readValue(json, javaType); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 将对象转换为JsonNode + * + * @param object + * 对象 + * @return JSON对象 + */ + public static JsonNode toJSONNode(Object object) { + return OBJECT_MAPPER.valueToTree(object); + } + + /** + * 将字符串转换为JsonNode + * + * @param jsonStr + * json字符串 + * @return JSON对象 + */ + public static JsonNode toJSONNode(String jsonStr) { + try { + return OBJECT_MAPPER.readTree(jsonStr); + } catch (JsonProcessingException e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + } + + /** + * 获取JsonNode 属性值 + * + * @param name + * 属性名 + * @param objectNode + * json节点 + * @return + */ + public static String getValueAsString(String name, JsonNode objectNode) { + String propertyValue = null; + JsonNode propertyNode = objectNode.get(name); + if (propertyNode != null && !propertyNode.isNull()) { + propertyValue = propertyNode.asText(); + } + return propertyValue; + } + + public static ObjectNode createObjectNode() { + return OBJECT_MAPPER.createObjectNode(); + } + + public static ArrayNode createArrayNode() { + return OBJECT_MAPPER.createArrayNode(); + } + + /** + * 解析JSON为指定类型引用类型对象 + * + * @param json + * JsonNode + * @param + * T + * @return 指定类型对象 + */ + public static T parseObject(JsonNode json, Class clazz) { + return OBJECT_MAPPER.convertValue(json, clazz); + } + + /** + * 解析JSON为列表 + * + * @param json + * JsonNode + * @param clazz + * 类型 + * @param + * T + * @return 列表 + * @throws IOException + */ + public static List parseArray(JsonNode json, Class clazz) { + if (!json.isArray()) { + throw new ApiException(GlobalApiCodes.PARSE_ERROR.formatDefaultMessage("JSON 非 数组形式 ")); + } + + try { + return OBJECT_MAPPER.readerForListOf(clazz).readValue(json); + } catch (IOException e) { + throw new ApiException(GlobalApiCodes.PARSE_ERROR.formatDefaultMessage("JSON Array "), e); + } + } + + public static Map toMap(JsonNode json) { + return JsonUtils.parseObject(json, Map.class); + } + + public static Map toMap(String json) { + return JsonUtils.parseObject(json, Map.class); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/TaskDecoratorUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/TaskDecoratorUtils.java new file mode 100644 index 00000000..763b3a60 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/TaskDecoratorUtils.java @@ -0,0 +1,36 @@ +package com.dstz.base.common.utils; + +import org.springframework.core.task.TaskDecorator; + +/** + * task decorator utils + * + * @author wacxhs + */ +public class TaskDecoratorUtils { + + /** + * decorate runnable + * + * @param target target + * @param taskDecorators taskDecorators + * @return decorate runnable + */ + public static Runnable decorate(Runnable target, TaskDecorator... taskDecorators) { + Runnable runnable = target; + for (int i = taskDecorators.length - 1; i >= 0; i--) { + runnable = taskDecorators[i].decorate(runnable); + } + return runnable; + } + + /** + * build chain task decorator + * + * @param taskDecorators taskDecorators + * @return chain taskDecorators + */ + public static TaskDecorator buildChainTaskDecorator(TaskDecorator... taskDecorators) { + return target -> decorate(target, taskDecorators); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMapUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMapUtil.java new file mode 100644 index 00000000..91c28b45 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMapUtil.java @@ -0,0 +1,98 @@ +package com.dstz.base.common.utils; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.Function; + +/** + *
+ * 描述:线程map的工具类
+ * 让开发员随时放自己想要的东西到线程变量中
+ * 作者:aschs
+ * 邮箱:aschs@agilebpm.cn
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +public class ThreadMapUtil { + private final static ThreadLocal> threadLocalMap = new ThreadLocal<>(); + + static { + ContextCleanUtils.register(threadLocalMap::remove, ContextCleanUtils.Phase.REQUEST_COMPLETE, ContextCleanUtils.Phase.THREAD); + } + + private ThreadMapUtil() { + + } + + private static Map map() { + Map map = threadLocalMap.get(); + if (map == null) { + threadLocalMap.set(new ConcurrentHashMap()); + map = threadLocalMap.get(); + } + return map; + } + + public static void put(String key, Object value) { + map().put(key, value); + } + + public static Object get(String key) { + if (threadLocalMap.get() == null) { + return null; + } + + return map().get(key); + } + + public static Object remove(String key) { + Object obj = get(key); + map().remove(key); + if (map().isEmpty()) { + threadLocalMap.remove(); + } + return obj; + } + + public static Object removeDefault(String key, Object defaultValue) { + Object obj = get(key); + if (obj == null) { + obj = defaultValue; + } + map().remove(key); + if (map().isEmpty()) { + threadLocalMap.remove(); + } + return obj; + } + + public static Map getMap() { + return threadLocalMap.get(); + } + + /** + *
+	 * 清除线程变量
+	 * 
+ */ + public static void remove() { + threadLocalMap.remove(); + } + + public static Object getOrDefault(String key, Object defaultValue) { + return map().getOrDefault(key, defaultValue); + } + + /** + *
+	 * 获取某个值,为空时创建mappingFunction
+	 * 
+ * + * @param key + * @param mappingFunction + * @return + */ + public static Object computeIfAbsent(String key, Function mappingFunction) { + return map().computeIfAbsent(key, mappingFunction); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMsgUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMsgUtil.java new file mode 100644 index 00000000..f47c01c6 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadMsgUtil.java @@ -0,0 +1,94 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.collection.CollectionUtil; + +import java.util.ArrayList; +import java.util.List; + +/** + * 消息Util工具类,在线程变量中添加消息,消息使用list存放。 + * + * @author aschs + */ +public class ThreadMsgUtil { + private static ThreadLocal> localMsg = new ThreadLocal<>(); + + static { + ContextCleanUtils.register(localMsg::remove, ContextCleanUtils.Phase.THREAD, ContextCleanUtils.Phase.REQUEST_COMPLETE); + } + + /** + * 添加消息。 + * + * @param msg + */ + public static void addMsg(String msg) { + List list = localMsg.get(); + if (CollectionUtil.isEmpty(list)) { + list = new ArrayList<>(); + localMsg.set(list); + } + list.add(msg); + } + + /** + * 获取消息数据,并直接清除消息中的数据。 + * + * @return + */ + public static List getMsg() { + return getMsg(true); + } + + /** + * 获取消息数据。 + * + * @param clean + * @return + */ + public static List getMsg(boolean clean) { + List list = localMsg.get(); + if (clean) { + localMsg.remove(); + } + return list; + } + + /** + * 返回流程消息。 + * + * @return + */ + public static String getMessage() { + return getMessage(true); + } + + /** + * 获取消息。 + * + * @param clean + * @return + */ + public static String getMessage(boolean clean) { + return getMessage(clean, "\r\n"); + } + + public static String getMessage(boolean clean, String lineBreak) { + List list = getMsg(clean); + if (CollectionUtil.isEmpty(list)) { + return ""; + } + StringBuilder sb = new StringBuilder(); + for (String msg : list) { + sb.append(msg).append(lineBreak); + } + return sb.toString(); + } + + /** + * 清除消息。 + */ + public static void clean() { + localMsg.remove(); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadNowUtil.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadNowUtil.java new file mode 100644 index 00000000..a15d1437 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadNowUtil.java @@ -0,0 +1,23 @@ +package com.dstz.base.common.utils; + +import java.util.Date; + +/** + *
+ * 预留入口
+ * 
+ * + * @author aschs + * @date 2022年3月30日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public class ThreadNowUtil { + private ThreadNowUtil() { + + } + + public static Date getNow() { + Date now = new Date(); + return now; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadSchedulerUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadSchedulerUtils.java new file mode 100644 index 00000000..3b680b90 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ThreadSchedulerUtils.java @@ -0,0 +1,28 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.thread.ThreadUtil; + +import java.util.concurrent.ScheduledExecutorService; +import java.util.concurrent.ScheduledThreadPoolExecutor; + +/** + * 线程调度器工具类 + * + * @author wacxhs + */ +public class ThreadSchedulerUtils { + + private static final class Singleton { + static final ScheduledThreadPoolExecutor scheduledThreadPoolExecutor = new ScheduledThreadPoolExecutor(2, ThreadUtil.newNamedThreadFactory("ab-scheduler-", true)); + } + + /** + * 获取调度执行器 + * + * @return 调度执行器 + */ + public static ScheduledExecutorService getScheduledExecutorService() { + return Singleton.scheduledThreadPoolExecutor; + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ToStringUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ToStringUtils.java new file mode 100644 index 00000000..cf620601 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/ToStringUtils.java @@ -0,0 +1,34 @@ +package com.dstz.base.common.utils; + +import org.apache.commons.lang3.builder.ToStringBuilder; +import org.apache.commons.lang3.builder.ToStringStyle; + +/** + *
+ * 描述:类转字符串工具,字符串格式如下:
+ * 类名[字段a:a,字段b:b,...]
+ * 作者:aschs
+ * 邮箱:aschs@agilebpm.cn
+ * 日期:2020年3月8日 上午9:37:48
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ * + * @author aschs + */ +public class ToStringUtils { + private ToStringUtils() { + + } + + /** + * 对象字段信息字符串化 + * + * @param object + * 对象 + * @return 对象信息字符串 + */ + public static String toString(Object object) { + return ToStringBuilder.reflectionToString(object, ToStringStyle.SHORT_PREFIX_STYLE); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/UserContextUtils.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/UserContextUtils.java new file mode 100644 index 00000000..05cb0329 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/utils/UserContextUtils.java @@ -0,0 +1,131 @@ +package com.dstz.base.common.utils; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.context.UserContext; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; + +import java.util.Optional; +import java.util.Set; + +/** + * 用户上下文工具类 + * + * @author wacxhs + */ +public class UserContextUtils { + + private static volatile UserContext userContext; + + private UserContextUtils() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * 获取用户上下文 + * + * @return 用户上下文 + */ + public static UserContext getUserContext() { + if (userContext == null) { + synchronized (UserContextUtils.class) { + userContext = SpringUtil.getBean(UserContext.class); + } + } + return userContext; + } + + /** + * 获取用户,请判空 + * + * @return 用户 + */ + public static Optional getUser() { + return ObjectUtil.defaultIfNull(getUserContext().getUser(), Optional.empty()); + } + + /** + * 强依赖当前用户的接口场景
+ * 如果你的方法有可能出现在没登录用户场景的,勿用 + * @return + */ + public static IUser getValidUser() { + return getUserContext().getUser().orElseThrow(() -> new BusinessException(GlobalApiCodes.NO_LOGIN_USER)); + } + + /** + * 获取用户ID + * + * @return 用户ID + */ + public static String getUserId() { + return getUser().map(IUser::getUserId).orElse(null); + } + + /** + * 获取用户姓名 + * + * @return 获取用户姓名 + */ + public static String getUserName() { + return getUser().map(IUser::getFullName).orElse(null); + } + + /** + * 获取用户账户 + * + * @return 用户账户 + */ + public static String getAccount(){ + return getUser().map(IUser::getUsername).orElse(null); + } + + /** + * 获取组织 + * + * @return 组织 + */ + public static Optional getGroup() { + return ObjectUtil.defaultIfNull(getUserContext().getOrg(), Optional.empty()); + } + + /** + * 获取当前组织ID + * + * @return 组织ID + */ + public static String getGroupId() { + return getUserContext().getOrg().map(IGroup::getGroupId).orElse(null); + } + + /** + * 是否超级管理员 + * + * @return 是否超级管理员 + */ + public static boolean isSuperAdmin() { + return getUserContext().isSuperAdmin(); + } + + /** + * 清除当前执行人。 + * void + */ + public static void clear() { + getUserContext().clear(); + } + + /** + * + * @param userId + * @return + */ + public static Set getFilterUserIds(String userId) { + // TODO 返回 工作交接 + return CollectionUtil.newHashSet(UserContextUtils.getUserId()); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/CreateValidation.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/CreateValidation.java new file mode 100644 index 00000000..bf4f8076 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/CreateValidation.java @@ -0,0 +1,10 @@ +package com.dstz.base.common.validation; + +/** + * 创建校验,只做校验器标记使用 + * + * @author wacxhs + */ +public interface CreateValidation { + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/UpdateValidation.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/UpdateValidation.java new file mode 100644 index 00000000..33585468 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/validation/UpdateValidation.java @@ -0,0 +1,10 @@ +package com.dstz.base.common.validation; + +/** + * 更新校验,只做校验器标记使用 + * + * @author wacxhs + */ +public interface UpdateValidation { + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMap.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMap.java new file mode 100644 index 00000000..674a803a --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMap.java @@ -0,0 +1,81 @@ +package com.dstz.base.common.valuemap; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.valuemap.loader.AbNullValueMapLoader; + +import java.lang.annotation.*; + +/** + * 值映射 + * + * @author wacxhs + */ +@Target({ElementType.FIELD, ElementType.METHOD}) +@Retention(RetentionPolicy.RUNTIME) +@Documented +public @interface AbValueMap { + + /** + * 映射类型 + * + * @return 映射类型 + */ + AbValueMapType type(); + + /** + * 加载器 + * + * @return 加载器 + */ + Class> loader() default AbNullValueMapLoader.class; + + /** + * 固定值 + * + * @return 固定值 + */ + String fixValue() default StrUtil.EMPTY; + + /** + * 匹配字段 + * + * @return 匹配字段 + */ + String matchField() default StrUtil.EMPTY; + + /** + * 固定类 + * + * @return 固定类 + */ + Class[] fixClass() default {}; + + /** + * 字段映射,未配置则渲染加载器所有字段 + * + * @return 字段映射 + */ + AttrMap[] attrMap() default {}; + + /** + * 属性映射 + */ + @Retention(RetentionPolicy.RUNTIME) + @Target({}) + @interface AttrMap { + + /** + * 原字段名称 + * + * @return 原字段名称 + */ + String originName(); + + /** + * 目标字段名称,允许为空 + * + * @return 映射目标字段名称 + */ + String targetName() default StrUtil.EMPTY; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapAnnotationProcessor.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapAnnotationProcessor.java new file mode 100644 index 00000000..30f57db1 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapAnnotationProcessor.java @@ -0,0 +1,278 @@ +package com.dstz.base.common.valuemap; + + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ConcurrentHashSet; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.lang.SimpleCache; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.stream.StreamUtil; +import cn.hutool.core.thread.NamedThreadFactory; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.async.ContextCleanTaskDecorator; +import com.dstz.base.common.async.ContextDuplicationTaskDecorator; +import com.dstz.base.common.utils.TaskDecoratorUtils; +import com.dstz.base.common.utils.CastUtils; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonMappingException; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.BeanPropertyWriter; +import com.fasterxml.jackson.databind.ser.BeanSerializer; +import com.fasterxml.jackson.databind.ser.PropertyWriter; +import com.google.common.collect.ImmutableMap; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.aop.support.AopUtils; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.beans.PropertyDescriptor; +import java.io.IOException; +import java.lang.reflect.Method; +import java.lang.reflect.UndeclaredThrowableException; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.CountDownLatch; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +/** + * 注解处理器 + * + * @author wacxhs + */ +public class AbValueMapAnnotationProcessor { + + private static final Logger logger = LoggerFactory.getLogger(AbValueMapAnnotationProcessor.class); + + /** + * 类注解字段缓存 + */ + private final SimpleCache, List> classAnnotationFieldCache = new SimpleCache<>(); + + private volatile ThreadPoolTaskExecutor threadPoolTaskExecutor; + + public static AbValueMapAnnotationProcessor getInstance() { + return Holder.INSTANCE; + } + + + /** + * 获取线程任务池 + * + * @return 线程任务池 + */ + private ThreadPoolTaskExecutor getThreadPoolTaskExecutor() { + if (threadPoolTaskExecutor == null) { + synchronized (this) { + if (threadPoolTaskExecutor == null) { + ThreadPoolTaskExecutor localThreadPoolTaskExecutor = new ThreadPoolTaskExecutor(); + localThreadPoolTaskExecutor.setKeepAliveSeconds(30); + localThreadPoolTaskExecutor.setMaxPoolSize(Math.max(Math.round(Runtime.getRuntime().availableProcessors() / 0.7f), 10)); + localThreadPoolTaskExecutor.setTaskDecorator(TaskDecoratorUtils.buildChainTaskDecorator(ContextCleanTaskDecorator.INSTANCE, ContextDuplicationTaskDecorator.INSTANCE)); + localThreadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); + localThreadPoolTaskExecutor.setThreadFactory(new NamedThreadFactory("ab-valuemap-", true)); + localThreadPoolTaskExecutor.initialize(); + this.threadPoolTaskExecutor = localThreadPoolTaskExecutor; + } + } + } + return threadPoolTaskExecutor; + } + + + private Map loadMappedValue(Collection mapKeys, AbValueMap abValueMap) { + return ObjectUtil.defaultIfNull(AbValueMapLoaderProvider.findAbValueMapLoader(abValueMap).loading(abValueMap, mapKeys), Collections.emptyMap()); + } + + public void prepareContainerMapValue(SerializerProvider serializerProvider, Object value) { + Map> fieldGroupValues = Collections.emptyMap(); + if (value instanceof Iterable) { + Iterable iterable = CastUtils.cast(value); + fieldGroupValues = extractFieldGroupValues(StreamUtil.of(iterable, true), serializerProvider); + } else if (value instanceof Map) { + Map map = CastUtils.cast(value); + fieldGroupValues = extractFieldGroupValues(map.entrySet().parallelStream(), serializerProvider); + } else if (ArrayUtil.isArray(value)) { + fieldGroupValues = extractFieldGroupValues(Arrays.stream((Object[]) value).parallel(), serializerProvider); + } + if (MapUtil.isNotEmpty(fieldGroupValues)) { + Map> fieldValueMap; + if (fieldGroupValues.size() > 1) { + // 利用多线程并行加载提升速度 + fieldValueMap = new ConcurrentHashMap<>((int) (fieldGroupValues.size() / MapUtil.DEFAULT_LOAD_FACTOR + 1)); + CountDownLatch countDownLatch = new CountDownLatch(fieldGroupValues.size()); + for (Map.Entry> entry : fieldGroupValues.entrySet()) { + final AbValueMap abValueMap = entry.getKey().getAnnotation(AbValueMap.class); + getThreadPoolTaskExecutor().submit(() -> { + try { + fieldValueMap.put(entry.getKey().getName(), loadMappedValue(entry.getValue(), abValueMap)); + } finally { + countDownLatch.countDown(); + } + }); + } + // 等待所有线程完成 + try { + countDownLatch.await(); + } catch (InterruptedException e) { + ExceptionUtil.wrapAndThrow(e); + } + } else { + Map.Entry> entry = CollUtil.getFirst(fieldGroupValues.entrySet()); + AbValueMap abValueMap = entry.getKey().getAnnotation(AbValueMap.class); + fieldValueMap = ImmutableMap.of(entry.getKey().getName(), loadMappedValue(entry.getValue(), abValueMap)); + } + pushStack(serializerProvider, fieldValueMap); + } else { + pushStack(serializerProvider, null); + } + } + + private void pushStack(SerializerProvider serializerProvider, Map> fieldValueMap) { + LinkedList>> stack = CastUtils.cast(serializerProvider.getAttribute(AbValueMap.class)); + if (stack == null) { + serializerProvider.setAttribute(AbValueMap.class, stack = new LinkedList<>()); + } + stack.push(fieldValueMap); + } + + private LinkedList>> stack(SerializerProvider serializerProvider) { + return CastUtils.cast(serializerProvider.getAttribute(AbValueMap.class)); + } + + public void cleanContainerMapValue(SerializerProvider serializerProvider) { + LinkedList stack = stack(serializerProvider); + if (CollUtil.isNotEmpty(stack)) { + stack.poll(); + } + } + + public void serialFieldMapped(Object pojo, JsonGenerator gen, SerializerProvider serializerProvider, PropertyWriter writer) throws Exception { + // 无注解字段直接跳过 + if (writer.getAnnotation(AbValueMap.class) == null) { + return; + } + AbValueMap abValueMap = writer.getAnnotation(AbValueMap.class); + if (abValueMap == null) { + return; + } + Object mappedObject = getPropertyMappedValue(pojo, serializerProvider, abValueMap, (BeanPropertyWriter) writer); + if (mappedObject == null) { + return; + } + writeMappedFieldValue(gen, writer.getName(), abValueMap, mappedObject); + } + + private Object getPropertyMappedValue(Object pojo, SerializerProvider serializerProvider, AbValueMap abValueMap, BeanPropertyWriter propertyWriter) throws Exception { + Object fieldValue = propertyWriter.get(pojo); + // 字段值为空跳过 + if (fieldValue == null) { + return null; + } + Map valueMappedMap; + Map> filedGroupValueMap = CollUtil.getFirst(stack(serializerProvider)); + // 上层来源不是容器 + if (filedGroupValueMap == null) { + valueMappedMap = loadMappedValue(Collections.singleton(fieldValue), abValueMap); + } else { + valueMappedMap = filedGroupValueMap.get(propertyWriter.getName()); + } + return CollUtil.isEmpty(valueMappedMap) ? null : valueMappedMap.get(fieldValue); + } + + private void writeMappedFieldValue(JsonGenerator gen, String fieldNamePrefix, AbValueMap abValueMap, Object mappedObject) throws IOException { + // map 类型数据处理 + if (mappedObject instanceof Map) { + Map mappedMap = CastUtils.cast(mappedObject); + if (ArrayUtil.isEmpty(abValueMap.attrMap())) { + for (Map.Entry entry : mappedMap.entrySet()) { + gen.writeFieldName(fieldNamePrefix + StrUtil.upperFirst(entry.getKey())); + gen.writeObject(entry.getValue()); + } + } else { + for (AbValueMap.AttrMap attrMap : abValueMap.attrMap()) { + gen.writeFieldName(StrUtil.isNotEmpty(attrMap.targetName()) ? attrMap.targetName() : fieldNamePrefix + StrUtil.upperFirst(attrMap.originName())); + gen.writeObject(mappedMap.get(attrMap.originName())); + } + } + } else { + Class mappedObjectClass = AopUtils.getTargetClass(mappedObject); + if (ArrayUtil.isEmpty(abValueMap.attrMap())) { + for (PropertyDescriptor propertyDescriptor : BeanUtil.getPropertyDescriptors(mappedObjectClass)) { + Method readMethod = propertyDescriptor.getReadMethod(); + if (readMethod != null) { + gen.writeFieldName(fieldNamePrefix + StrUtil.upperFirst(propertyDescriptor.getName())); + gen.writeObject(ReflectUtil.invoke(mappedObject, readMethod)); + } + } + } else { + Map propertyDescriptorMap = BeanUtil.getPropertyDescriptorMap(mappedObjectClass, false); + for (AbValueMap.AttrMap attrMap : abValueMap.attrMap()) { + PropertyDescriptor propertyDescriptor = propertyDescriptorMap.get(attrMap.originName()); + Method readMethod = propertyDescriptor != null ? propertyDescriptor.getReadMethod() : null; + if (readMethod != null) { + gen.writeFieldName(StrUtil.isNotEmpty(attrMap.targetName()) ? attrMap.targetName() : fieldNamePrefix + StrUtil.upperFirst(attrMap.originName())); + gen.writeObject(ReflectUtil.invoke(mappedObject, readMethod)); + } + } + } + } + } + + private Map> extractFieldGroupValues(Stream stream, SerializerProvider serializerProvider) { + Map> fieldGroupValues = MapUtil.newConcurrentHashMap(); + stream.forEach(obj -> { + Class typeClass = ClassUtil.getClass(obj); + for (BeanPropertyWriter propertyWriter : getAnnotationFields(serializerProvider, typeClass)) { + try { + Object fieldValue = propertyWriter.get(obj); + if (fieldValue != null) { + fieldGroupValues.computeIfAbsent(propertyWriter, k -> new ConcurrentHashSet<>()).add(fieldValue); + } + } catch (Exception e) { + throw new UndeclaredThrowableException(e); + } + } + }); + return fieldGroupValues; + } + + private List getAnnotationFields(SerializerProvider serializerProvider, Class typeClass) { + if (typeClass == null) { + return Collections.emptyList(); + } + return classAnnotationFieldCache.get(typeClass, () -> { + try { + JsonSerializer typedValueSerializer = serializerProvider.findTypedValueSerializer(typeClass, true, null); + if (typedValueSerializer instanceof BeanSerializer) { + return StreamUtil.of(IterUtil.asIterable(typedValueSerializer.properties())) + .filter(propertyWriter -> propertyWriter instanceof BeanPropertyWriter && Objects.nonNull(propertyWriter.getAnnotation(AbValueMap.class))) + .map(BeanPropertyWriter.class::cast) + .collect(Collectors.toList()); + } + } catch (JsonMappingException ex) { + logger.error(ex.getMessage(), ex); + } + return Collections.emptyList(); + }); + } + + private interface Holder { + AbValueMapAnnotationProcessor INSTANCE = new AbValueMapAnnotationProcessor(); + } + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoader.java new file mode 100644 index 00000000..dbd15699 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoader.java @@ -0,0 +1,22 @@ +package com.dstz.base.common.valuemap; + +import java.util.Collection; +import java.util.Map; + +/** + * ab 值映射加载器 + * + * @author wacxhs + */ +public interface AbValueMapLoader { + + /** + * 加载映射 + * + * @param abValueMap 值映射器 + * @param mapKeys 映射键 + * @return 关联值,值应返回Map或者对象 + */ + Map loading(AbValueMap abValueMap, Collection mapKeys); + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoaderProvider.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoaderProvider.java new file mode 100644 index 00000000..95e5cc57 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapLoaderProvider.java @@ -0,0 +1,39 @@ +package com.dstz.base.common.valuemap; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.valuemap.loader.AbEnumValueMapLoader; +import com.dstz.base.common.valuemap.loader.AbNullValueMapLoader; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * @author wacxhs + */ +@SuppressWarnings("all") +public class AbValueMapLoaderProvider { + + private static final Map> valueMapTypeMap = new ConcurrentHashMap<>(); + + private static final Map, AbValueMapLoader> valueMapLoaderClassMap = new ConcurrentHashMap<>(); + + static { + register(AbValueMapType.ENUM, new AbEnumValueMapLoader()); + } + + public static void register(AbValueMapType abValueMapType, AbValueMapLoader valueMapLoader) { + valueMapTypeMap.put(abValueMapType, valueMapLoader); + valueMapLoaderClassMap.put(valueMapLoader.getClass(), valueMapLoader); + } + + public static AbValueMapLoader findAbValueMapLoader(AbValueMap abValueMap) { + AbValueMapLoader abValueMapLoader; + if (AbNullValueMapLoader.class.equals(abValueMap.loader())) { + abValueMapLoader = ObjectUtil.defaultIfNull(valueMapTypeMap.get(abValueMap.type()), AbNullValueMapLoader.NULL); + } else { + abValueMapLoader = valueMapLoaderClassMap.computeIfAbsent(abValueMap.loader(), SpringUtil::getBean); + } + return abValueMapLoader; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapType.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapType.java new file mode 100644 index 00000000..b947abdd --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/AbValueMapType.java @@ -0,0 +1,58 @@ +package com.dstz.base.common.valuemap; + +/** + * 值映射类型 + * + * @author wacxhs + */ +public enum AbValueMapType { + /** + * 枚举 + */ + ENUM, + + /** + * 字典 + */ + DICT, + + /** + * 分类 + */ + TYPE, + + /** + * 系统属性 + */ + SYS_PROPERTIES, + + /** + * 用户 + */ + USER, + + /** + * 组织 + */ + ORG, + + /** + * 自定义 + */ + CUSTOM, + + /** + * 系统数据源 + */ + SYS_DATA_SOURCE, + + /** + * spring的bean映射 + */ + BEAN, + + /** + * 流程定义 + */ + BPM_DEFINITION, +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/BeanPropertyAnnotationValueMapFilter.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/BeanPropertyAnnotationValueMapFilter.java new file mode 100644 index 00000000..9ac4aa45 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/BeanPropertyAnnotationValueMapFilter.java @@ -0,0 +1,18 @@ +package com.dstz.base.common.valuemap; + +import com.dstz.base.common.jackson.PropertyFilterAdapter; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.SerializerProvider; +import com.fasterxml.jackson.databind.ser.PropertyWriter; + +/** + * @author wacxhs + */ +public class BeanPropertyAnnotationValueMapFilter extends PropertyFilterAdapter { + + @Override + public void serializeAsField(Object pojo, JsonGenerator gen, SerializerProvider prov, PropertyWriter writer) throws Exception { + writer.serializeAsField(pojo, gen, prov); + AbValueMapAnnotationProcessor.getInstance().serialFieldMapped(pojo, gen, prov, writer); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/JacksonContainerSerializerDelegate.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/JacksonContainerSerializerDelegate.java new file mode 100644 index 00000000..e479fd4d --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/JacksonContainerSerializerDelegate.java @@ -0,0 +1,31 @@ +package com.dstz.base.common.valuemap; + +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * jackson 序列化代理 + * + * @author wacxhs + */ +public class JacksonContainerSerializerDelegate extends JsonSerializer { + + private final JsonSerializer delegateSerializer; + + public JacksonContainerSerializerDelegate(JsonSerializer delegateSerializer) { + this.delegateSerializer = delegateSerializer; + } + + @Override + public void serialize(Object value, JsonGenerator gen, SerializerProvider serializerProvider) throws IOException { + try { + AbValueMapAnnotationProcessor.getInstance().prepareContainerMapValue(serializerProvider, value); + delegateSerializer.serialize(value, gen, serializerProvider); + } finally { + AbValueMapAnnotationProcessor.getInstance().cleanContainerMapValue(serializerProvider); + } + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbBeanValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbBeanValueMapLoader.java new file mode 100644 index 00000000..9c5bc085 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbBeanValueMapLoader.java @@ -0,0 +1,49 @@ +package com.dstz.base.common.valuemap.loader; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import org.springframework.stereotype.Component; + +import java.beans.PropertyDescriptor; +import java.io.Serializable; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.Map; + +/** + * spring bean 值映射 + * + * @author aschs + */ +@Component +public class AbBeanValueMapLoader implements AbValueMapLoader { + + public AbBeanValueMapLoader() { + AbValueMapLoaderProvider.register(AbValueMapType.BEAN, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + final Class fixClass = abValueMap.fixClass()[0]; + Assert.notNull(fixClass, "@AbValueMap fixClass 未指定"); + final PropertyDescriptor matchFieldPropertyDescriptor = BeanUtil.getPropertyDescriptor(fixClass, abValueMap.matchField()); + Assert.notNull(matchFieldPropertyDescriptor, "{}不存在属性{}", fixClass.getName(), abValueMap.matchField()); + final Method matchFieldReadMethod = matchFieldPropertyDescriptor.getReadMethod(); + Assert.notNull(matchFieldPropertyDescriptor, "{}属性{}不存在Getter方法", fixClass.getName(), abValueMap.matchField()); + + Collection beans = CastUtils.cast(SpringUtil.getBeansOfType(fixClass).values()); + Map mapValues = MapUtil.newHashMap(CollUtil.size(beans)); + CollUtil.forEach(beans, (bean, i) -> mapValues.put(ReflectUtil.invoke(bean, matchFieldReadMethod), bean)); + + return mapValues; + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbEnumValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbEnumValueMapLoader.java new file mode 100644 index 00000000..642fb2b2 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbEnumValueMapLoader.java @@ -0,0 +1,57 @@ +package com.dstz.base.common.valuemap.loader; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Converter; +import cn.hutool.core.convert.ConverterRegistry; +import cn.hutool.core.util.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; + +import java.beans.PropertyDescriptor; +import java.lang.reflect.Method; +import java.util.Collection; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Stream; + +/** + * 枚举值加载器 + * + * @author wacxhs + */ +public class AbEnumValueMapLoader implements AbValueMapLoader { + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + if (ArrayUtil.length(abValueMap.fixClass()) != 1 && !EnumUtil.isEnum(abValueMap.fixClass()[0])) { + throw new IllegalArgumentException(ClassUtil.getClassName(abValueMap.fixClass(), false) + " 非枚举类"); + } + final Class enumClass = abValueMap.fixClass()[0]; + // 反射获取出枚举字段类型 + Method keyMethod; + if (StrUtil.isEmpty(abValueMap.matchField())) { + // key、code、type 较常用,未找到则使用name匹配 + keyMethod = Stream.of("key", "code", "type").map(fieldName -> BeanUtil.getPropertyDescriptor(enumClass, fieldName)).filter(Objects::nonNull).findFirst().map(PropertyDescriptor::getReadMethod).orElseGet(() -> ReflectUtil.getMethodByName(enumClass, "name")); + } else { + keyMethod = BeanUtil.getPropertyDescriptor(enumClass, abValueMap.matchField()).getReadMethod(); + } + Converter converter = ConverterRegistry.getInstance().getDefaultConverter(keyMethod.getReturnType()); + // 反射类型映射,用于匹配 + Map mapKeyTypeMap = new HashMap<>(mapKeys.size()); + for (Object mapKey : mapKeys) { + mapKeyTypeMap.put(converter.convert(mapKey, null), mapKey); + } + Map dataMap = new HashMap<>(mapKeys.size()); + for (Object enumConstant : enumClass.getEnumConstants()) { + Object fieldValue = ReflectUtil.invoke(enumConstant, keyMethod); + Object refKey = mapKeyTypeMap.get(fieldValue); + if (mapKeyTypeMap.containsKey(fieldValue)) { + dataMap.put(refKey, enumConstant); + } + } + return dataMap; + } + + +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbNullValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbNullValueMapLoader.java new file mode 100644 index 00000000..9e153b30 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbNullValueMapLoader.java @@ -0,0 +1,21 @@ +package com.dstz.base.common.valuemap.loader; + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; + +import java.util.Collection; +import java.util.Collections; +import java.util.Map; + +/** + * @author wacxhs + */ +public class AbNullValueMapLoader implements AbValueMapLoader { + + public static final AbNullValueMapLoader NULL = new AbNullValueMapLoader(); + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + return Collections.emptyMap(); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbOrgValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbOrgValueMapLoader.java new file mode 100644 index 00000000..2d423b7b --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbOrgValueMapLoader.java @@ -0,0 +1,61 @@ +package com.dstz.base.common.valuemap.loader; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.func.LambdaUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.*; + +/** + * 组织映射值加载器 + * + * @author wacxhs + */ +@Component +public class AbOrgValueMapLoader implements AbValueMapLoader { + + private static final String GROUP_ID_FIELD_NAME = LambdaUtil.getFieldName(IGroup::getGroupId); + + private static final String GROUP_CODE_FIELD_NAME = LambdaUtil.getFieldName(IGroup::getGroupCode); + + private final GroupApi groupApi; + + public AbOrgValueMapLoader(GroupApi groupApi) { + this.groupApi = groupApi; + AbValueMapLoaderProvider.register(AbValueMapType.ORG, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + String fieldName = StrUtil.emptyToDefault(abValueMap.matchField(), GROUP_ID_FIELD_NAME); + Iterator iterator; + if (StrUtil.equals(fieldName, GROUP_ID_FIELD_NAME)) { + iterator = groupApi.getByGroupIds(GroupType.ORG.getType(), mapKeys); + } else if (StrUtil.equals(fieldName, GROUP_CODE_FIELD_NAME)) { + iterator = groupApi.getByGroupCodes(GroupType.ORG.getType(), mapKeys); + } else { + throw new IllegalStateException(StrUtil.format("{} Field {} not found", IGroup.class.getName(), fieldName)); + } + Method readMethod = BeanUtil.getPropertyDescriptor(IGroup.class, fieldName).getReadMethod(); + return Optional.ofNullable(iterator) + .filter(IterUtil::isNotEmpty) + .map(IterUtil::asIterable) + .map(iter -> CollUtil.toMap(iter, MapUtil.newHashMap(), k -> Convert.toStr(readMethod.invoke(k)))) + .>map(CastUtils::cast) + .orElseGet(Collections::emptyMap); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbUserValueMapLoader.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbUserValueMapLoader.java new file mode 100644 index 00000000..975c9eb3 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/valuemap/loader/AbUserValueMapLoader.java @@ -0,0 +1,61 @@ +package com.dstz.base.common.valuemap.loader; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.func.LambdaUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IUser; +import org.springframework.stereotype.Component; + +import java.lang.reflect.Method; +import java.util.*; + +/** + * 用户关联加载器 + * + * @author wacxhs + */ +@Component("abUserValueMapLoader") +public class AbUserValueMapLoader implements AbValueMapLoader { + + private static final String USER_ID_FIELD_NAME = LambdaUtil.getFieldName(IUser::getUserId); + + private static final String USER_NAME_FIELD_NAME = LambdaUtil.getFieldName(IUser::getUsername); + + private final UserApi userApi; + + public AbUserValueMapLoader(UserApi userApi) { + this.userApi = userApi; + AbValueMapLoaderProvider.register(AbValueMapType.USER, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + final String fieldName = StrUtil.emptyToDefault(abValueMap.matchField(), USER_ID_FIELD_NAME); + Iterator iterator; + if (USER_ID_FIELD_NAME.equalsIgnoreCase(fieldName)) { + iterator = userApi.getByUserIds(mapKeys); + } else if (USER_NAME_FIELD_NAME.equals(fieldName)) { + iterator = userApi.getByUsernames(mapKeys); + } else { + throw new IllegalStateException(StrUtil.format("{} Field {} not found", IUser.class.getName(), fieldName)); + } + Method readMethod = BeanUtil.getPropertyDescriptor(IUser.class, fieldName).getReadMethod(); + return Optional.ofNullable(iterator) + .filter(IterUtil::isNotEmpty) + .map(IterUtil::asIterable) + .map(iterable -> CollUtil.toMap(iterable, MapUtil.newHashMap(), user -> Convert.toStr(ReflectUtil.invoke(user, readMethod)))) + .>map(CastUtils::cast) + .orElseGet(Collections::emptyMap); + } +} diff --git a/ab-base/ab-base-common/src/main/java/com/dstz/base/common/vo/ApiResponseExtractor.java b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/vo/ApiResponseExtractor.java new file mode 100644 index 00000000..133b20d0 --- /dev/null +++ b/ab-base/ab-base-common/src/main/java/com/dstz/base/common/vo/ApiResponseExtractor.java @@ -0,0 +1,64 @@ +package com.dstz.base.common.vo; + + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.codes.IBaseCode; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; + +import java.util.Optional; +import java.util.function.Function; + +/** + * 接口响应提取器 + * + * @author wacxhs + */ +public class ApiResponseExtractor { + + private final ApiResponse apiResponse; + + public ApiResponseExtractor(ApiResponse apiResponse) { + Assert.notNull(apiResponse, () -> new BusinessException(GlobalApiCodes.REMOTE_CALL_ERROR.formatDefaultMessage("ApiResponse is null"))); + this.apiResponse = apiResponse; + } + + /** + * 断言接口响应是否成功 + * + *

+ * 接口响应如存在 + *

+ * + * @return + */ + public ApiResponseExtractor assertSuccess() { + // 校验登录会话是否超时 + if (StrUtil.equals(apiResponse.getCode(), GlobalApiCodes.LOGIN_INVALID.getCode())) { + throw new BusinessMessage(GlobalApiCodes.LOGIN_INVALID); + } else if (!StrUtil.equals(apiResponse.getCode(), GlobalApiCodes.SUCCESS.getCode())) { + throw new BusinessException(IBaseCode.newBuilder().withCode(apiResponse.getCode()).withMessage(apiResponse.getMessage()).build()); + } + return this; + } + + public ApiResponseExtractor assertCode(String code, Function, ? extends RuntimeException> errorFunction) { + Assert.isTrue(apiResponse.getCode().equalsIgnoreCase(code), () -> errorFunction.apply(apiResponse)); + return this; + } + + public T getData() { + return apiResponse.getData(); + } + + public Optional getDataOptional() { + return Optional.ofNullable(apiResponse.getData()); + } + + public static ApiResponseExtractor of(ApiResponse apiResponse) { + return new ApiResponseExtractor<>(apiResponse); + } +} diff --git a/ab-base/ab-base-common/src/test/java/com/dstz/base/common/cache/MemoryCacheTest.java b/ab-base/ab-base-common/src/test/java/com/dstz/base/common/cache/MemoryCacheTest.java new file mode 100644 index 00000000..e71e4c9b --- /dev/null +++ b/ab-base/ab-base-common/src/test/java/com/dstz/base/common/cache/MemoryCacheTest.java @@ -0,0 +1,79 @@ +package com.dstz.base.common.cache; + +import org.junit.Assert; +import org.junit.Test; + +import java.time.Duration; +import java.util.Arrays; +import java.util.Collections; + +public class MemoryCacheTest { + + @Test + public void test_getIfPresent() throws Exception { + MemoryCache memoryCache = new MemoryCache(Arrays.asList( + new CacheRegion("test1", Duration.ofSeconds(1L)), + new CacheRegion("test2", Duration.ofSeconds(1L)) + )); + // 放入缓存数据 + memoryCache.put("test1", "name", "张三"); + memoryCache.put("test2", "name", "李四"); + + // 断言缓存内数据 + Assert.assertEquals("张三", memoryCache.getIfPresent("test1", "name")); + Assert.assertEquals("李四", memoryCache.getIfPresent("test2", "name")); + + // 断言过期数据 + Thread.sleep(1000L); + Assert.assertNull(memoryCache.getIfPresent("test1", "name")); + Assert.assertNull(memoryCache.getIfPresent("test2", "name")); + + // 测试未定义区域 + Assert.assertThrows("test3 is undefined", IllegalArgumentException.class, () -> { + memoryCache.getIfPresent("test3", "name"); + }); + } + + @Test + public void test_get() throws Exception { + MemoryCache memoryCache = new MemoryCache(Collections.singletonList(new CacheRegion("test", Duration.ofSeconds(1L)))); + // 测试数据不存在加载 + Assert.assertEquals("张三", memoryCache.get("test", "name", () -> "张三")); + Assert.assertEquals("张三", memoryCache.getIfPresent("test", "name")); + + Thread.sleep(1000L); + Assert.assertNull(memoryCache.getIfPresent("test", "name")); + } + + @Test + public void test_invalidate() throws Exception { + MemoryCache memoryCache = new MemoryCache(Arrays.asList( + new CacheRegion("test1", Duration.ofSeconds(10L)), + new CacheRegion("test2", Duration.ofSeconds(10L)) + )); + + memoryCache.put("test1", "name", "张三"); + memoryCache.put("test2", "name", "李四"); + + // 清理test1区域缓存 + memoryCache.invalidate("test1", "name"); + + Assert.assertEquals("李四", memoryCache.getIfPresent("test2", "name")); + + memoryCache.invalidateAll(); + Assert.assertNull(memoryCache.getIfPresent("test1", "name")); + Assert.assertNull(memoryCache.getIfPresent("test2", "name")); + } + + @Test + public void test_exists() throws Exception { + MemoryCache memoryCache = new MemoryCache(Arrays.asList( + new CacheRegion("test1", Duration.ofSeconds(10L)), + new CacheRegion("test2", Duration.ofSeconds(10L)) + )); + Assert.assertFalse(memoryCache.exists("test1", "name")); + memoryCache.put("test1", "name", "张三"); + Assert.assertTrue(memoryCache.exists("test1", "name")); + } + +} \ No newline at end of file diff --git a/ab-base/ab-base-mapper/pom.xml b/ab-base/ab-base-mapper/pom.xml new file mode 100644 index 00000000..8ea45faa --- /dev/null +++ b/ab-base/ab-base-mapper/pom.xml @@ -0,0 +1,46 @@ + + + + ab-base + com.dstz + 2.5.0 + + 4.0.0 + + ab-base-mapper + + + + com.dstz + ab-base-common + + + org.springframework + spring-tx + + + org.springframework + spring-context + + + com.baomidou + mybatis-plus-core + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-extension + ${mybatis-plus.version} + + + org.springframework + spring-jdbc + + + com.alibaba + druid + + + \ No newline at end of file diff --git a/ab-base/ab-base-mapper/src/main/java/Ognl.java b/ab-base/ab-base-mapper/src/main/java/Ognl.java new file mode 100644 index 00000000..fc5d2748 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/Ognl.java @@ -0,0 +1,44 @@ +import cn.hutool.core.util.StrUtil; +import com.dstz.base.enums.AbDbType; + +/** + * 给 mybatis mapper 扩展 工具类。 + * + * @author jeff + * @date 2020-10-10 + */ +public class Ognl { + + /** + * 字符串是否为空 + * + * @param str + * @return + */ + public static boolean isStrEmplty(String str) { + return StrUtil.isEmpty(str); + } + + + /** + * 当前线程请求是否为SQLServer + * + * @return + */ + public static boolean isSqlServer() { + return AbDbType.SQLSERVER.equals(""); + } + + /** + * 当前线程请求是否为 postgresql + * + * @return + */ + public static boolean isPostgreSql() { + return AbDbType.POSTGRESQL.equals(""); + } + + public static String $aaaaaaa$(){ + return "001"; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/AbModel.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/AbModel.java new file mode 100644 index 00000000..637dc207 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/AbModel.java @@ -0,0 +1,48 @@ +package com.dstz.base.entity; + +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.dstz.base.common.utils.ToStringUtils; +import com.fasterxml.jackson.annotation.JsonIgnore; + +import cn.hutool.core.util.StrUtil; + +/** + *
+ * 根据ab特性优化后的baomidou的Model类
+ * 自带id主键属性,自带toString方法
+ * 
+ * + * @param + * @author aschs + * @date 2022年2月22日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +public abstract class AbModel> extends Model implements IPersistentEntity { + + @Override + public String toString() { + return ToStringUtils.toString(this); + } + + /** + *
+     * 判断id是否为空
+     * 
+ * @return + */ + @JsonIgnore + public boolean isIdEmpty() { + return StrUtil.isEmpty(this.getId()); + } + + /** + *
+     * 判断id是否为非空
+     * 
+ * @return + */ + @JsonIgnore + public boolean isIdNotEmpty() { + return StrUtil.isNotEmpty(this.getId()); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/IPersistentEntity.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/IPersistentEntity.java new file mode 100644 index 00000000..4a08e386 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/entity/IPersistentEntity.java @@ -0,0 +1,129 @@ +package com.dstz.base.entity; + +import java.util.Date; + +/** + * 持久化实体,所有实体应实现 + * + * @author wacxhs + * @since 2022-01-20 + */ +public interface IPersistentEntity { + + /** + * 设置ID + * + * @param id ID + */ + void setId(String id); + + /** + * 获取ID + * + * @return ID + */ + String getId(); + + /** + * 设置创建者ID + * + * @param createBy 创建者ID + */ + default void setCreateBy(String createBy) { + } + + /** + * 获取创建者ID + * + * @return 创建者ID + */ + default String getCreateBy() { + return null; + } + + /** + * 设置创建时间 + * + * @param createTime 创建时间 + */ + default void setCreateTime(Date createTime) { + } + + /** + * 获取创建时间 + * + * @return 创建时间 + */ + default Date getCreateTime() { + return null; + } + + /** + * 设置更新者ID + * + * @param updateBy 更新者ID + */ + default void setUpdateBy(String updateBy) { + } + + /** + * 获取更新者ID + * + * @return 更新者ID + */ + default String getUpdateBy() { + return null; + } + + /** + * 设置更新 + * + * @param updater 更新者 + */ + default void setUpdater(String updater) { + } + + /** + * 获取更新者 + * + * @return 更新者 + */ + default String getUpdater() { + return null; + } + + /** + * 设置更新时间 + * + * @param updateTime 更新时间 + */ + default void setUpdateTime(Date updateTime) { + } + + /** + * 获取更新时间 + * + * @return 更新时间 + */ + default Date getUpdateTime() { + return null; + } + + + /** + * 设置乐观锁版本号 + * + * @param rev 乐观锁版本号 + */ + default void setRev(Integer rev) { + } + + /** + * 获取乐观锁版本号 + * + * @return 乐观锁版本号 + */ + default Integer getRev() { + return null; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/enums/AbDbType.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/enums/AbDbType.java new file mode 100644 index 00000000..91f0391c --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/enums/AbDbType.java @@ -0,0 +1,148 @@ +package com.dstz.base.enums; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.utils.JdbcUtils; +import org.springframework.util.StringUtils; + +import javax.sql.DataSource; +import java.util.Arrays; + +/** + * 数据库类型 + * + * @author wacxhs + */ +public enum AbDbType { + + /** + * MYSQL + */ + MYSQL("MySQL", "mysql", "MySQL数据库"), + + /** + * ORACLE + */ + ORACLE("Oracle", "oracle", "Oracle数据库"), + + /** + * SQLSERVER + */ + SQLSERVER("Microsoft SQL Server", "sqlserver", "SQLServer数据库"), + + /** + * POSTGRE + */ + POSTGRESQL("PostgreSQL", "postgresql", "Postgre数据库"), + + /** + * Kingbase + */ + KINGBASE("kingbasees", "kingbasees", "人大金仓数据库"), + + /** + * 达梦数据库 + */ + DM("DM DBMS", "DM", "达梦数据库"), + + /** + * 未知数据库 + */ + UNKNOW(null, "UNKNOW", "未知数据库"); + + /** + * 数据库厂商名称 + */ + private final String productName; + + /** + * 数据库名称 + */ + private final String db; + + /** + * 数据库描述 + */ + private final String desc; + + AbDbType(String productName, String db, String desc) { + this.productName = productName; + this.db = db; + this.desc = desc; + } + + public String getProductName() { + return productName; + } + + public String getDb() { + return db; + } + + public String getDesc() { + return desc; + } + + /** + * 获取数据库类型 + * + * @param dbType 字符串数据库类型 + * @return 数据库类型 + */ + public static AbDbType getDbType(String dbType) { + return Arrays.stream(values()).filter(item -> item.getDb().equalsIgnoreCase(dbType)).findFirst().orElse(AbDbType.UNKNOW); + + } + + boolean matchProductName(String productName) { + return this.productName != null && this.productName.equalsIgnoreCase(productName); + } + + public boolean equalsAny(AbDbType... any) { + for (AbDbType target : any) { + if (equals(target)) { + return true; + } + } + return false; + } + + /** + * 数据库厂商名称获取数据库类型 + * @param productName product name + * @return the database driver or {@link #UNKNOW} if not found + */ + public static AbDbType fromProductName(String productName) { + if (StringUtils.hasLength(productName)) { + for (AbDbType candidate : values()) { + if (candidate.matchProductName(productName)) { + return candidate; + } + } + } + return UNKNOW; + } + + /** + * 当前数据库类型 + * + * @return 数据库类型 + */ + public static AbDbType currentDbType() { + String jdbcType = PropertyEnum.JDBC_TYPE.getYamlValue(String.class); + if (CharSequenceUtil.isEmpty(jdbcType)) { + synchronized (AbDbType.class) { + jdbcType = PropertyEnum.JDBC_TYPE.getYamlValue(String.class); + if (CharSequenceUtil.isEmpty(jdbcType)) { + DataSource dataSource = SpringUtil.getBean(DataSource.class); + jdbcType = JdbcUtils.getConnectionProductName(dataSource); + Assert.notBlank(jdbcType, "无法从数据源连接中获取到数据库类型,您可通过环境变量({})指定", PropertyEnum.JDBC_TYPE.getKey()); + System.setProperty(PropertyEnum.JDBC_TYPE.getKey(), jdbcType); + } + } + } + return getDbType(jdbcType); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/idgen/IdentifierGeneratorAdapter.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/idgen/IdentifierGeneratorAdapter.java new file mode 100644 index 00000000..4c31d700 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/idgen/IdentifierGeneratorAdapter.java @@ -0,0 +1,30 @@ +package com.dstz.base.idgen; + +import com.baomidou.mybatisplus.core.incrementer.IdentifierGenerator; +import com.dstz.base.common.idgen.IdGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.math.BigInteger; + +/** + * 适配Mybatis Plus生成器 + * + * @author wacxhs + */ +@Component +public class IdentifierGeneratorAdapter implements IdentifierGenerator { + + @Autowired + private IdGenerator idGenerator; + + @Override + public Number nextId(Object entity) { + return new BigInteger(idGenerator.nextId()); + } + + @Override + public String nextUUID(Object entity) { + return idGenerator.nextId(); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/AbQueryFilterInterceptor.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/AbQueryFilterInterceptor.java new file mode 100644 index 00000000..76e5df49 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/AbQueryFilterInterceptor.java @@ -0,0 +1,71 @@ +package com.dstz.base.interceptor; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.query.AbPage; +import com.dstz.base.query.AbQueryFilter; +import org.apache.ibatis.cache.CacheKey; +import org.apache.ibatis.executor.Executor; +import org.apache.ibatis.mapping.BoundSql; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.plugin.Interceptor; +import org.apache.ibatis.plugin.Intercepts; +import org.apache.ibatis.plugin.Invocation; +import org.apache.ibatis.plugin.Signature; +import org.apache.ibatis.session.ResultHandler; +import org.apache.ibatis.session.RowBounds; + +import java.util.List; + +@Intercepts( + { + @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}), + @Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}), + } + ) +public class AbQueryFilterInterceptor implements Interceptor { + @SuppressWarnings("unchecked") + @Override + public Object intercept(Invocation invocation) throws Throwable { + Object[] args = invocation.getArgs(); + if(ArrayUtil.isEmpty(args) || args.length < 2) { + return invocation.proceed(); + } + + Object param = args[1]; + AbPage page = null; + // 分页 ,参数转换 + Boolean isAbQueryFilterBoolean = param instanceof AbQueryFilter; + if(isAbQueryFilterBoolean) { + AbQueryFilter queryFilter = (AbQueryFilter) param; + // mybatisPlugs 分页插件 自定义参数要声明, mybatisplugs 和 它自身 page插件 冲突,只能这么绕过去 + page = queryFilter.getPage(); + // 无分页,不传入Page + if (page == null) { + page = new AbPage(); + page.setParams(queryFilter.generateQuerySql()); + } else { + // 有分页 ,则将分页转成MAP 穿进去,为了绕过mp的不一致处理逻辑 + page.setParams(queryFilter.generateQuerySql()); + page.put("AB_PAGE_INFO", page); + } + //将queryFilter转为Map Param + if (CollUtil.isNotEmpty(queryFilter.getSelectColumnNames())) { + page.put("SELECT_COLUMN_NAMES", CollUtil.join(queryFilter.getSelectColumnNames(), StrUtil.COMMA)); + } + + args[1] = page; + } + + Object object = invocation.proceed(); + if(isAbQueryFilterBoolean) { + // 封装成 abPageList 返回 + return new PageListDTO<>(page.getSize(), page.getCurrent(), page.getTotal(),(List) object); + } + + return object; + } + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/MybatisPlusInterceptorCustomizer.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/MybatisPlusInterceptorCustomizer.java new file mode 100644 index 00000000..756be43e --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/interceptor/MybatisPlusInterceptorCustomizer.java @@ -0,0 +1,19 @@ +package com.dstz.base.interceptor; + +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; + +/** + * 自定义处理器 + * + * @author wacxhs + */ +public interface MybatisPlusInterceptorCustomizer { + + /** + * 自定义拦截处理 + * + * @param mybatisPlusInterceptor mybatis拦截器 + */ + void customize(MybatisPlusInterceptor mybatisPlusInterceptor); + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/AbBaseManager.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/AbBaseManager.java new file mode 100644 index 00000000..91c94500 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/AbBaseManager.java @@ -0,0 +1,226 @@ +package com.dstz.base.manager; + + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.entity.IPersistentEntity; +import com.dstz.base.query.AbQueryFilter; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + * AB 通用业务处理,所有通用业务处理应实现 + * + * @param 实体类型 + * @author wacxhs + * @since 2022-01-24 + */ +public interface AbBaseManager { + + /** + * 批量创建默认大小 + */ + int DEFAULT_BULK_CRETE_SIZE = 1000; + + /** + * 创建实体对象 + * + * @param entity 实体 + * @return 影响行数 + */ + int create(T entity); + + /** + * 按实体ID更新实体对象 + * + * @param entity 实体 + * @return 影响行数 + */ + int update(T entity); + + /** + * 按实体ID更新完整更新实例对象,对实体对象字段不做判空 + * + * @param entity 实体 + * @return 影响行数 + */ + int updateFullById(T entity); + + /** + * 根据 whereEntity 条件,更新记录 + * + * @param entity 实体对象 (set 条件值,可以为 null) + * @param wrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) + * @return 影响行数 + */ + int update(T entity, Wrapper wrapper); + + /** + * 创建或修改,如果实体ID存修改,不存在创建新记录 + * + * @param entity 实体 + * @return 影响行数 + */ + int createOrUpdate(T entity); + + /** + * 分页列表 + * + * @param queryFilter 分页 + * @param + * @return 分页列表 + */ + PageListDTO query(AbQueryFilter queryFilter); + + /** + * 分页列表 + * + * @param baseQuery 分页查询条件 + * @param wrapper 构造条件 + * @return 分页列表 + */ + PageListDTO query(QueryParamDTO baseQuery, Wrapper wrapper); + + /** + * 分页列表 + * + * @param baseQuery 分页查询条件 + * @param wrapper 构造条件 + * @param clazz 返回的VO或DTO对象 + * @return 分页列表 + */ + PageListDTO query(QueryParamDTO baseQuery, Wrapper wrapper, Class clazz); + /** + * 按实体ID获取实体 + * + * @param id 实体ID + * @return 实体记录 + */ + T getById(Serializable id); + + /** + * 按实体ID集获取实体 + * + * @param ids 实体ID集 + * @return 实体记录 + */ + List selectByIds(Collection ids); + + /** + * 根据 entity 条件,查询全部记录 + * + * @param queryWrapper 实体对象封装操作类(可以为 null) + * @return 实体记录 + */ + List selectByWrapper(Wrapper queryWrapper); + + /** + * 分页列表 + * + * @param page 分页对象 + * @param queryWrapper 实体对象封装操作类(可以为 null) + * @return 分页信息 + */ + IPage selectByPage(IPage page, Wrapper queryWrapper); + + /** + * 根据 Wrapper 条件,查询总记录数 + * + * @param queryWrapper 实体对象封装操作类(可以为 null) + * @return 记录数 + */ + Long selectCount(Wrapper queryWrapper); + + + /** + * 根据 entity 条件,删除记录 + * + * @param wrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句) + * @return 影响行数 + */ + int remove(Wrapper wrapper); + + /** + * 按实体ID删除对象 + * + * @param id 实体ID + * @return 影响行数 + */ + int removeById(Serializable id); + + /** + * 按实体ID集删除记录 + * + * @param ids 实体ID集 + */ + int removeByIds(Collection ids); + + + /** + * 根据 Wrapper 条件,判断是否存在记录 + * + * @param queryWrapper 实体对象封装操作类 + * @return 是否存在记录 + */ + boolean exists(Wrapper queryWrapper); + + + /** + * 查找指定条件的对象 + * @param queryWrapper 条件 + * @return 对象实体 + */ + T selectOne(Wrapper queryWrapper); + + /** + * 获取所有对象集合 + * + * @return 对象集合 + */ + List list(); + + /** + * 批量创建,请注意对象大小 + * + * @param list 列表 + * @return 影响行数 + */ + default void bulkCreate(Iterable list) { + bulkCreate(list, DEFAULT_BULK_CRETE_SIZE); + } + + /** + * 批量创建,对于大对象减少批量写入大小 + * + * @param list 列表 + * @param size 批量大小 + * @return 影响行数 + */ + void bulkCreate(Iterable list, int size); + + /** + *
+     * ab系统大部分业务都有根据编码获取对象
+     * ps:使用者清楚知道自己操作的model是有code_字段才能用
+     * 
+ * + * @param code + * @return + */ + T getByCode(String code); + + /** + *
+     * 业务model存在编码时进行编码唯一性校验
+     * 
+ * + * @param entity + * @param code + * @return true:通过校验|false:校验失败 + */ + boolean checkCode(T entity, String code); +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/impl/AbBaseManagerImpl.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/impl/AbBaseManagerImpl.java new file mode 100644 index 00000000..3aed8e1c --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/manager/impl/AbBaseManagerImpl.java @@ -0,0 +1,189 @@ +package com.dstz.base.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.entity.IPersistentEntity; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.AbQueryFilter; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.Collection; +import java.util.List; + +/** + * AB 通用业务处理实现,所有通用业务处理应实现 + * + * @param + * @author wacxhs + * @since 2022-01-24 + */ +public abstract class AbBaseManagerImpl implements AbBaseManager { + + @Autowired + private AbBaseMapper abBaseMapper; + + /** + * 获取对应 entity 的 BaseMapper + * + * @return BaseMapper + */ + protected AbBaseMapper getBaseMapper() { + return abBaseMapper; + } + + @Override + public int create(T entity) { + return getBaseMapper().insert(entity); + } + + @Override + public int update(T entity) { + return getBaseMapper().updateById(entity); + } + + @Override + public int updateFullById(T entity) { + return getBaseMapper().updateFullById(entity); + } + + @Override + public int update(T entity, Wrapper wrapper) { + return getBaseMapper().update(entity, wrapper); + } + + @Override + public int createOrUpdate(T entity) { + return StrUtil.isEmpty(entity.getId()) ? getBaseMapper().insert(entity) : getBaseMapper().updateFullById(entity); + } + + @Override + public T getById(Serializable id) { + return getBaseMapper().selectById(id); + } + + @Override + public PageListDTO query(AbQueryFilter queryFilter) { + return getBaseMapper().query(queryFilter); + } + + @Override + public PageListDTO query(QueryParamDTO baseQuery, Wrapper wrapper) { + return convertPageDto(getBaseMapper().selectPage(new Page<>(baseQuery.getCurrentPage(), baseQuery.getPageSize()), wrapper)); + } + + @Override + public PageListDTO query(QueryParamDTO baseQuery, Wrapper wrapper, Class clazz) { + IPage iPage = getBaseMapper().selectPage(new Page<>(baseQuery.getCurrentPage(), baseQuery.getPageSize()), wrapper) + .convert(s -> BeanCopierUtils.transformBean(s, clazz)); + return new PageListDTO(iPage.getSize(), iPage.getCurrent(), iPage.getTotal(), iPage.getRecords()); + } + + private PageListDTO convertPageDto(IPage page) { + return new PageListDTO<>(page.getSize(), page.getCurrent(), page.getTotal(), page.getRecords()); + } + + @Override + public List selectByIds(Collection ids) { + return getBaseMapper().selectBatchIds(ids); + } + + @Override + public List selectByWrapper(Wrapper queryWrapper) { + return getBaseMapper().selectList(queryWrapper); + } + + @Override + public IPage selectByPage(IPage page, Wrapper queryWrapper) { + return getBaseMapper().selectPage(page, queryWrapper); + } + + @Override + public Long selectCount(Wrapper queryWrapper) { + return getBaseMapper().selectCount(queryWrapper); + } + + @Override + public int remove(Wrapper wrapper) { + return getBaseMapper().delete(wrapper); + } + + + @Override + public int removeById(Serializable id) { + return getBaseMapper().deleteById(id); + } + + + @Override + public int removeByIds(Collection ids) { + return getBaseMapper().deleteBatchIds(ids); + } + + @Override + public boolean exists(Wrapper queryWrapper) { + return getBaseMapper().exists(queryWrapper); + } + + @Override + public T selectOne(Wrapper queryWrapper) { + return getBaseMapper().selectOne(queryWrapper); + } + + @Override + public List list() { + return getBaseMapper().selectList(null); + } + + @Override + public void bulkCreate(Iterable iterable, int size) { + // 能被hutool拆分列表 + if (iterable instanceof List) { + List list = CastUtils.cast(iterable); + if (list.size() <= size) { + getBaseMapper().bulkCreate(iterable); + } else { + ListUtil.split(list, size).forEach(getBaseMapper()::bulkCreate); + } + } else { + List bulkList = new ArrayList<>(size); + for (T item : iterable) { + bulkList.add(item); + if (bulkList.size() >= size) { + getBaseMapper().bulkCreate(bulkList); + bulkList.clear(); + } + } + if(CollUtil.isNotEmpty(bulkList)) { + getBaseMapper().bulkCreate(bulkList); + } + } + } + + @Override + public T getByCode(String code) { + QueryWrapper queryWrapper = new QueryWrapper<>(); + queryWrapper.eq("code_", code); + return this.selectOne(queryWrapper); + } + + @Override + public boolean checkCode(T entity, String code) { + T t = getByCode(code); + if (t != null && !t.getId().equals(entity.getId())) { + return false; + } + return true; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbBaseMapper.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbBaseMapper.java new file mode 100644 index 00000000..38fcb129 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbBaseMapper.java @@ -0,0 +1,52 @@ +package com.dstz.base.mapper; + +import com.baomidou.mybatisplus.core.conditions.Wrapper; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import com.baomidou.mybatisplus.core.toolkit.Constants; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.entity.IPersistentEntity; +import com.dstz.base.mapper.methods.AbBulkCreate; +import com.dstz.base.query.AbQueryFilter; +import org.apache.ibatis.annotations.Param; +import org.apache.ibatis.cursor.Cursor; + +/** + * ab 通用Mapper,所有Mapper底层Mapper接口实现 + * + * @param 实体模型 + * @author wacxhs + */ +public interface AbBaseMapper extends BaseMapper { + + /** + * queryFilter 分页列表查询 + * @param page + * @return + */ + PageListDTO query(AbQueryFilter abQueryFilter); + + + /** + * 根据实体ID完整性更新,不做字段判空 + * + * @param entity 实体 + * @return 影响行数 + */ + int updateFullById(@Param(Constants.ENTITY) T entity); + + /** + * 批量创建 + * + * @param records 记录 + * @return 影响行数 + */ + int bulkCreate(@Param(AbBulkCreate.METHOD_PARAM_NAME) Iterable records); + + /** + * 根据条件查询游标 + * + * @param wrapper 查询条件包裹 + * @return 游标 + */ + Cursor queryCursorByWrapper(@Param(Constants.WRAPPER) Wrapper wrapper); +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbSqlInjector.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbSqlInjector.java new file mode 100644 index 00000000..a1fe0c81 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/AbSqlInjector.java @@ -0,0 +1,36 @@ +package com.dstz.base.mapper; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.injector.DefaultSqlInjector; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.dstz.base.mapper.methods.AbBulkCreate; +import com.dstz.base.mapper.methods.AbQuery; +import com.dstz.base.mapper.methods.AbQueryCursorByWrapper; +import com.dstz.base.mapper.methods.AbUpdateFullById; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + *
+ * 覆盖mybatis-plus的每个mapper植入方法的时机,给每个mapper植入对应的query方法
+ * 
+ * @author aschs + * @date 2022年3月14日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +@Service +public class AbSqlInjector extends DefaultSqlInjector { + @Override + public List getMethodList(Class mapperClass, TableInfo tableInfo) { + List methods = super.getMethodList(mapperClass, tableInfo); + methods.add(new AbQuery());//增加AB的query方法进入mapper + // 增加批量创建 + methods.add(new AbBulkCreate()); + // 增加 根据ID完整性更新 + methods.add(new AbUpdateFullById()); + // 增加 根据查询条件获取游标 + methods.add(new AbQueryCursorByWrapper()); + return methods; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/handler/AbMetaObjectHandler.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/handler/AbMetaObjectHandler.java new file mode 100644 index 00000000..eaf0c226 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/handler/AbMetaObjectHandler.java @@ -0,0 +1,90 @@ +package com.dstz.base.mapper.handler; + + +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler; +import com.baomidou.mybatisplus.core.metadata.TableFieldInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.metadata.TableInfoHelper; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import org.apache.ibatis.reflection.MetaObject; +import org.springframework.aop.support.AopUtils; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.math.BigInteger; +import java.util.Date; +import java.util.Objects; +import java.util.Optional; +import java.util.function.Supplier; + +/** + * 业务实体通用字段处理器 + * + * @author wacxhs + */ +@Component +public class AbMetaObjectHandler implements MetaObjectHandler { + + @Override + public void insertFill(MetaObject metaObject) { + Optional currentUser = UserContextUtils.getUser(); + Optional currentGroup = UserContextUtils.getGroup(); + // 创建者ID + this.strictInsertFill(metaObject, "createBy", () -> currentUser.map(IUser::getUserId).orElse(StrUtil.EMPTY), String.class); + // 创建者姓名 + this.strictInsertFill(metaObject, "creator", () -> currentUser.map(IUser::getFullName).orElse(StrUtil.EMPTY), String.class); + // 创建时间 + this.strictInsertFill(metaObject, "createTime", Date::new, Date.class); + // 创建着所属组织ID + this.strictInsertFill(metaObject, "createOrgId", () -> currentGroup.map(IGroup::getGroupId).orElse(StrUtil.EMPTY), String.class); + updateFill(metaObject, currentUser); + // 乐关锁默认字段 + TableInfo tableInfo = TableInfoHelper.getTableInfo(AopUtils.getTargetClass(metaObject.getOriginalObject())); + TableFieldInfo versionFieldInfo = tableInfo.getVersionFieldInfo(); + if (versionFieldInfo != null) { + metaObject.setValue(versionFieldInfo.getField().getName(), getTypeDefaultValue(versionFieldInfo.getPropertyType())); + } + } + + private void updateFill(MetaObject metaObject, Optional currentUser) { + // 更新者ID + this.strictUpdateFill(metaObject, "updateBy", () -> currentUser.map(IUser::getUserId).orElse(StrUtil.EMPTY), String.class); + // 更新者 + this.strictUpdateFill(metaObject, "updater", () -> currentUser.map(IUser::getFullName).orElse(StrUtil.EMPTY), String.class); + // 更新时间 + this.strictUpdateFill(metaObject, "updateTime", Date::new, Date.class); + } + + @Override + public void updateFill(MetaObject metaObject) { + Optional currentUser = UserContextUtils.getUser(); + updateFill(metaObject, currentUser); + } + + @Override + public MetaObjectHandler strictFillStrategy(MetaObject metaObject, String fieldName, Supplier fieldVal) { + Object obj = fieldVal.get(); + if (Objects.nonNull(obj)) { + metaObject.setValue(fieldName, obj); + } + return this; + } + + private Object getTypeDefaultValue(Class typeClass) { + if (ClassUtil.isBasicType(typeClass) || ClassUtil.isAssignable(BigDecimal.class, typeClass) || ClassUtil.isAssignable(BigInteger.class, typeClass)) { + return Convert.convert(typeClass, "0"); + } else if (ClassUtil.isAssignable(java.sql.Date.class, typeClass)) { + return new java.sql.Date(System.currentTimeMillis()); + } else if (ClassUtil.isAssignable(java.sql.Timestamp.class, typeClass)) { + return new java.sql.Timestamp(System.currentTimeMillis()); + } else if (ClassUtil.isAssignable(Date.class, typeClass)) { + return new Date(); + } + throw new UnsupportedOperationException(String.format("类型(%s)不支持默认值", typeClass.getName())); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbBulkCreate.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbBulkCreate.java new file mode 100644 index 00000000..7865eda1 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbBulkCreate.java @@ -0,0 +1,50 @@ +package com.dstz.base.mapper.methods; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.enums.SqlMethod; +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.enums.AbDbType; +import org.apache.ibatis.executor.keygen.NoKeyGenerator; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +/** + * 批量创建 + * + * @author wacxhs + */ +public class AbBulkCreate extends AbstractMethod { + + private static final long serialVersionUID = 116839614956393180L; + + public static final String METHOD_NAME = "bulkCreate"; + + public static final String METHOD_PARAM_NAME = "list"; + + + public AbBulkCreate() { + super(METHOD_NAME); + } + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + String sqlScript; + if(AbDbType.MYSQL.getDb().equalsIgnoreCase(configuration.getDatabaseId()) || StrUtil.isEmpty(configuration.getDatabaseId())){ + sqlScript = injectMappedStatementByMysql(mapperClass, modelClass, tableInfo); + }else{ + throw new IllegalStateException(String.format("Not Support databaseId %s", configuration.getDatabaseId())); + } + SqlSource sqlSource = this.languageDriver.createSqlSource(this.configuration, sqlScript, modelClass); + return this.addInsertMappedStatement(mapperClass, modelClass, METHOD_NAME, sqlSource, NoKeyGenerator.INSTANCE, null, null); + } + + private String injectMappedStatementByMysql(Class mapperClass, Class modelClass, TableInfo tableInfo) { + String insertSqlColumn = tableInfo.getAllSqlSelect(); + String insertSqlProperty = StrUtil.removeSuffix(tableInfo.getKeyInsertSqlProperty(true, "et.", false) + filterTableFieldInfo(tableInfo.getFieldList(), null, tableFieldInfo -> tableFieldInfo.getInsertSqlProperty("et."), StrUtil.EMPTY), StrUtil.COMMA); + String convertForeach = SqlScriptUtils.convertForeach(StrUtil.wrap(insertSqlProperty, StrPool.LPAREN, StrPool.RPAREN), METHOD_PARAM_NAME, null, "et", StrUtil.COMMA); + return String.format(SqlMethod.INSERT_ONE.getSql(), tableInfo.getTableName(), StrUtil.wrap(insertSqlColumn, StrPool.LPAREN, StrPool.RPAREN), convertForeach); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQuery.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQuery.java new file mode 100644 index 00000000..d21df856 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQuery.java @@ -0,0 +1,47 @@ +package com.dstz.base.mapper.methods; + +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +import java.text.MessageFormat; +import java.util.Map; + +public class AbQuery extends AbstractMethod { + public AbQuery() { + super("query"); + } + + public AbQuery(String name) { + super(name); + } + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + StringBuilder sb = new StringBuilder(""); + + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sb.toString(), Map.class); + //注意addSelectMappedStatementForTable是查询用的,新增、更新、删除都分别对应不同方法 + return this.addSelectMappedStatementForTable(mapperClass, "query", sqlSource, tableInfo); + } + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQueryCursorByWrapper.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQueryCursorByWrapper.java new file mode 100644 index 00000000..5c340b05 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbQueryCursorByWrapper.java @@ -0,0 +1,30 @@ +package com.dstz.base.mapper.methods; + +import com.baomidou.mybatisplus.core.enums.SqlMethod; +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +/** + * queryCursorByWrapper 实现封装 + * + * @author wacxhs + */ +public class AbQueryCursorByWrapper extends AbstractMethod { + + private static final long serialVersionUID = -4642949513973517235L; + + public AbQueryCursorByWrapper() { + super("queryCursorByWrapper"); + } + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + SqlMethod sqlMethod = SqlMethod.SELECT_LIST; + String sql = String.format(sqlMethod.getSql(), sqlFirst(), sqlSelectColumns(tableInfo, true), tableInfo.getTableName(), + sqlWhereEntityWrapper(true, tableInfo), sqlOrderBy(tableInfo), sqlComment()); + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); + return this.addSelectMappedStatementForTable(mapperClass, methodName, sqlSource, tableInfo); + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbUpdateFullById.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbUpdateFullById.java new file mode 100644 index 00000000..ce694220 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/mapper/methods/AbUpdateFullById.java @@ -0,0 +1,45 @@ +package com.dstz.base.mapper.methods; + +import com.baomidou.mybatisplus.core.enums.SqlMethod; +import com.baomidou.mybatisplus.core.injector.AbstractMethod; +import com.baomidou.mybatisplus.core.metadata.TableInfo; +import com.baomidou.mybatisplus.core.toolkit.StringPool; +import com.baomidou.mybatisplus.core.toolkit.sql.SqlScriptUtils; +import org.apache.ibatis.mapping.MappedStatement; +import org.apache.ibatis.mapping.SqlSource; + +import java.util.Objects; +import java.util.stream.Collectors; + +/** + * 根据ID完整性更新,不做字段判空 + * + * @author wacxhs + */ +public class AbUpdateFullById extends AbstractMethod { + + private static final long serialVersionUID = -8602998669480218957L; + + public AbUpdateFullById() { + super("updateFullById"); + } + + @Override + public MappedStatement injectMappedStatement(Class mapperClass, Class modelClass, TableInfo tableInfo) { + final String additional = optlockVersion(tableInfo) + tableInfo.getLogicDeleteSql(true, true); + String sqlSetScript = tableInfo.getFieldList().stream().filter(field -> { + if (tableInfo.isWithLogicDelete()) { + return !(tableInfo.isWithLogicDelete() && field.isLogicDelete()); + } + return true; + }).map(field -> field.getSqlSet(true, ENTITY_DOT)).filter(Objects::nonNull).collect(Collectors.joining(StringPool.NEWLINE)); + + String sql = String.format(SqlMethod.UPDATE_BY_ID.getSql(), tableInfo.getTableName(), + SqlScriptUtils.convertSet(sqlSetScript), + tableInfo.getKeyColumn(), ENTITY_DOT + tableInfo.getKeyProperty(), additional); + SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass); + return addUpdateMappedStatement(mapperClass, modelClass, methodName, sqlSource); + } + + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbPage.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbPage.java new file mode 100644 index 00000000..e088be9b --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbPage.java @@ -0,0 +1,111 @@ +package com.dstz.base.query; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import org.apache.ibatis.session.RowBounds; + +import java.util.Collection; +import java.util.Map; +import java.util.Set; + +@SuppressWarnings("rawtypes") +public class AbPage extends Page implements Map { + + private static final long serialVersionUID = 1L; + + private Map params; + + // 分页默认不查CountSQL + public AbPage() { + super(); + this.searchCount = false; + } + + public AbPage(long current, long size) { + super(current, size, 0); + } + + public AbPage(long current, long size, long total) { + super(current, size, total, true); + } + + public AbPage(long current, long size, boolean searchCount) { + super(current, size, 0, searchCount); + } + + public AbPage(long current, long size, long total, boolean searchCount) { + super(current, size, total, searchCount); + } + + public AbPage(RowBounds rowBounds, boolean searchCount) { + super((rowBounds.getOffset() / rowBounds.getLimit()) + 1, rowBounds.getLimit(), 0, searchCount); + } + + @Override + public int size() { + return params.size(); + } + + @Override + public boolean isEmpty() { + return params.isEmpty(); + } + + @Override + public boolean containsKey(Object key) { + return params.containsKey(key); + } + + @Override + public boolean containsValue(Object value) { + return params.containsValue(value); + } + + @Override + public Object get(Object key) { + return params.get(key); + } + + @Override + public Object put(String key, Object value) { + return params.put(key, value); + } + + @Override + public Object remove(Object key) { + return params.remove(key); + } + + @Override + public void putAll(Map m) { + params.putAll(m); + } + + @Override + public void clear() { + params.clear(); + } + + @Override + public Set keySet() { + return params.keySet(); + } + + @Override + public Collection values() { + return params.values(); + } + + @Override + public Set> entrySet() { + return params.entrySet(); + } + + public Map getParams() { + return params; + } + + public void setParams(Map params) { + this.params = params; + } + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbQueryFilter.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbQueryFilter.java new file mode 100644 index 00000000..4ca07235 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/AbQueryFilter.java @@ -0,0 +1,65 @@ +package com.dstz.base.query; + +import com.dstz.base.query.impl.DefaultQueryFieldGroup; + +import java.util.List; +import java.util.Map; + +/** + * 查询构造器API + * @author Jeff + * + */ +public interface AbQueryFilter { + + // where sql 预定名字 + static final String WHERE_SQL = "whereSql"; + // orderBySql sql 预定名字 + static final String ORDERBY_SQL = "orderBySql"; + + /** + * 分页 + * @return + */ + AbPage getPage(); + + /** + * 生成查询用的SQL,设置到查询参数中,并返回查询参数 + * @return + */ + Map generateQuerySql(); + + AbQueryFilter noPage(); + + AbQueryFilter addFilter(String fildName,Object value ,ConditionType conditionType); + + //不处理下划线 + AbQueryFilter addFilterOriginal(String fildName,Object value ,ConditionType conditionType); + + //指定groupKey + AbQueryFilter addFilter(String fildName,Object value ,ConditionType conditionType,String groupKey); + + + AbQueryFilter likeFilter(String fildName,Object value ); + + AbQueryFilter inFilter(String fildName,Object value ); + + AbQueryFilter notEqFilter(String fildName,Object value ); + + AbQueryFilter eqFilter(String fildName,Object value ); + + AbQueryFilter addParam(String fildName, Object value); + + AbQueryFilter clearQuery(); + + DefaultQueryFieldGroup getQueryFieldGroup(); + + List getQuerySortList(); + + /** + * 获取查询列名,也就是 select xxxx from table + * + * @return 查询列名 + */ + List getSelectColumnNames(); +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/ConditionType.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/ConditionType.java new file mode 100644 index 00000000..d0134554 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/ConditionType.java @@ -0,0 +1,117 @@ +package com.dstz.base.query; + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; + +public enum ConditionType { + /** + * 等于 + */ + EQUAL("EQ", "=", "等于", new String[] { "varchar", "number", "date", "clob" }), + /** + * 等于忽略大小写 + */ + /* EQUAL_IGNORE_CASE("EIC", "=", "等于忽略大小写", new String[] { "varchar" }),*/ + /** + * 小于 + */ + LESS("LT", "<", "小于", new String[] { "number", "date" }), + /** + * 大于 + */ + GREAT("GT", ">", "大于", new String[] { "number", "date" }), + /** + * 小于等于 + */ + LESS_EQUAL("LE", "<=", "小于等于", new String[] { "number", "date" }), + /** + * 大于等于 + */ + GREAT_EQUAL("GE", ">=", "大于等于", new String[] { "number", "date" }), + /** + * 不等于 + */ + NOT_EQUAL("NE", "!=", "不等于", new String[] { "varchar", "number", "date", "clob" }), + /** + * 相似 + */ + LIKE("LK", "like", "相似", new String[] { "varchar", "clob" }), + /** + * 右相似 + */ + LEFT_LIKE("LFK", "llike", "右相似", new String[] { "varchar", "clob" }), + /** + * 左相似 + */ + RIGHT_LIKE("RHK", "rlike", "左相似", new String[] { "varchar", "clob" }), + /** + * + */ + IS_NULL("INL", "is null", "为空", new String[] { "varchar", "number", "date", "clob" }, false), + /** + * + */ + NOTNULL("NNL", "is not null", "非空", new String[] { "varchar", "number", "date", "clob" }, false), + /** + * 在...中 + */ + IN("IN", "in", "在...中", new String[] { "varchar", "number", "date" }), + /** + * 不在...中 + */ + NOT_IN("NI", "not in", "不在...中", new String[] { "varchar", "number", "date" }), + /** + * 在...之间 + */ + BETWEEN("BT", "between", "在...之间", new String[] { "number", "date" }); + + private String key; + private String condition; + private String desc; + private String[] supports; + /** + * 是否需要参数 + */ + private boolean needParam = true; + + private ConditionType(String key, String condition, String desc, String[] supports) { + this.key = key; + this.condition = condition; + this.desc = desc; + this.supports = supports; + } + + private ConditionType(String key, String condition, String desc, String[] supports, boolean needParam) { + this(key, condition, desc, supports); + this.needParam = needParam; + } + + public static ConditionType formKey(String key) { + for (ConditionType condtion : ConditionType.values()) { + if (condtion.key.equals(key)) + return condtion; + } + + throw new ApiException(GlobalApiCodes.PARAMETER_INVALID.formatMessage("入参条件类型不合法:{}", key)); + } + + public String key() { + return key; + } + + public String condition() { + return condition; + } + + public String desc() { + return desc; + } + + public String[] supports() { + return supports; + } + + public boolean needParam() { + return needParam; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/FieldRelation.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/FieldRelation.java new file mode 100644 index 00000000..f42e7882 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/FieldRelation.java @@ -0,0 +1,22 @@ +package com.dstz.base.query; + +/** + *
+ * 描述:查询字段之间的关系枚举。
+ * 
+ */ +public enum FieldRelation { + AND("AND"), + OR("OR"), + NOT("NOT"); + + private String key; + + FieldRelation(String key) { + this.key = key; + } + + public String key() { + return key; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/QuerySort.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/QuerySort.java new file mode 100644 index 00000000..1e1e8a17 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/QuerySort.java @@ -0,0 +1,59 @@ +package com.dstz.base.query; + +import java.util.Arrays; +import java.util.List; +import java.util.stream.Collectors; + +public class QuerySort { + public static final String ASC = "ASC"; + /** + * 需要进行排序的字段 + */ + private String column; + /** + * 是否正序排列,默认 true + */ + private Boolean asc = true; + + public String getColumn() { + return column; + } + public void setColumn(String column) { + this.column = column; + } + + public Boolean getAsc() { + return asc; + } + public void setAsc(Boolean asc) { + this.asc = asc; + } + + + public QuerySort(String column, Boolean asc) { + this.column = column; + this.asc = asc; + } + + public static QuerySort asc(String column) { + return build(column, true); + } + + public static QuerySort desc(String column) { + return build(column, false); + } + + + public static List ascs(String... columns) { + return Arrays.stream(columns).map(QuerySort::asc).collect(Collectors.toList()); + } + + public static List descs(String... columns) { + return Arrays.stream(columns).map(QuerySort::desc).collect(Collectors.toList()); + } + + private static QuerySort build(String column, boolean asc) { + return new QuerySort(column, asc); + } + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultAbQueryFilter.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultAbQueryFilter.java new file mode 100644 index 00000000..9a77ffb2 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultAbQueryFilter.java @@ -0,0 +1,478 @@ +package com.dstz.base.query.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.NamingCase; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.constats.ThreadMapKeyConstant; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.utils.ThreadMapUtil; +import com.dstz.base.query.AbPage; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.QuerySort; +import org.apache.ibatis.session.RowBounds; + +import javax.validation.Valid; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; + +/** + * 查询的默认实现 + * + * @author Jeff + */ +public class DefaultAbQueryFilter implements AbQueryFilter { + + /** + * 查询参数 + */ + private Map queryParam = null; + + /** + * 分页信息 + */ + private AbPage page = null; + + /** + * 查询分组,每组以括号组合条件 + */ + private DefaultQueryFieldGroup queryFieldGroup = null; + + /** + * 查询排序 + */ + private List querySortList = null; + + private List selectColumnNames; + + + @Override + public AbPage getPage() { + return this.page; + } + + @Override + public Map generateQuerySql() { + // 生成whereSQL + if (CollectionUtil.isNotEmpty(queryParam)) { + this.queryParam.put(WHERE_SQL, this.queryFieldGroup.getWhereSql()); + } + + // 生成OrderBySql + if (CollectionUtil.isNotEmpty(this.getQuerySortList())) { + StringBuilder orderBySql = new StringBuilder(); + for (QuerySort fieldSort : this.getQuerySortList()) { + orderBySql.append(fieldSort.getColumn()).append(" ").append(fieldSort.getAsc() ? "ASC" : "DESC ").append(","); + } + orderBySql.deleteCharAt(orderBySql.length() - 1); + + this.queryParam.put(ORDERBY_SQL, orderBySql.toString()); + } + + return queryParam; + } + + public DefaultAbQueryFilter() { + + } + + /** + * 构造函数 + */ + public DefaultAbQueryFilter(QueryParamDTO queryParamDto) { + initByQueryParamDto(queryParamDto, null, true); + } + + /** + * 构造函数 + * + * @param flag 是否自定转换下划线格式 + */ + public DefaultAbQueryFilter(QueryParamDTO queryParamDto, boolean flag) { + initByQueryParamDto(queryParamDto, null, flag); + } + + /** + * 构造函数 + * + * @param queryParamDto 查询条件的DTO + * @param accessQueryFilters 准入的查询条件 + */ + public DefaultAbQueryFilter(@Valid QueryParamDTO queryParamDto, Set accessQueryFilters) { + initByQueryParamDto(queryParamDto, accessQueryFilters, true); + } + + /** + * 构造函数 + * + * @param queryParamDto 查询条件的DTO + * @param accessQueryFilters 准入的查询条件 + * @param flag 是否自定转换下划线格式 + */ + public DefaultAbQueryFilter(@Valid QueryParamDTO queryParamDto, Set accessQueryFilters, boolean flag) { + initByQueryParamDto(queryParamDto, accessQueryFilters, flag); + } + + private void initByQueryParamDto(QueryParamDTO queryParamDto, Set accessQueryFilters, boolean flag) { + //查询条件初始化 + queryParam = MapUtil.newHashMap(queryParamDto.getQueryParam().size() + 5, false); + queryFieldGroup = new DefaultQueryFieldGroup(); + + // 转换查询列 + this.selectColumnNames = CollUtil.map(queryParamDto.getColumnNames(), s -> { + String to = StrUtil.removeSuffix(NamingCase.toUnderlineCase(s), StrPool.DOLLAR); + return s.endsWith("$") ? to : to + "_"; + }, true); + + this.initQueryCondition(queryParamDto.getQueryParam(), accessQueryFilters, flag); + + // 排序条件初始化,QueryDTO 设计仅支持一个排序 + if (StrUtil.isNotEmpty(queryParamDto.getSortColumn())) { + this.querySortList = Collections.singletonList( + //决定是否增加下划线 + new QuerySort(flag ? handleDbFiledName(queryParamDto.getSortColumn()) : queryParamDto.getSortColumn(), + StrUtil.equalsIgnoreCase(QuerySort.ASC, queryParamDto.getSortOrder()) + )); + } + //如果是否分页为false,则不分页。 分页对象不存在或者为true则分页 + if (null != queryParamDto.getEnablePage() && !queryParamDto.getEnablePage()) { + this.page = null; + } else { + boolean searchCount = ObjectUtil.defaultIfNull(queryParamDto.getSearchCount(), Boolean.TRUE); + if (queryParamDto.getOffset() != null && queryParamDto.getLimit() != null) { + RowBounds rowBounds = new RowBounds(queryParamDto.getOffset(), queryParamDto.getLimit()); + this.page = new AbPage(rowBounds, searchCount); + } else { + long currentPage = ObjectUtil.defaultIfNull(queryParamDto.getCurrentPage(), 1L); + long pageSize = ObjectUtil.defaultIfNull(queryParamDto.getPageSize(), 10L); + this.page = new AbPage(currentPage, pageSize, searchCount); + // 分页设置 + } + } + } + + + /** + * 处理页进来的请求参数。 + * + *
+     * 	1.参数字段命名规则。
+     * 	a:参数名称^参数类型+条件 eg:a$VEQ 则表示,a字段是varchar类型,条件是eq $后第一个参数为数据类型
+     * 	b:参数名字^参数类型  eg:b$V则表示,b字段是varchar类型 用于sql拼参数
+     * 	2.在这里构建的逻辑都是and逻辑。
+     * 3.参数类型:V :字符串 varchar N:数字number D:日期date
+     * 条件参数 枚举:QueryOP
+     *
+     * 
+ * + * @param accessQueryFilters + * @param queryConditionGroupParam + * @param accessQueryFilters + * @param flag 是否转换下划线 默认为转换 + */ + private void initQueryCondition(Map queryConditionGroupParam, Set accessQueryFilters, boolean flag) { + // 设置动态允许查询参数,兼容数据权限设置字段查询 + if (CollUtil.isNotEmpty(accessQueryFilters)) { + Optional.ofNullable(ThreadMapUtil.get(ThreadMapKeyConstant.QueryFilter.ACCESS_QUERY_FILTERS)) + .>map(CastUtils::cast) + .filter(CollUtil::isNotEmpty) + .map(accessQueryFilters::addAll); + } + queryConditionGroupParam.forEach((key, val) -> { + // 是否queryFilter约定入参 + if (val == null || !StrUtil.contains(key, StrPool.DOLLAR)) { + return; + } + + // 值为空跳过查询条件 + if (ObjectUtil.isEmpty(val)) { + return; + } + + // 参数中不能存在空格中断 + if (key.indexOf(StrPool.C_SPACE) != -1) { + throw new ApiException(GlobalApiCodes.PARAMETER_INVALID.formatMessage("参数不合法{},{}", key, val)); + } + + String[] aryParamKey = key.split("\\" + StrPool.DOLLAR); + if (aryParamKey.length != 2) { + return; + } + + // 参数出现了不允许的字段 + if (CollectionUtil.isNotEmpty(accessQueryFilters) && !accessQueryFilters.contains(aryParamKey[0])) { + throw new ApiException(GlobalApiCodes.PARAMETER_UNALLOWED.formatDefaultMessage(aryParamKey[0])); + } + + + // 转换成 下划线的,数据库的字段 + String fieldName = flag ? NamingCase.toUnderlineCase(aryParamKey[0]).concat(StrPool.UNDERLINE) : aryParamKey[0]; + String condition = aryParamKey[1];//截取条件 + String groupKey = ""; + if (condition.contains("_OR")) {//组标识 + groupKey = condition.split("_")[1]; + condition = condition.substring(0, condition.indexOf("_")); + } + + + ConditionType conditionType = null; + if (condition.length() > 1) { + conditionType = ConditionType.formKey(condition.substring(1)); + } + + Object nativeValue = handelValue(conditionType, val, condition); + + if (ConditionType.LESS.equals(conditionType) || ConditionType.LESS_EQUAL.equals(conditionType)) { + fieldName = fieldName + "-end"; + } + this.queryParam.put(fieldName.replace(CharUtil.DOT, CharUtil.UNDERLINE), nativeValue); + + // 部分条件为null,仅仅将参数放进去自行使用 + if (conditionType != null) { + this.queryFieldGroup.addQueryField(groupKey, fieldName, conditionType, nativeValue); + } + }); + } + + /** + * 将 Value 处理下 + * + * @param conditionType + * @param strValue + * @param condition + * @return + */ + public Object handelValue(ConditionType conditionType, Object strValue, String condition) { + + Object nativeValue = null; + + // in 类型的直接处理 + switch (conditionType) { + case IN: + case NOT_IN: + return getInValueSql(strValue); + + case LEFT_LIKE: + return "%" + String.valueOf(strValue); + + case RIGHT_LIKE: + return String.valueOf(strValue) + "%"; + + case LIKE: + return "%" + String.valueOf(strValue).replace("_", "\\_").replace("%", "%") + "%"; + } + + // 手动添加时无需转换数据类型 + if (condition == null) return strValue; + + String columnType = condition.substring(0, 1); + + // 默认其他的 执行格式操作 + if ("V".equals(columnType)) { + nativeValue = Convert.toStr(strValue); + } else if ("N".equals(columnType)) { + nativeValue = Convert.toNumber(strValue); + } else if ("D".equals(columnType)) { + nativeValue = DateUtil.parse(String.valueOf(strValue)); + } + + return nativeValue; + } + + /** + * 针对in查询方式,根据传入的value的类型做不同的处理。 value 是 string,则分隔字符串,然后合并为符合in规范的字符串。 + * value 是 list,则直接合并为符合in规范的字符串 + * + * @return + */ + private String getInValueSql(Object value) { + Iterable listValue = null; + if (value instanceof String) { + listValue = StrUtil.split((String) value, CharUtil.COMMA); + } else if (value instanceof Iterable) { + listValue = (Iterable) value; + } + + if (CollectionUtil.isEmpty(listValue)) { + return StrUtil.EMPTY; + } + + StringBuilder inSqlStr = new StringBuilder(); + for (Object obj : listValue) { + String valueString = Objects.isNull(obj) ? null : obj.toString(); + if (StrUtil.isNotEmpty(valueString)) { + inSqlStr.append(CharUtil.SINGLE_QUOTE) + .append(valueString) + .append(CharUtil.SINGLE_QUOTE) + .append(CharUtil.COMMA); + } + } + if (inSqlStr.charAt(inSqlStr.length() - 1) == CharUtil.COMMA) { + inSqlStr.deleteCharAt(inSqlStr.length() - 1); + } + return inSqlStr.toString(); + } + + public Map getQueryParam() { + return queryParam; + } + + public void setQueryParam(Map queryParam) { + this.queryParam = queryParam; + } + + public DefaultQueryFieldGroup getQueryFieldGroup() { + return queryFieldGroup; + } + + public void setQueryFieldGroup(DefaultQueryFieldGroup queryFieldGroup) { + this.queryFieldGroup = queryFieldGroup; + } + + public List getQuerySortList() { + return querySortList; + } + + public void setQuerySortList(List querySortList) { + this.querySortList = querySortList; + } + + public void setPage(AbPage page) { + this.page = page; + } + + public static DefaultAbQueryFilter build() { + DefaultAbQueryFilter filter = buildWithOutPage(); + filter.setPage(new AbPage()); + return filter; + } + + public static DefaultAbQueryFilter buildWithOutPage() { + DefaultAbQueryFilter filter = new DefaultAbQueryFilter(); + filter.setQueryParam(MapUtil.newHashMap(10)); + filter.setQueryFieldGroup(new DefaultQueryFieldGroup()); + return filter; + } + + public static AbQueryFilter buildWithFilter(String fildName, Object value, ConditionType conditionType) { + return build().addFilter(fildName, value, conditionType); + } + + public static AbQueryFilter buildWithEqFilter(String fildName, Object value) { + return build().addFilter(fildName, value, ConditionType.EQUAL); + } + + @Override + public AbQueryFilter noPage() { + this.page = null; + return this; + } + + + @Override + public AbQueryFilter addFilter(String fieldName, Object value, ConditionType conditionType) { + return this.addFilter(fieldName, value, conditionType, Boolean.TRUE, null); + } + + private AbQueryFilter addFilter(String fieldName, Object value, ConditionType conditionType, Boolean underline, String groupKey) { + if (StrUtil.isEmpty(fieldName) || value == null) { + return this; + } + // 转换成 下划线的,数据库的字段 + if (underline) { + fieldName = handleDbFiledName(fieldName); + } + + Object nativeValue = handelValue(conditionType, value, null); + + // TODO 把 queryFiled 改造成 filedName paramName 两个字段存储 + this.queryParam.put(fieldName.replace(CharUtil.DOT, CharUtil.UNDERLINE), nativeValue); + this.getQueryFieldGroup().addQueryField(groupKey, fieldName, conditionType, nativeValue); + + return this; + } + + @Override + public AbQueryFilter addFilterOriginal(String fieldName, Object value, ConditionType conditionType) { + return this.addFilter(fieldName, value, conditionType, Boolean.FALSE, null); + } + + @Override + public AbQueryFilter addFilter(String fieldName, Object value, ConditionType conditionType, String groupKey) { + return this.addFilter(fieldName, value, conditionType, Boolean.FALSE, groupKey); + } + + // 转换成 下划线的,数据库的字段 + private String handleDbFiledName(String filedName) { + String dbName = NamingCase.toUnderlineCase(filedName); + // 强制以 _ 结尾 + if (!dbName.endsWith(StrPool.UNDERLINE)) { + dbName = dbName.concat(StrPool.UNDERLINE); + } + return dbName; + } + + @Override + public AbQueryFilter likeFilter(String fildName, Object value) { + return this.addFilter(fildName, value, ConditionType.LIKE); + + } + + @Override + public AbQueryFilter inFilter(String fildName, Object value) { + return this.addFilter(fildName, value, ConditionType.IN); + } + + @Override + public AbQueryFilter notEqFilter(String fildName, Object value) { + return this.addFilter(fildName, value, ConditionType.NOT_EQUAL); + } + + @Override + public AbQueryFilter addParam(String fildName, Object value) { + this.getQueryParam().put(fildName, value); + return this; + } + + @Override + public AbQueryFilter clearQuery() { + this.getQueryParam().clear(); + this.getQuerySortList().clear(); + this.getQueryFieldGroup().getFieldGroupMap().clear(); + return this; + } + + + public static void main(String[] args) { + AbQueryFilter queryFilter = DefaultAbQueryFilter.buildWithFilter("b.userName", "张三", ConditionType.EQUAL) + .addFilter("a.userName", "张三,sss", ConditionType.IN); + + System.out.println(queryFilter.generateQuerySql()); + + + } + + @Override + public AbQueryFilter eqFilter(String fildName, Object value) { + return this.addFilter(fildName, value, ConditionType.EQUAL); + } + + @Override + public List getSelectColumnNames() { + return selectColumnNames; + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultQueryFieldGroup.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultQueryFieldGroup.java new file mode 100644 index 00000000..212b3099 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/DefaultQueryFieldGroup.java @@ -0,0 +1,129 @@ +package com.dstz.base.query.impl; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.enums.GlobalApiCodes; +//gitlab.agilebpm.cn/agilebpm-v5/agile-bpm +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.ConditionType; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; + +/** + * 查询分组 + *
+ * 组与组 之间 是 OR 条件,暂不支持调整
+ * eg: defaultGroup AND (groupA) OR (groupB) OR (groupC)
+ * 字段与字段 之间 默认是 AND 条件,暂不支持设置
+ * eg: (groupA) : ( name_ like 'xx' and age_ = '3' )
+ * 
+ * @author Jeff + * + */ +public class DefaultQueryFieldGroup { + public final static String DEFAULT_KEY = "AB_DGL_DK"; + + private Map> fieldGroupMap = null; + + + /** + * 初始化 queryGroup 大小 + */ + public DefaultQueryFieldGroup() { + this.fieldGroupMap = MapUtil.newHashMap(6); + } + + /** + * 只有IN 类型需要对参数直接拼接好,这个时候采用上inValue + * @param groupKey + * @param fieldName + * @param condition + * @param inValue + */ + public void addQueryField(String groupKey, String fieldName, ConditionType condition, Object inValue) { + if(StrUtil.isEmpty(groupKey)) { + groupKey = DEFAULT_KEY; + } + + if(condition == ConditionType.IN || condition == ConditionType.NOT_IN) { + if(StrUtil.isEmptyIfStr(inValue)) { + throw new ApiException(GlobalApiCodes.PARAMETER_INVALID); + } + } + List fields = fieldGroupMap.getOrDefault(groupKey, new ArrayList<>(10)); + fields.add(new QueryField(fieldName, condition, StrUtil.toStringOrNull(inValue)) ); + this.fieldGroupMap.put(groupKey, fields); + } + + public String getWhereSql() { + //默认组的条件 + String mainSql = genSql(DEFAULT_KEY); + StringBuilder orSql = new StringBuilder();//or组sql + + fieldGroupMap.forEach((key, val) -> { + if(DEFAULT_KEY.equals(key)) { + return; + } + String str = this.genSql(key); + + if(orSql.length()>0) { + orSql.append(" or "); + }else { + orSql.append("( "); + } + + orSql.append(str); + }); + + if(StrUtil.isNotEmpty(orSql)) { + orSql.append(" )"); + if(StrUtil.isNotEmpty(mainSql)) { + orSql.insert(0, "and"); + } + } + + if(StrUtil.isNotEmpty(mainSql)) { + orSql.insert(0, mainSql); + } + + return orSql.toString(); + + } + + private String genSql(String groupKey) { + List fields = fieldGroupMap.get(groupKey); + if (CollectionUtil.isEmpty(fields)) { + return StrUtil.EMPTY; + } + + StringBuilder sql = new StringBuilder("("); + + for (QueryField field : fields) { + if ( ! CharUtil.equals('(', sql.charAt(sql.length()-1), false)) { + sql.append(" AND "); + } + sql.append(field.getSql()); + } + + sql.append(")"); + + return sql.toString(); + } + + public Map> getFieldGroupMap() { + return fieldGroupMap; + } + + public void setFieldGroupMap(Map> fieldGroupMap) { + this.fieldGroupMap = fieldGroupMap; + } + + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/QueryField.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/QueryField.java new file mode 100644 index 00000000..f4a49655 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/query/impl/QueryField.java @@ -0,0 +1,127 @@ +package com.dstz.base.query.impl; + +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.FieldRelation; + +public class QueryField { + /** + * 字段名 + */ + private String fieldName; + /** + * 字段条件 + */ + private ConditionType condition; + + private String inValue ; + + /** + * 字段关联关系 默认AND + */ + private FieldRelation fieldRelation = FieldRelation.AND; + + + public QueryField(String fieldName,ConditionType condition,String inValue) { + this.setFieldName(fieldName); + this.setCondition(condition); + this.inValue = inValue; + } + // 处理多表SQL 比如 a.xxx 入参改成 a_xxx + private String getParamName() { + return fieldName.replace(CharUtil.DOT, CharUtil.UNDERLINE); + } + + public Object getSql() { + if (condition == null) { + condition = ConditionType.EQUAL; + } + + String fieldParamName = StrUtil.concat(true, "#{",getParamName(), "}"); + String conName=this.fieldName.contains("-")?this.fieldName.split("-")[0]:this.fieldName; + + + switch (condition) { + + // 相等 + case EQUAL: return StrUtil.concat(false, this.fieldName, " = ", fieldParamName); + + /*case EQUAL_IGNORE_CASE: return "upper(" + this.fieldName + ") = " + fieldParamName;*/ + + case LESS: return StrUtil.concat(false, conName, " < ", fieldParamName); + + case LESS_EQUAL: return StrUtil.concat(false, conName, " <= ", fieldParamName); + + case GREAT: return StrUtil.concat(false, this.fieldName, " > ", fieldParamName); + + case GREAT_EQUAL: return StrUtil.concat(false, this.fieldName, " >= ", fieldParamName); + + case NOT_EQUAL: return StrUtil.concat(false, this.fieldName, " != ", fieldParamName); + + case LEFT_LIKE: return StrUtil.concat(false, this.fieldName, " like ", fieldParamName); + + case RIGHT_LIKE: return StrUtil.concat(false, this.fieldName, " like ", fieldParamName); + + case LIKE: return StrUtil.concat(false, this.fieldName, " like ", fieldParamName); + + case NOTNULL: return this.fieldName.concat(" is not null "); + + case IS_NULL: return this.fieldName.concat(" is null "); + + case IN: return StrUtil.concat(false, this.fieldName, " in ( ",this.inValue ," )"); + + case NOT_IN: return StrUtil.concat(false, this.fieldName, " not in ( ",this.inValue ," )"); + + case BETWEEN: + // 在 ... 之间 + if (fieldName.endsWith("-end")) { + return fieldName.substring(0, fieldName.length() - 4) + " <= " + fieldParamName; + } else { + fieldName += " >= " + fieldParamName; + } + } + + return StrUtil.EMPTY; + } + + + public ConditionType getCondition() { + return condition; + } + public void setCondition(ConditionType condition) { + this.condition = condition; + } + + + public String getFieldName() { + return fieldName; + } + + + public void setFieldName(String fieldName) { + this.fieldName = fieldName; + } + + + public FieldRelation getFieldRelation() { + return fieldRelation; + } + + + public void setFieldRelation(FieldRelation fieldRelation) { + this.fieldRelation = fieldRelation; + } + + + public String getInValue() { + return inValue; + } + + + public void setInValue(String inValue) { + this.inValue = inValue; + } + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/EntityUtils.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/EntityUtils.java new file mode 100644 index 00000000..a8d2c2da --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/EntityUtils.java @@ -0,0 +1,35 @@ +package com.dstz.base.utils; + +import com.baomidou.mybatisplus.core.toolkit.LambdaUtils; +import com.baomidou.mybatisplus.core.toolkit.support.ColumnCache; +import com.baomidou.mybatisplus.core.toolkit.support.LambdaMeta; +import com.baomidou.mybatisplus.core.toolkit.support.SFunction; +import org.apache.ibatis.reflection.property.PropertyNamer; + +import java.util.Map; + +/** + * 实体操作工具 + * + * @author wacxhs + */ +public class EntityUtils { + + + /** + * 获取列名 + * + * @param func lambda function + * @param Entity + * @return 列名 + */ + public static String getColumnName(SFunction func) { + LambdaMeta lambdaMeta = LambdaUtils.extract(func); + String fieldName = PropertyNamer.methodToProperty(lambdaMeta.getImplMethodName()); + Map columnMap = LambdaUtils.getColumnMap(lambdaMeta.getInstantiatedClass()); + ColumnCache columnCache = columnMap.get(LambdaUtils.formatKey(fieldName)); + return columnCache.getColumn(); + } + + +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/JdbcUtils.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/JdbcUtils.java new file mode 100644 index 00000000..9692c554 --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/JdbcUtils.java @@ -0,0 +1,57 @@ +package com.dstz.base.utils; + +import cn.hutool.core.exceptions.ExceptionUtil; +import org.springframework.jdbc.datasource.DataSourceUtils; + +import javax.sql.DataSource; +import java.sql.Connection; +import java.sql.SQLException; + +/** + * jdbc 工具类 + * + * @author wacxhs + */ +public class JdbcUtils { + + /** + * 获取连接 schema + * + * @return 连接 schema + */ + public static String getConnectionSchema(DataSource dataSource) { + Connection connection = null; + try { + connection = DataSourceUtils.getConnection(dataSource); + return connection.getSchema(); + } catch (SQLException e) { + ExceptionUtil.wrapAndThrow(e); + return null; + } finally { + if (connection != null) { + DataSourceUtils.releaseConnection(connection, dataSource); + } + } + } + + /** + * 获取连接产品名称 + * + * @param dataSource 数据源 + * @return 连接产品名称 + */ + public static String getConnectionProductName(DataSource dataSource) { + Connection connection = null; + try { + connection = DataSourceUtils.getConnection(dataSource); + return connection.getMetaData().getDatabaseProductName(); + } catch (SQLException e) { + ExceptionUtil.wrapAndThrow(e); + return null; + } finally { + if (connection != null) { + DataSourceUtils.releaseConnection(connection, dataSource); + } + } + } +} diff --git a/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/PageUtils.java b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/PageUtils.java new file mode 100644 index 00000000..f4e1e77f --- /dev/null +++ b/ab-base/ab-base-mapper/src/main/java/com/dstz/base/utils/PageUtils.java @@ -0,0 +1,41 @@ +package com.dstz.base.utils; + + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; + +import java.util.List; +import java.util.function.Consumer; + +/** + * @author lightning + * @date 2023/04/24 + * 批量操作分批处理 + */ + +public class PageUtils { + + + /** + * @param pageSize 单次分页数量 + * @param list 需要分页的list + * @param handler 执行方法 + * @param + */ + public static void handle(int pageSize, List list, Consumer> handler) { + if (pageSize <= 0) { + throw new BusinessException(GlobalApiCodes.PARAMETER_INVALID.formatDefaultMessage("pageSize")); + } + if (list == null || list.isEmpty()) { + return; + } + if (list.size() <= pageSize) { + handler.accept(list); + } else { + for (int i = 0; i < list.size(); i += pageSize) { + handler.accept(list.subList(i, Math.min(i + pageSize, list.size()))); + } + } + } +} + diff --git a/ab-base/ab-base-web/pom.xml b/ab-base/ab-base-web/pom.xml new file mode 100644 index 00000000..f0bdcea4 --- /dev/null +++ b/ab-base/ab-base-web/pom.xml @@ -0,0 +1,52 @@ + + + + ab-base + com.dstz + 2.5.0 + + 4.0.0 + + ab-base-web + + + + com.dstz + ab-base-api + + + com.dstz + ab-base-common + + + com.dstz + ab-base-mapper + + + org.springframework + spring-context + + + org.springframework + spring-web + + + org.springframework + spring-aop + + + org.springframework + spring-aspects + + + org.springframework + spring-webmvc + + + jakarta.servlet + jakarta.servlet-api + + + \ No newline at end of file diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/api/WebSessionAttributesApi.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/api/WebSessionAttributesApi.java new file mode 100644 index 00000000..abe19796 --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/api/WebSessionAttributesApi.java @@ -0,0 +1,34 @@ +package com.dstz.base.web.api; + +import java.io.Serializable; +import java.util.Map; + +/** + * web会话属性操作,用于操作当前会话用户属性 + * + * @author wacxhs + */ +public interface WebSessionAttributesApi { + + /** + * 设置值 + * + * @param name 属性名 + * @param value 属性值,如果设置null则删除对应属性 + */ + void setValue(String name, Serializable value); + + /** + * 设置批量值 + * + * @param values 与setValue用法一样 + */ + void setValues(Map values); + + /** + * 获取值 + * + * @param name 属性名 + */ + Serializable getValue(String name); +} diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbBaseController.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbBaseController.java new file mode 100644 index 00000000..2ca6da86 --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbBaseController.java @@ -0,0 +1,12 @@ +package com.dstz.base.web.controller; + +/** + * AB 基类Controller + * + * @author jeff + */ +public abstract class AbBaseController { + + + +} diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbCrudController.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbCrudController.java new file mode 100644 index 00000000..73adafe2 --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/AbCrudController.java @@ -0,0 +1,137 @@ +package com.dstz.base.web.controller; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.ReflectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.AbRequestUtils; +import com.dstz.base.entity.IPersistentEntity; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import javax.validation.Valid; +import java.lang.reflect.Method; +import java.util.List; +import java.util.Set; + +/** + * 增删改查控制器 + * + * @param + * @author wacxhs + * @since 2022-01-24 + */ +public abstract class AbCrudController extends AbBaseController { + + @Autowired + protected AbBaseManager abBaseManager; + + /** + * 获取实体描述 + * + * @return 实体描述 + */ + protected abstract String getEntityDesc(); + + + /** + * 获取准入的过滤方法 + * @return + */ + public Set getAccessQueryFilters() { + return null; + } + + /** + * 分页列表 + */ + @RequestMapping(value = "listJson", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse listJson(@Valid @RequestBody QueryParamDTO queryParamDto){ + PageListDTO pageList = abBaseManager.query(new DefaultAbQueryFilter(queryParamDto,getAccessQueryFilters())); + return ApiResponse.success(pageList); + } + + /** + * 按实体ID获取实体记录 + * + * @param id 实体ID + * @return 接口响应-实体记录 + */ + @RequestMapping(value = "get", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse get(@RequestParam(name = "id") String id) { + T entity = null; + if (StringUtils.hasLength(id)) { + entity = abBaseManager.getById(id); + } + return ApiResponse.success(entity); + } + + /** + * 保存实体数据 + * + * @param entity 实体 + * @return 接口响应-实体ID + */ + @RequestMapping(value = "save", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse save(@Valid @RequestBody T entity) { + invokeBeforeCheck("saveCheck", entity); + String desc; + if (StringUtils.hasLength(entity.getId())) { + desc = "更新%s成功"; + Assert.isTrue(abBaseManager.update(entity) > 0, () -> new BusinessMessage(GlobalApiCodes.DATA_VERSION_OLD)); + } else { + desc = "添加%s成功"; + abBaseManager.create(entity); + } + return ApiResponse.success(entity.getId()).withMessage(String.format(desc, getEntityDesc())); + } + + /** + * 执行前检查 + * + * @param methodName 方法名称 + * @param param 方法参数 + */ + protected void invokeBeforeCheck(String methodName, Object param) { + Method saveCheck = ReflectUtil.getMethodByName(getClass(), methodName); + if (saveCheck != null) { + ReflectUtil.invoke(this, saveCheck, param); + } + } + + /** + * 实体批量删除 + * + * @param id 实体ID,多个,分隔 + * @return 接口响应 + */ + @RequestMapping(value = "remove", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse remove(@RequestParam(name = "id") String id) { + checkIsDemoEnvironment(); + List ids = StrUtil.split(id, CharUtil.COMMA); + invokeBeforeCheck("removeCheck", ids); + abBaseManager.removeByIds(ids); + final String message = String.format("删除%s成功", getEntityDesc()); + return ApiResponse.success().withMessage(message); + } + + // 演示环境禁止删除操作 + protected void checkIsDemoEnvironment() { + if("demoa5.tongzhouyun.com".equals(AbRequestUtils.getHttpServletRequest().getServerName())) { + throw new BusinessException(GlobalApiCodes.REMOTE_CALL_ERROR.formatDefaultMessage("演示环境禁止删除操作,访问信息已经被统计!")); + } + } + +} diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/advice/AbRestControllerAdvice.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/advice/AbRestControllerAdvice.java new file mode 100644 index 00000000..77bd98fd --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/advice/AbRestControllerAdvice.java @@ -0,0 +1,80 @@ +package com.dstz.base.web.controller.advice; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.exceptions.BusinessMessage; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.http.HttpStatus; +import org.springframework.validation.FieldError; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +import java.util.Objects; + +/** + * rest rest advice + * + * @author wacxhs + * @since 2022-01-22 + */ +@RestControllerAdvice +public class AbRestControllerAdvice { + + private static final Logger logger = LoggerFactory.getLogger(AbRestControllerAdvice.class); + + /** + * 参数校验异常处理器 + * + * @param validationException 校验异常 + * @return 接口响应 + */ + @ExceptionHandler(MethodArgumentNotValidException.class) + @ResponseStatus(HttpStatus.OK) + public ApiResponse validationExceptionHandler(MethodArgumentNotValidException validationException) { + ApiResponse apiResponse = new ApiResponse().withCode(GlobalApiCodes.PARAMETER_INVALID.getCode()); + FieldError fieldError = validationException.getBindingResult().getFieldError(); + if (Objects.nonNull(fieldError)) { + apiResponse.withMessage(fieldError.getDefaultMessage()); + } + return apiResponse; + } + + /** + * 内部错误处理器 + * + * @param throwable throwable + * @return 接口响应 + */ + @ExceptionHandler(Throwable.class) + @ResponseStatus(HttpStatus.OK) + public ApiResponse internalErrorHandler(Throwable throwable) { + Throwable rootCause = ExceptionUtil.getCausedBy(throwable, ApiException.class); + if(rootCause == null){ + rootCause = throwable; + } + // 提示性的不做堆栈记录 + if (rootCause instanceof BusinessMessage) { + return ApiResponse.fail(((BusinessMessage) rootCause).getBaseCode().getCode(), rootCause.getMessage()); + } + + logger.error(throwable.getMessage(), throwable); + + String code; + String message; + if(rootCause instanceof ApiException){ + ApiException apiException = (ApiException) rootCause; + code = apiException.getBaseCode().getCode(); + message = apiException.getBaseCode().getMessage(); + }else{ + code = GlobalApiCodes.INTERNAL_ERROR.getCode(); + message ="PROD".equalsIgnoreCase(SpringUtil.getActiveProfile()) ? GlobalApiCodes.INTERNAL_ERROR.getMessage() : rootCause.toString(); + } + return ApiResponse.fail(code, message); + } +} diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/aspect/AbControllerAspect.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/aspect/AbControllerAspect.java new file mode 100644 index 00000000..0803bd63 --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/controller/aspect/AbControllerAspect.java @@ -0,0 +1,149 @@ +package com.dstz.base.web.controller.aspect; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.MDCConstant; +import com.dstz.base.common.events.AbRequestLogEvent; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.requestlog.AbRequestLog; +import com.dstz.base.common.utils.AbRequestUtils; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import org.aopalliance.intercept.MethodInterceptor; +import org.aopalliance.intercept.MethodInvocation; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.slf4j.MDC; +import org.springframework.core.MethodParameter; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.method.HandlerMethod; +import org.springframework.web.servlet.HandlerMapping; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; +import javax.servlet.http.HttpServletRequest; +import java.util.*; + +/** + * 请求控制器AOP切面 + * + * @author wacxhs + */ +public class AbControllerAspect implements MethodInterceptor { + + private static final Logger logger = LoggerFactory.getLogger(AbControllerAspect.class); + + /** + * 获取当前用户 + * + * @return 当前用户 + */ + private Optional getCurrentUser() { + try { + return UserContextUtils.getUser(); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return Optional.empty(); + } + } + + /** + * 获取当前用户所在的当前组织 + * + * @return 当前组织 + */ + private Optional getCurrentGroup() { + try { + return UserContextUtils.getGroup(); + } catch (Exception e) { + logger.error(e.getMessage(), e); + return Optional.empty(); + } + } + + @Nullable + @Override + public Object invoke(@Nonnull MethodInvocation invocation) throws Throwable { + final HttpServletRequest request = Objects.requireNonNull(AbRequestUtils.getHttpServletRequest()); + final Map parameterMap = ServletUtil.getParamMap(request); + final String traceId = getTraceId(); + + HandlerMethod handlerMethod = (HandlerMethod) request.getAttribute(HandlerMapping.BEST_MATCHING_HANDLER_ATTRIBUTE); + // @PathVariable 获取 + Optional.ofNullable(request.getAttribute(HandlerMapping.URI_TEMPLATE_VARIABLES_ATTRIBUTE)).filter(ObjectUtil::isNotEmpty).>map(CastUtils::cast).ifPresent(parameterMap::putAll); + + AbRequestLog.Builder requestLogBuilder = AbRequestLog.newBuilder(); + requestLogBuilder.withTraceId(traceId); + getCurrentUser().ifPresent(u -> requestLogBuilder.withUserId(u.getUserId()).withUsername(u.getUsername())); + getCurrentGroup().ifPresent(g->requestLogBuilder.withGroupId(g.getGroupId()).withGroupName(g.getGroupName())); + requestLogBuilder.withClientIp(getClientIp(request)); + requestLogBuilder.withRequestMethod(request.getMethod()).withUrl(request.getRequestURL().toString()); + requestLogBuilder.withPathPattern((String) request.getAttribute(HandlerMapping.BEST_MATCHING_PATTERN_ATTRIBUTE)); + requestLogBuilder.withRequestTime(new Date()); + requestLogBuilder.withRequestHeaderMap(ServletUtil.getHeaderMap(request)).withRequestParameterMap(parameterMap); + requestLogBuilder.withRequestBody(Arrays.stream(handlerMethod.getMethodParameters()).filter(item -> item.hasParameterAnnotation(RequestBody.class)).map(MethodParameter::getParameterIndex).findFirst().map(parameterIndex -> invocation.getArguments()[parameterIndex]).orElse(null)); + // 放置到request请求中,供其他地方获取设值 + request.setAttribute(AbRequestLog.class.getName(), requestLogBuilder.build()); + + SpringUtil.publishEvent(AbRequestLogEvent.createPreProcess(requestLogBuilder.build())); + + Throwable throwable = null; + Object returnVal = null; + try { + returnVal = invocation.proceed(); + } catch (Throwable e) { + throwable = e; + } + requestLogBuilder.withResponseTime(new Date()); + // 再重新获取下用户信息,用于登录后信息获取 + UserContextUtils.getUser().ifPresent(u -> requestLogBuilder.withUserId(u.getUserId()).withUsername(u.getUsername())); + getCurrentGroup().ifPresent(g->requestLogBuilder.withGroupId(g.getGroupId()).withGroupName(g.getGroupName())); + // 异常信息 + if (Objects.nonNull(throwable)) { + Throwable causedBy = ExceptionUtil.getCausedBy(throwable, BusinessMessage.class); + // 提示性消息不记录到exception里 + if(causedBy != null) { + BusinessMessage businessMessage = (BusinessMessage) causedBy; + requestLogBuilder.withResponseBody(ApiResponse.fail(businessMessage.getBaseCode().getCode(), businessMessage.getMessage())); + }else{ + requestLogBuilder.withException(throwable); + } + SpringUtil.publishEvent(AbRequestLogEvent.createPostProcess(requestLogBuilder.build())); + throw throwable; + } + // 正常信息返回 + if (returnVal instanceof ResponseEntity) { + ResponseEntity responseEntity = (ResponseEntity) returnVal; + if (!(responseEntity.getBody() instanceof byte[])) { + requestLogBuilder.withResponseBody(responseEntity.getBody()); + } + } else { + requestLogBuilder.withResponseBody(returnVal); + } + SpringUtil.publishEvent(AbRequestLogEvent.createPostProcess(requestLogBuilder.build())); + return returnVal; + } + + private String getTraceId() { + String traceId = MDC.get(MDCConstant.TRACE_ID); + if (StringUtils.isNotEmpty(traceId)) { + return traceId; + } + return IdUtil.simpleUUID(); + } + + + private String getClientIp(HttpServletRequest request) { + String clientIp = ServletUtil.getClientIP(request); + return "0:0:0:0:0:0:0:1".equals(clientIp) ? "127.0.0.1" : clientIp; + } + +} diff --git a/ab-base/ab-base-web/src/main/java/com/dstz/base/web/filter/AbContextCleanFilter.java b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/filter/AbContextCleanFilter.java new file mode 100644 index 00000000..a5d18b09 --- /dev/null +++ b/ab-base/ab-base-web/src/main/java/com/dstz/base/web/filter/AbContextCleanFilter.java @@ -0,0 +1,36 @@ +package com.dstz.base.web.filter; + +import com.dstz.base.common.utils.ContextCleanUtils; +import org.springframework.core.Ordered; +import org.springframework.stereotype.Component; +import org.springframework.web.filter.OncePerRequestFilter; + +import javax.servlet.FilterChain; +import javax.servlet.ServletException; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; + +/** + * ab 上下文清理过滤器 + * + * @author wacxhs + */ +@Component("abContextCleanFilter") +public class AbContextCleanFilter extends OncePerRequestFilter implements Ordered { + + @Override + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws IOException, ServletException { + try { + ContextCleanUtils.executeAll(); + filterChain.doFilter(request, response); + } finally { + ContextCleanUtils.executeAll(); + } + } + + @Override + public int getOrder() { + return -200; + } +} diff --git a/ab-base/pom.xml b/ab-base/pom.xml new file mode 100644 index 00000000..f99e17cb --- /dev/null +++ b/ab-base/pom.xml @@ -0,0 +1,37 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-base + pom + + ab-base-api + ab-base-common + ab-base-mapper + ab-base-web + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + \ No newline at end of file diff --git a/ab-cms/ab-cms-api/pom.xml b/ab-cms/ab-cms-api/pom.xml new file mode 100644 index 00000000..185ed4d6 --- /dev/null +++ b/ab-cms/ab-cms-api/pom.xml @@ -0,0 +1,20 @@ + + + 4.0.0 + + com.dstz + ab-cms + 2.5.0 + + ab-cms-api + + + + + com.dstz + ab-base-common + + + diff --git a/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsConstant.java b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsConstant.java new file mode 100644 index 00000000..210285d4 --- /dev/null +++ b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsConstant.java @@ -0,0 +1,199 @@ +package com.dstz.cms.api.constant; + + +/** + * @author niu + * @description CMS信息模块内用到的常量 + */ +public class CmsConstant { + + + /** + * 发布状态:(未发布) + */ + public static int STATUS_UNPUBLISHED = 0; + + /** + * 发布状态:(已发布) + */ + public static int STATUS_PUBLISHED = 1; + + /** + * 发布状态:(下架) + */ + public static int STATUS_DOWN = 2; + + /** + * 已读状态 (未读) + */ + public static int TYPE_UN_READ = 0; + + /** + * 已读状态 (已读) + */ + public static int TYPE_IS_READ = 1; + + /** + * 已读状态 (过期) + */ + public static int TYPE_EXPIRED = 2; + + /** + * 查询条件 (标题模糊查询) + */ + public static String TITLE$VLK = "title$VLK"; + + /** + * 查询条件 (状态精确查询) + */ + public static String STATUS$VEQ = "status$VEQ"; + + /** + * 新闻或公告的评论数属性的字符串 + */ + public static String COMMENTS_NUM = "commentsNum"; + + /** + * 评论类型 (0公告, 1新闻) + */ + public static int COMMENT_TYPE_NOTIFY = 0; + + /** + * 评论类型 (0公告, 1新闻) + */ + public static int COMMENT_TYPE_NEWS = 1; + + /** + * 站内信的数据map的key-内容 + */ + public static String CONTENT = "content"; + + /** + * 站内信的数据map的key-标题 + */ + public static String SUBJECT = "subject"; + + /** + * 是否是所有人的key + */ + public static String ALL = "all"; + + /** + * user的key + */ + public static String USER = "user"; + + /** + * 编码的key + */ + public static String CODE = "code"; + + /** + * 角色的key + */ + public static String ROLE = "role"; + + /** + * 岗位的key + */ + public static String POST = "post"; + + /** + * 组织的key + */ + public static String ORG = "org"; + + /** + * 站内信推送类型(两种: 0通知, 1待办) + */ + public static int INNER_NOTICE = 0; + + /** + * 站内信推送类型(两种: 0通知, 1待办) + */ + public static int INNER_TODO = 1; + + /** + * 知识库我的文档类型(收藏, 我的, 借阅中, 借阅通过) + */ + public static String DOCUMENT_FAVORITE = "favorite"; + + /** + * 知识库我的文档类型(收藏, 我的, 借阅中, 借阅通过) + */ + public static String DOCUMENT_MY = "my"; + + /** + * 知识库我的文档类型(收藏, 我的, 借阅中, 借阅通过) + */ + public static String DOCUMENT_BORROW = "borrow"; + + /** + * 知识库我的文档类型(收藏, 我的, 借阅中, 借阅通过) + */ + public static String DOCUMENT_PASS = "pass"; + + + /** + * 知识库我的文档类型(已拒绝) + */ + public static String DOCUMENT_REJECT = "reject"; + + /** + * 知识库我的文档类型(已归还) + */ + public static String DOCUMENT_REVERT = "revert"; + + /** + * 知识库我的文档类型(无权限) + */ + public static String DOCUMENT_NONE = "none"; + + /** + * 知识库我的文档来源 字符串 + */ + public static String DOCUMENT_SOURCE = "source"; + /** + * 知识库的根目录ID + */ + public static String ROOT_ID = "0"; + + /** + * 知识库类型 目录 + */ + public static String FIELD = "field"; + /** + * 知识库类型 文档 + */ + public static String DOC = "doc"; + + /** + * 知识库的借阅为收藏的数据类型 + */ + public static Integer FAVORITE_TYPE = 1; + + /** + * 知识库 数据int类型bool: 有值/真 + */ + public static String INT_TRUE = "1"; + + /** + * 知识库 数据int类型bool: 空/假 + */ + public static String INT_FALSE = "0"; + + /** + * 公告/新闻标题模板字符串 + */ + public static String TITLE = "${title}"; + + /** + * 新闻模板code + */ + public static String NEWS_MESSAGE_TEMPLATE = "news_message_template"; + + /** + * 公告模板code + */ + public static String NOTIFY_MESSAGE_TEMPLATE = "notify_message_template"; +} diff --git a/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsStatusCode.java b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsStatusCode.java new file mode 100644 index 00000000..a438ad4b --- /dev/null +++ b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/constant/CmsStatusCode.java @@ -0,0 +1,44 @@ +package com.dstz.cms.api.constant; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * @author lzl + * @retrofit niu + * @description CMS模块错误码枚举类 + */ +public enum CmsStatusCode implements IBaseCode { + + PARAM_INVALID("paramInvalid", "参数无效,未找到匹配的数据"), + BUS_SRC_REL("busSrcRel", "要删除的资源被关联,无法进行删除"), + BUS_SRC_STS_UPDATE("busSrcStsUpdate", "要操作的资源状态已改变,无法进行操作"), + NOTIFY_READ_ONLY("notifyReadOnly", "当前公告已发布,已发布的公告不可修改"), + NEWS_READ_ONLY("newsReadOnly", "当前新闻已发布,已发布的新闻不可修改"), + NOTIFY_DELETE_DISABLED("notifyDeleteDisabled", "当前公告为发布状态,请先下架公告后才能删除!"), + NEWS_DELETE_DISABLED("newsDeleteDisabled", "当前新闻为发布状态,请先下架公告后才能删除!"), + DOCUMENT_DELETE_FAIL("documentDeleteFail", "删除失败,此目录下存在文档,请先修改文档归属目录!"), + BORROW_FAIL("borrowFail", "借阅失败,借阅已申请或已通过!"), + DOWNLOAD_FAIL("downloadFail", "下载失败,缺少必要参数!"), + DOWNLOAD_ERROR("downloadError", "下载出错,请联系管理员!"), + SAVE_ERROR("saveError", "保存失败,请稍后重试"), + COLUMN_TOO_LONG("columnTooLong", "字段[{}]太长,超出数据库字段限制!"), + PORTAL_DELETE_FAIL("portalDeleteFail", "【{}】为系统内置门户,禁止删除!"); + + private final String code; + private final String message; + + CmsStatusCode(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getMessage() { + return this.message; + } +} diff --git a/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/event/RemoveNotifyTypeEvent.java b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/event/RemoveNotifyTypeEvent.java new file mode 100644 index 00000000..cf1f2258 --- /dev/null +++ b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/event/RemoveNotifyTypeEvent.java @@ -0,0 +1,18 @@ +package com.dstz.cms.api.event; + +import org.springframework.context.ApplicationEvent; + +import java.util.Collection; +import java.util.List; + +public class RemoveNotifyTypeEvent extends ApplicationEvent { + + public RemoveNotifyTypeEvent(Collection ids) { + super(ids); + } + + @Override + public List getSource() { + return (List) super.getSource(); + } +} \ No newline at end of file diff --git a/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/model/FreemarkerData.java b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/model/FreemarkerData.java new file mode 100644 index 00000000..799defb7 --- /dev/null +++ b/ab-cms/ab-cms-api/src/main/java/com/dstz/cms/api/model/FreemarkerData.java @@ -0,0 +1,118 @@ +package com.dstz.cms.api.model; + +import com.dstz.base.common.utils.UserContextUtils; +import org.apache.commons.lang3.time.DateFormatUtils; + +import java.util.Date; + +/** + * 替换模版内数据的实体类 + * + * @author niu + * @since 2022-03-10 + */ + +public class FreemarkerData { + + /** + * 标题 (新闻/行程/公告/流程的名称) + */ + private String subject; + + /** + * 发表的意见(流程模块的待办/驳回需要填写) + */ + private String submitOpinion; + + /** + * 上一节点名称 (流程驳回需要填写) + */ + private String submitTaskname; + + /** + * 类型(中文:新闻/行程/公告/流程) + */ + private String type; + + /*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~下方属性可以忽略不填~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + + /** + * 发送人(自动填充,可以省略不写) + */ + private String senderName = UserContextUtils.getUser().get().getFullName(); + + /** + * 发送时间(自动填充,可以省略不写) + */ + private String sendTime = DateFormatUtils.format(new Date(), "yyyy-MM-dd HH:mm:ss"); + + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSenderName() { + return senderName; + } + + public void setSenderName(String senderName) { + this.senderName = senderName; + } + + public String getSendTime() { + return sendTime; + } + + public void setSendTime(String sendTime) { + this.sendTime = sendTime; + } + + public String getSubmitOpinion() { + return submitOpinion; + } + + public void setSubmitOpinion(String submitOpinion) { + this.submitOpinion = submitOpinion; + } + + public String getSubmitTaskname() { + return submitTaskname; + } + + public void setSubmitTaskname(String submitTaskname) { + this.submitTaskname = submitTaskname; + } + + /** + * 供流程快速创建使用(流程待办,流程驳回使用, submitTaskname属性为驳回时所需, 待办可以传null) + */ + public FreemarkerData(String subject, String submitOpinion, String submitTaskname) { + this.subject = subject; + this.submitOpinion = submitOpinion; + this.submitTaskname = submitTaskname; + } + + /** + * 快速创建构造对象(新闻,公告,行程,流程结束使用) + */ + public FreemarkerData(String subject) { + this.subject = subject; + } + + public FreemarkerData() { + } + +} diff --git a/ab-cms/ab-cms-core/pom.xml b/ab-cms/ab-cms-core/pom.xml new file mode 100644 index 00000000..4fa57370 --- /dev/null +++ b/ab-cms/ab-cms-core/pom.xml @@ -0,0 +1,52 @@ + + + 4.0.0 + + com.dstz + ab-cms + 2.5.0 + + ab-cms-core + + + + com.dstz + ab-base-web + + + com.dstz + ab-sys-api + + + com.dstz + ab-org-api + + + com.dstz + ab-cms-api + ${project.version} + + + com.dstz + ab-component-mq-api + + + com.dstz + ab-auth-api + 2.5.0 + compile + + + com.dstz + ab-sys-core + 2.5.0 + compile + + + com.dstz + ab-component-msg-api + + + diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsComments.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsComments.java new file mode 100644 index 00000000..334bfe65 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsComments.java @@ -0,0 +1,123 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.util.Date; + +/** + *

+ * 评论表 + *

+ * + * @author niu + * @since 2022-03-04 + */ +@TableName("cms_comments") +public class CmsComments extends AbModel { + + /** + * 评论纪录主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 公告或新闻纪录主键 + */ + @TableField("msg_id_") + private String msgId; + + /** + * 0:公告1:新闻 + */ + @TableField("comment_type_") + private int commentType; + + /** + * 评论内容 + */ + @TableField("comment_content_") + private String commentContent; + + /** + * 评论者主键 + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 评论人姓名 + */ + + @TableField(value = "creator_", fill = FieldFill.INSERT) + private String creator; + + /** + * 评论发表时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getMsgId() { + return msgId; + } + + public void setMsgId(String msgId) { + this.msgId = msgId; + } + + public int getCommentType() { + return commentType; + } + + public void setCommentType(int commentType) { + this.commentType = commentType; + } + + public String getCommentContent() { + return commentContent; + } + + public void setCommentContent(String commentContent) { + this.commentContent = commentContent; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFastMenu.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFastMenu.java new file mode 100644 index 00000000..4dd77139 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFastMenu.java @@ -0,0 +1,149 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.entity.AbModel; + +import java.util.Date; +import java.io.Serializable; + +/** + *

+ * 快捷菜单表 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@TableName("cms_fast_menu") +public class CmsFastMenu extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 归属人 + */ + @TableField("user_id_") + private String userId; + + /** + * 资源id + */ + @TableField("resource_id_") + private String resourceId; + + /** + * 是否为移动端(0否 1是) + */ + @TableField("mobile_") + private int mobile; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人 + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 为PC端批量保存时定制的构造方法 + */ + public CmsFastMenu(String userId, String resourceId) { + this.userId = userId; + this.resourceId = resourceId; + this.mobile = 0; + } + + /** + * 为移动端端批量保存时定制的构造方法 + */ + public CmsFastMenu(String resourceId) { + this.userId = UserContextUtils.getUserId(); + this.resourceId = resourceId; + this.mobile = 1; + } + + public CmsFastMenu(String id, String userId, String resourceId, int mobile, Date createTime, String createBy) { + this.id = id; + this.userId = userId; + this.resourceId = resourceId; + this.mobile = mobile; + this.createTime = createTime; + this.createBy = createBy; + } + + public CmsFastMenu() { + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + public int getMobile() { + return mobile; + } + + public void setMobile(int mobile) { + this.mobile = mobile; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFrequentUsed.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFrequentUsed.java new file mode 100644 index 00000000..089ea7cc --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsFrequentUsed.java @@ -0,0 +1,75 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; + +/** + *

+ * 常用流程管理 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@TableName("cms_frequent_used") +public class CmsFrequentUsed extends AbModel { + + /** + * 常用流程id + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 用户id + */ + @TableField("user_id_") + private String userId; + + /** + * 流程id + */ + @TableField("def_id_") + private String defId; + + public CmsFrequentUsed(String userId, String defId) { + this.userId = userId; + this.defId = defId; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getDefId() { + return defId; + } + + public void setDefId(String defId) { + this.defId = defId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNews.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNews.java new file mode 100644 index 00000000..b476d17e --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNews.java @@ -0,0 +1,306 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.util.Date; + +/** + *

+ * 新闻表 + *

+ * + * @author niu + * @since 2022-03-04 + */ +@TableName("cms_news") +public class CmsNews extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 新闻标题 + */ + @TableField("title_") + private String title; + + /** + * 新闻内容 + */ + @TableField("content_") + private String content; + + /** + * 轮播图片文件信息 + */ + @TableField("images_") + private String images; + + /** + * 附件信息 + */ + @TableField("attachments_") + private String attachments; + + /** + * 评论数量 + */ + @TableField("comments_num_") + private int commentsNum; + + /** + * 访问数量 + */ + @TableField("visit_num_") + private int visitNum; + + /** + * 发布时间 + */ + @TableField("release_time_") + private Date releaseTime; + + /** + * 发布人或下架人 + */ + @TableField("release_by_") + private String releaseBy; + + /** + * 发布人或下架人名字 + */ + @TableField("release_name_") + private String releaseName; + + /** + * 发布状态0未发布 1已发布 + */ + @TableField("status_") + private Integer status; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + + public void releaseNews(String userId, String fullName) { + this.releaseBy = userId; + this.releaseName = fullName; + this.status = 1; + this.releaseTime = new Date(); + } + + public void withdrawNews(String userId, String fullName) { + this.releaseBy = userId; + this.releaseName = fullName; + this.status = 2; + this.releaseTime = new Date(); + } + + public CmsNews(String id, String title, String content, String images, String attachments, int commentsNum, int visitNum, Date releaseTime, String releaseBy, String releaseName, Integer status, Date createTime, String createBy, String createOrgId, Date updateTime, String updater, String updateBy) { + this.id = id; + this.title = title; + this.content = content; + this.images = images; + this.attachments = attachments; + this.commentsNum = commentsNum; + this.visitNum = visitNum; + this.releaseTime = releaseTime; + this.releaseBy = releaseBy; + this.releaseName = releaseName; + this.status = status; + this.createTime = createTime; + this.createBy = createBy; + this.createOrgId = createOrgId; + this.updateTime = updateTime; + this.updater = updater; + this.updateBy = updateBy; + } + + public CmsNews() { + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getImages() { + return images; + } + + public void setImages(String images) { + this.images = images; + } + + public String getAttachments() { + return attachments; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } + + public Integer getCommentsNum() { + return commentsNum; + } + + public void setCommentsNum(Integer commentsNum) { + this.commentsNum = commentsNum; + } + + public int getVisitNum() { + return visitNum; + } + + public void setVisitNum(int visitNum) { + this.visitNum = visitNum; + } + + public Date getReleaseTime() { + return releaseTime; + } + + public void setReleaseTime(Date releaseTime) { + this.releaseTime = releaseTime; + } + + public String getReleaseBy() { + return releaseBy; + } + + public void setReleaseBy(String releaseBy) { + this.releaseBy = releaseBy; + } + + public String getReleaseName() { + return releaseName; + } + + public void setReleaseName(String releaseName) { + this.releaseName = releaseName; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotify.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotify.java new file mode 100644 index 00000000..52b63206 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotify.java @@ -0,0 +1,311 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; + +import java.util.Date; + +/** + * 系统公告表 + * + * @author niu + * @since 2022-02-28 + */ +@TableName("cms_notify") +public class CmsNotify extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 公告标题 + */ + @TableField("title_") + private String title; + + /** + * 公告内容 + */ + @TableField("content_") + private String content; + + /** + * 公告类型code + */ + @AbValueMap(type = AbValueMapType.DICT, fixValue = "gglx", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + @TableField("type_id_") + private String typeId; + + /** + * 附件json格式字符串信息 + */ + @TableField("attachments_") + private String attachments; + + /** + * 发布人或下架人根据状态来区分 + */ + @TableField("release_by_") + private String releaseBy; + + /** + * 发布人姓名 + */ + @TableField("release_name_") + private String releaseName; + + /** + * 发布时间或下架时间根据状态来区分 + */ + @TableField("release_time_") + private Date releaseTime; + + /** + * 发布状态0未发布1已发布2下架 + */ + @TableField("status_") + private Integer status; + + /** + * 评论数量 + */ + @TableField("comments_num_") + private int commentsNum; + + /** + * 访问数量 + */ + @TableField("visit_num_") + private int visitNum; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + public CmsNotify(String id, String title, String content, String typeId, String attachments, String releaseBy, String releaseName, Date releaseTime, Integer status, int commentsNum, int visitNum, Date createTime, String createBy, String createOrgId, Date updateTime, String updater, String updateBy) { + this.id = id; + this.title = title; + this.content = content; + this.typeId = typeId; + this.attachments = attachments; + this.releaseBy = releaseBy; + this.releaseName = releaseName; + this.releaseTime = releaseTime; + this.status = status; + this.commentsNum = commentsNum; + this.visitNum = visitNum; + this.createTime = createTime; + this.createBy = createBy; + this.createOrgId = createOrgId; + this.updateTime = updateTime; + this.updater = updater; + this.updateBy = updateBy; + } + + public CmsNotify() { + } + + @Override + public String getId() { + return id; + } + + public void releaseNotify(String userId, String fullName) { + this.releaseBy = userId; + this.releaseName = fullName; + this.status = 1; + this.releaseTime = new Date(); + } + + public void withdrawNotify(String userId, String fullName) { + this.releaseBy = userId; + this.releaseName = fullName; + this.status = 2; + this.releaseTime = new Date(); + } + + + + public String getTitle() { + return title; + } + + public String getContent() { + return content; + } + + public String getTypeId() { + return typeId; + } + + public String getAttachments() { + return attachments; + } + + public String getReleaseBy() { + return releaseBy; + } + + public String getReleaseName() { + return releaseName; + } + + public Date getReleaseTime() { + return releaseTime; + } + + public Integer getStatus() { + return status; + } + + public int getCommentsNum() { + return commentsNum; + } + + public int getVisitNum() { + return visitNum; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public void setTitle(String title) { + this.title = title; + } + + public void setContent(String content) { + this.content = content; + } + + public void setTypeId(String typeId) { + this.typeId = typeId; + } + + public void setAttachments(String attachments) { + this.attachments = attachments; + } + + public void setReleaseBy(String releaseBy) { + this.releaseBy = releaseBy; + } + + public void setReleaseName(String releaseName) { + this.releaseName = releaseName; + } + + public void setReleaseTime(Date releaseTime) { + this.releaseTime = releaseTime; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public void setCommentsNum(Integer commentsNum) { + this.commentsNum = commentsNum; + } + + public void setVisitNum(int visitNum) { + this.visitNum = visitNum; + } + + public void setCommentsNum(int commentsNum) { + this.commentsNum = commentsNum; + } + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyShare.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyShare.java new file mode 100644 index 00000000..5b960532 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyShare.java @@ -0,0 +1,83 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; + +/** + *

+ * 公告发布对应部门表 + *

+ * + * @author niu + * @since 2022-03-01 + */ +@TableName("cms_notify_share") +public class CmsNotifyShare extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 公告id + */ + @TableField("notify_id_") + private String notifyId; + + /** + * 部门id + */ + @TableField("group_id_") + private String groupId; + + /** + * 部门名 + */ + @TableField("group_name_") + private String groupName; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getNotifyId() { + return notifyId; + } + + public void setNotifyId(String notifyId) { + this.notifyId = notifyId; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public CmsNotifyShare(String notifyId, String groupId, String groupName) { + this.notifyId = notifyId; + this.groupId = groupId; + this.groupName = groupName; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyUser.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyUser.java new file mode 100644 index 00000000..32c3bc3a --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/CmsNotifyUser.java @@ -0,0 +1,81 @@ +package com.dstz.cms.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.util.Date; + +/** + * 公告用户关联对象和 + * + * @author niu + * @since 2022-02-28 + */ +@TableName("cms_notify_user") +public class CmsNotifyUser extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 公告id + */ + @TableField("notify_id_") + private String notifyId; + + /** + * 用户id + */ + @TableField("user_id_") + private String userId; + + /** + * (创建时间)阅读时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getNotifyId() { + return notifyId; + } + + public void setNotifyId(String notifyId) { + this.notifyId = notifyId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public CmsNotifyUser(String notifyId, String userId) { + this.notifyId = notifyId; + this.userId = userId; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsFileDTO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsFileDTO.java new file mode 100644 index 00000000..462cd4e8 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsFileDTO.java @@ -0,0 +1,57 @@ +package com.dstz.cms.core.entity.dto; + +import java.io.Serializable; + +/** + * CMD的附件属性DTO对象 + */ +public class CmsFileDTO implements Serializable { + + private String fileName; + + private String fileSize; + + private String fileType; + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFileSize() { + return fileSize; + } + + public void setFileSize(String fileSize) { + this.fileSize = fileSize; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public CmsFileDTO(String fileName, String fileSize, String fileType) { + this.fileName = fileName; + this.fileSize = fileSize; + this.fileType = fileType; + } + public CmsFileDTO(String fileName, String fileType) { + this.fileName = fileName; + this.fileType = fileType; + } + @Override + public String toString() { + return "CmsFileDTO{" + + "fileName='" + fileName + '\'' + + ", fileSize='" + fileSize + '\'' + + ", fileType='" + fileType + '\'' + + '}'; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsJsonDTO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsJsonDTO.java new file mode 100644 index 00000000..235e866a --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsJsonDTO.java @@ -0,0 +1,48 @@ +package com.dstz.cms.core.entity.dto; + +import java.io.Serializable; + +/** + * CMD的附件属性DTO对象 + */ +public class CmsJsonDTO implements Serializable { + + private String id; + + private String name; + + private String size; + + public CmsJsonDTO(String id, String name, String size) { + this.id = id; + this.name = name; + this.size = size; + } + + public CmsJsonDTO() { + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getSize() { + return size; + } + + public void setSize(String size) { + this.size = size; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNewsDTO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNewsDTO.java new file mode 100644 index 00000000..12466af8 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNewsDTO.java @@ -0,0 +1,28 @@ +package com.dstz.cms.core.entity.dto; + +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.cms.core.entity.CmsNews; + +import java.util.List; + +public class CmsNewsDTO extends CmsNews { + /** + * 公告关联的评论信息 (展示用) + */ + private List cmsCommentsList; + + public List getCmsCommentsList() { + return cmsCommentsList; + } + + public void setCmsCommentsList(List cmsCommentsList) { + this.cmsCommentsList = cmsCommentsList; + } + + public CmsNewsDTO() { + } + + public CmsNewsDTO(List cmsCommentsList) { + this.cmsCommentsList = cmsCommentsList; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNotifyDTO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNotifyDTO.java new file mode 100644 index 00000000..ec301887 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsNotifyDTO.java @@ -0,0 +1,27 @@ +package com.dstz.cms.core.entity.dto; + +import com.dstz.cms.core.entity.CmsNotify; + +import java.util.List; + + +public class CmsNotifyDTO extends CmsNotify { + + /** + * 要发布的部门Id集合 (添加用) + */ + private List groupIdList; + + + public CmsNotifyDTO() { + } + + public List getGroupIdList() { + return groupIdList; + } + + public void setGroupIdList(List groupIdList) { + this.groupIdList = groupIdList; + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsQueryParamDTO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsQueryParamDTO.java new file mode 100644 index 00000000..43a9218b --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/dto/CmsQueryParamDTO.java @@ -0,0 +1,32 @@ +package com.dstz.cms.core.entity.dto; + +import com.dstz.base.api.dto.QueryParamDTO; + +/** + * 查询条件的入参 + * @author Jeff + * + */ +public class CmsQueryParamDTO extends QueryParamDTO { + + /** + * 查询站内信的分页类型 (0通知, 1待办) + */ + private int type; + + + public CmsQueryParamDTO(int type) { + this.type = type; + } + + public CmsQueryParamDTO() { + } + + public int getType() { + return type; + } + + public void setType(int type) { + this.type = type; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsFastMenuVO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsFastMenuVO.java new file mode 100644 index 00000000..63ebf172 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsFastMenuVO.java @@ -0,0 +1,112 @@ +package com.dstz.cms.core.entity.vo; + +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serializable; + +/** + *

+ * 快捷菜单表 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@TableName("cms_fast_menu") +public class CmsFastMenuVO implements Serializable{ + + /** + * 主键 + */ + private String id; + + /** + * 归属人 + */ + private String userId; + + /** + * 资源id + */ + private String resourceId; + + /** + * 资源名称 + */ + private String resourceName; + + /** + * 图标code + */ + private String icon; + + /** + * 资源地址 + */ + private String resourceUrl; + + public CmsFastMenuVO(String id, String userId, String resourceId, String resourceName, String resourceUrl,String icon) { + this.id = id; + this.userId = userId; + this.resourceId = resourceId; + this.resourceName = resourceName; + this.resourceUrl = resourceUrl; + this.icon = icon; + } + + public CmsFastMenuVO( String resourceId, String resourceName, String resourceUrl,String icon) { + this.resourceId = resourceId; + this.resourceName = resourceName; + this.resourceUrl = resourceUrl; + this.icon = icon; + } + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getResourceId() { + return resourceId; + } + + public void setResourceId(String resourceId) { + this.resourceId = resourceId; + } + + public String getResourceName() { + return resourceName; + } + + public void setResourceName(String resourceName) { + this.resourceName = resourceName; + } + + public String getResourceUrl() { + return resourceUrl; + } + + public void setResourceUrl(String resourceUrl) { + this.resourceUrl = resourceUrl; + } + + public String getIcon() { + return icon; + } + + public void setIcon(String icon) { + this.icon = icon; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsHomeVO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsHomeVO.java new file mode 100644 index 00000000..382adf65 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsHomeVO.java @@ -0,0 +1,153 @@ +package com.dstz.cms.core.entity.vo; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.TableField; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 首页组件 + *

+ * + * @author niu + * @since 2022-04-11 + */ +public class CmsHomeVO implements Serializable { + + /** + * ID + */ + private String id; + + /** + * 名称 + */ + private String name; + + /** + * 编码 + */ + private String code; + + /** + * 图表类型: (内置部件、图表模块、ifream) + */ + private String type; + + /** + * 分类类型: 数据字典 :(纯粹自定义的分类筛选用) + */ + @AbValueMap(type = AbValueMapType.DICT, fixValue = "homeType", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + private String typeCode; + + /** + * 是否启用(0禁用 1启用) + */ + private Integer enable; + + /** + * 备注 + */ + private String remark; + + /** + * 创建人ID + */ + private String createBy; + + /** + * 创建人姓名 + */ + private String creator; + + + /** + * 创建时间 + */ + private Date createTime; + + + public Integer getEnable() { + return enable; + } + + public void setEnable(Integer enable) { + this.enable = enable; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsMyDocumentVO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsMyDocumentVO.java new file mode 100644 index 00000000..6a908385 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsMyDocumentVO.java @@ -0,0 +1,158 @@ +package com.dstz.cms.core.entity.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 知识库 + *

+ * + * @author niuniu + * @since 2022-08-19 + */ +public class CmsMyDocumentVO implements Serializable { + + /** + * id + */ + private String id; + + /** + * 文档名 + */ + private String name; + + /** + * 父目录ID + */ + private String parentId; + + /** + * 父目录名称 + */ + private String parentName; + + /** + * 关联文件名称 + */ + private String fileType; + + /** + * 继承id (继承=parentId, 非继承=自己id) + */ + private String rightsId; + + /** + * 阅读数量 + */ + private Integer readNum; + + /** + * 借阅人数 + */ + private Integer borrowNum; + + /** + * 权限名称(避免联查仅做展示,关系在权限表维护) + */ + private String rightsName; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 来自(收藏, 我的, 借阅中, 借阅通过) + */ + private String sources; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public String getFileType() { + return fileType; + } + + public void setFileType(String fileType) { + this.fileType = fileType; + } + + public String getRightsId() { + return rightsId; + } + + public void setRightsId(String rightsId) { + this.rightsId = rightsId; + } + + public Integer getReadNum() { + return readNum; + } + + public void setReadNum(Integer readNum) { + this.readNum = readNum; + } + + public Integer getBorrowNum() { + return borrowNum; + } + + public void setBorrowNum(Integer borrowNum) { + this.borrowNum = borrowNum; + } + + public String getRightsName() { + return rightsName; + } + + public void setRightsName(String rightsName) { + this.rightsName = rightsName; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getSources() { + return sources; + } + + public void setSources(String sources) { + this.sources = sources; + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyListVO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyListVO.java new file mode 100644 index 00000000..fa68cf2e --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyListVO.java @@ -0,0 +1,104 @@ +package com.dstz.cms.core.entity.vo; + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; + +import java.io.Serializable; +import java.util.Date; + + +public class CmsNotifyListVO implements Serializable { + + /** + * 主键 + */ + private String id; + + /** + * 公告标题 + */ + private String title; + + /** + * 公告类型code + */ + @AbValueMap(type = AbValueMapType.DICT, fixValue = "gglx", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + private String typeId; + + /** + * 已读状态 (0:未读, 1已读, 2过期) + */ + private Integer isRead; + + /** + * 评论数量 + */ + private Integer commentsNum; + + /** + * 访问数量 + */ + private Integer visitNum; + + /** + * 发布时间或下架时间根据状态来区分 + */ + private Date releaseTime; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getTypeId() { + return typeId; + } + + public void setTypeId(String typeId) { + this.typeId = typeId; + } + + public Integer getIsRead() { + return isRead; + } + + public void setIsRead(Integer isRead) { + this.isRead = isRead; + } + + public Date getReleaseTime() { + return releaseTime; + } + + public void setReleaseTime(Date releaseTime) { + this.releaseTime = releaseTime; + } + + public Integer getCommentsNum() { + return commentsNum; + } + + public void setCommentsNum(Integer commentsNum) { + this.commentsNum = commentsNum; + } + + public Integer getVisitNum() { + return visitNum; + } + + public void setVisitNum(Integer visitNum) { + this.visitNum = visitNum; + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyVO.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyVO.java new file mode 100644 index 00000000..1bb6f614 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/entity/vo/CmsNotifyVO.java @@ -0,0 +1,55 @@ +package com.dstz.cms.core.entity.vo; + +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.cms.core.entity.CmsNotify; +import com.dstz.cms.core.entity.CmsNotifyShare; + +import java.util.List; + + +public class CmsNotifyVO extends CmsNotify { + + /** + * 已读状态 (0:未读, 1已读, 2过期) + */ + protected Integer isRead; + + /** + * 公告关联的部门信息 (展示用) + */ + private List cmsNotifyShareList; + + /** + * 公告关联的评论信息 (展示用) + */ + private List cmsCommentsList; + + + public CmsNotifyVO() { + } + + public List getCmsCommentsList() { + return cmsCommentsList; + } + + public void setCmsCommentsList(List cmsCommentsList) { + this.cmsCommentsList = cmsCommentsList; + } + + public Integer getIsRead() { + return isRead; + } + + public void setIsRead(Integer isRead) { + this.isRead = isRead; + } + + public List getCmsNotifyShareList() { + return cmsNotifyShareList; + } + + public void setCmsNotifyShareList(List cmsNotifyShareList) { + this.cmsNotifyShareList = cmsNotifyShareList; + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/helper/CmsHelper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/helper/CmsHelper.java new file mode 100644 index 00000000..e2be10db --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/helper/CmsHelper.java @@ -0,0 +1,211 @@ +package com.dstz.cms.core.helper; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.entity.IPersistentEntity; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.cms.core.entity.dto.CmsJsonDTO; +import com.dstz.sys.api.SysFileApi; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.lang.reflect.Method; +import java.math.BigDecimal; +import java.text.DecimalFormat; +import java.util.Objects; + +import static com.dstz.cms.api.constant.CmsConstant.COMMENTS_NUM; +import static com.dstz.sys.api.constant.SysApiCodes.REFLEX_WARNING; + +/** + * CMS系统工具类 + * + * @author niu + * @since 2022-03-04 + */ +@Service +public class CmsHelper { + + @Autowired + private AbBaseMapper cmsNotifyMapper, cmsNewsMapper; + @Autowired + private SysFileApi sysFileApi; + + /** + * 更新新闻或公告的评论数量 + * + * @param commentType 业务类型(公告或者新闻) + * @param id 业务ID(公告ID或者新闻ID) + * @param flag 增加数量 true 减少数量 false + * @return int 改动条数 + */ + public int updateCommentsNum(int commentType, String id, boolean flag) { + try { + //根据传参确定mapper类型 + AbBaseMapper mapper = (AbBaseMapper) getInformation(commentType); + //获取新闻或者公告的实体 + IPersistentEntity iPersistentEntity = mapper.selectById(id); + if (iPersistentEntity == null) { + return 0; + } + int commentsNum; + commentsNum = Integer.parseInt(Objects.requireNonNull(getGetMethod(iPersistentEntity, COMMENTS_NUM)).toString()); + setValue(iPersistentEntity, iPersistentEntity.getClass(), COMMENTS_NUM, Integer.class, + flag ? commentsNum + 1 : commentsNum > 0 ? commentsNum - 1 : 0); + return mapper.updateById(iPersistentEntity); + } catch (Exception e) { + throw new BusinessException(REFLEX_WARNING); + } + } + + /** + * 根据评论类型判断应该调用哪个实现 + * + * @param commentType 评论类型(0公告, 1新闻) + * @return 操作父类 + */ + private AbBaseMapper getInformation(int commentType) { + return commentType == 0 ? cmsNotifyMapper : cmsNewsMapper; + } + + /** + * 通过json删除文件 + * + * @param json 关联得文件Json字符串 + */ + public void deleteFile(String json) { + if (StrUtil.isNotBlank(json)) { + sysFileApi.delete(Objects.requireNonNull(JsonUtils.parseArray(json, CmsJsonDTO.class)) + .stream().map(CmsJsonDTO::getId).toArray(String[]::new)); + } + } + + /** + * 根据属性,获取get方法 + * + * @param ob 对象 + * @param name 属性名 + */ + public static Object getGetMethod(Object ob, String name) throws Exception { + Method[] m = ob.getClass().getMethods(); + for (int i = m.length - 1; i >= 0; i--) { + if (("get" + name).equalsIgnoreCase(m[i].getName())) { + return m[i].invoke(ob); + } + } + return null; + } + + /** + * 根据属性,拿到set方法,并把值set到对象中 + * + * @param obj 对象 + * @param clazz 对象的class + * @param filedName 需要设置值得属性 + */ + public static void setValue(Object obj, Class clazz, String filedName, Class typeClass, Object value) { + filedName = removeLine(filedName); + String methodName = "set" + filedName.substring(0, 1).toUpperCase() + filedName.substring(1); + try { + Method method = clazz.getDeclaredMethod(methodName, typeClass); + method.invoke(obj, getClassTypeValue(typeClass, value)); + } catch (Exception ex) { + ex.printStackTrace(); + } + } + + /** + * 通过class类型获取获取对应类型的值 + * + * @param typeClass class类型 + * @param value 值 + * @return Object + */ + private static Object getClassTypeValue(Class typeClass, Object value) { + if (typeClass == int.class || value instanceof Integer) { + if (null == value) { + return 0; + } + return value; + } else if (typeClass == short.class) { + if (null == value) { + return 0; + } + return value; + } else if (typeClass == byte.class) { + if (null == value) { + return 0; + } + return value; + } else if (typeClass == double.class) { + if (null == value) { + return 0; + } + return value; + } else if (typeClass == long.class) { + if (null == value) { + return 0; + } + return value; + } else if (typeClass == String.class) { + if (null == value) { + return ""; + } + return value; + } else if (typeClass == boolean.class) { + if (null == value) { + return true; + } + return value; + } else if (typeClass == BigDecimal.class) { + if (null == value) { + return new BigDecimal(0); + } + return new BigDecimal(value + ""); + } else { + return typeClass.cast(value); + } + } + + + /** + * 处理字符串 如: abc_dex ---> abcDex + */ + public static String removeLine(String str) { + if (null != str && str.contains("_")) { + int i = str.indexOf("_"); + char ch = str.charAt(i + 1); + char newCh = (ch + "").substring(0, 1).toUpperCase().toCharArray()[0]; + String newStr = str.replace(str.charAt(i + 1), newCh); + return newStr.replace("_", ""); + } + return str; + } + + + /** + * 获取文件大小字符串 + * + * @param i 数字 + * @return 转换后的字符串 + */ + public static String getSize(long i) { + String result = ""; + long kb = 1024; + long mb = kb * 1024; + long gb = mb * 1024; + DecimalFormat df = new DecimalFormat("#.00"); + if (i >= gb) { + result = df.format((float) i / gb) + "GB"; + } else if (i >= mb) { + result = df.format((float) i / mb) + "MB"; + } else if (i >= kb) { + result = String.format("%.2f", (float) i / kb) + "KB"; + } else { + result = i + "B"; + } + return result; + } + +} \ No newline at end of file diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsCommentsManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsCommentsManager.java new file mode 100644 index 00000000..0ad9fb08 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsCommentsManager.java @@ -0,0 +1,16 @@ +package com.dstz.cms.core.manager; + +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.base.manager.AbBaseManager; + +/** + *

+ * 评论表 通用业务类 + *

+ * + * @author niu + * @since 2022-03-04 + */ +public interface CmsCommentsManager extends AbBaseManager { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFastMenuManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFastMenuManager.java new file mode 100644 index 00000000..4376d2e8 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFastMenuManager.java @@ -0,0 +1,43 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsFastMenu; +import com.dstz.cms.core.entity.vo.CmsFastMenuVO; + +import java.util.List; + +/** + *

+ * 快捷菜单管理 通用业务类 + *

+ * + * @author niu + * @since 2022-03-11 + */ +public interface CmsFastMenuManager extends AbBaseManager { + + /** + * 获取当前用户的快捷菜单(常用应用) + * @param type 是否为移动端 移动端:1 PC端:0 + */ + List getCmsFastMenuVO(int type); + + /** + * 清空当前用户的快捷菜单 + */ + void removeAll(); + + /** + * 批量新增快捷菜单 + * + * @param resourceIdList 菜单ID集合 + */ + void saveBatch(List resourceIdList); + + /** + * 新增快捷菜单 + * + * @param id 菜单ID集合 + */ + String saveOne(String id); +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFrequentUsedManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFrequentUsedManager.java new file mode 100644 index 00000000..1c571588 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsFrequentUsedManager.java @@ -0,0 +1,24 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsFrequentUsed; + +import java.util.List; + +/** + *

+ * 常用流程管理 通用业务类 + *

+ * + * @author niu + * @since 2022-03-11 + */ +public interface CmsFrequentUsedManager extends AbBaseManager { + + /** + * 批量新增常用流程 + * + * @param resourceIdList 常用流程ID集合 + */ + void saveBatch(List resourceIdList); +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNewsManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNewsManager.java new file mode 100644 index 00000000..28b75b74 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNewsManager.java @@ -0,0 +1,58 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsNews; +import com.dstz.cms.core.entity.dto.CmsNewsDTO; + +import java.util.List; + +/** + *

+ * 新闻表 通用业务类 + *

+ * + * @author niu + * @since 2022-03-04 + */ +public interface CmsNewsManager extends AbBaseManager { + /** + * 按实体ID获取实体 + * + * @param id 实体ID + * @return 实体记录 + */ + CmsNewsDTO details(String id); + + /** + * 新增或修改新闻 + * + * @param cmsNews 新闻对象 + */ + void saveOrUpdate(CmsNews cmsNews); + + /** + * 发布新闻 + * + * @param id 新闻id + */ + void releaseNews(String id); + + /** + * 下架新闻 + * + * @param id 新闻id + */ + void withdrawNews(String id); + + /** + * 分页列表 + */ + PageListDTO listJson(QueryParamDTO queryParamDto); + + /** + * 首页获取固定两条,精简后的新闻(剔除附件,和其他无用字段) + */ + PageListDTO getNewsPage(QueryParamDTO queryParamDto); +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyManager.java new file mode 100644 index 00000000..3a6b4509 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyManager.java @@ -0,0 +1,76 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsNotify; +import com.dstz.cms.core.entity.dto.CmsNotifyDTO; +import com.dstz.cms.core.entity.vo.CmsNotifyListVO; +import com.dstz.cms.core.entity.vo.CmsNotifyVO; + +import java.util.List; + +/** + *

+ * 系统公告表 通用业务类 + *

+ * + * @author niu + * @since 2022-02-28 + */ +public interface CmsNotifyManager extends AbBaseManager { + + /** + * 分页查询 + * + * @param queryParamDto 查询对象 + */ + PageListDTO page(QueryParamDTO queryParamDto); + + /** + * 查询指定ID的公告内容 + * + * @param id 公告ID + * @return CmsNotifyDTO 展示的公告详情 + */ + CmsNotifyVO details(String id); + + /** + * 创建公告 + * + * @param cmsNotifyDTO 新增的公告DTO对象 + */ + void save(CmsNotifyDTO cmsNotifyDTO); + + /** + * 修改公告 + * + * @param cmsNotifyDTO 修改的的公告DTO对象 + */ + void update(CmsNotifyDTO cmsNotifyDTO); + + /** + * 查看所属公告列表 (筛选公告关联的组织) + */ + PageListDTO getNotifyPage(QueryParamDTO queryParamDto); + + /** + * 查询用户未读公告数量 + */ + int queryUnReadCount(); + + /** + * 发布公告 + * + * @param id 公告id + */ + void releaseNotify(String id); + + /** + * 下架公告 + * + * @param id 公告id + */ + void withdrawNotify(String id); + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyShareManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyShareManager.java new file mode 100644 index 00000000..f60a209f --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyShareManager.java @@ -0,0 +1,46 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsNotifyShare; +import com.dstz.cms.core.entity.dto.CmsNotifyDTO; + +import java.util.List; + +/** + *

+ * 公告发布对应部门表 通用业务类 + *

+ * + * @author niu + * @since 2022-03-01 + */ +public interface CmsNotifyShareManager extends AbBaseManager { + + /** + * 保存组织和公告的关联关系 + * + * @param cmsNotifyDTO 公告对象 + */ + void saveRelation(CmsNotifyDTO cmsNotifyDTO); + + /** + * 通过公告ID删除关联的组织关系 + * + * @param cmsNotifyId 公告ID + */ + void deleteByNotifyId(String cmsNotifyId); + + /** + * 获取当前用户获取关联的公告ID集合 + * + */ + List getNotifyListByCurrentUser(); + + /** + * 获取公告关联的用户id集合 + * + */ + List getUserListByNotifyId(String cmsNotifyId); + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyUserManager.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyUserManager.java new file mode 100644 index 00000000..bd50acf2 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/CmsNotifyUserManager.java @@ -0,0 +1,36 @@ +package com.dstz.cms.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.cms.core.entity.CmsNotifyUser; + +import java.util.List; + +/** + *

+ * 公告用户关联表 通用业务类 + *

+ * + * @author niu + * @since 2022-02-28 + */ +public interface CmsNotifyUserManager extends AbBaseManager { + /** + * @param notifyId 公告ID + * @param userId 用户ID + * @return 是否阅读等信息的对象详情 + */ + CmsNotifyUser getNotifyUser(String notifyId, String userId); + + /** + * 获取当前用户已读的公告集合 + * + * @return 当前用户已读的公告集合 + */ + List getNotifyListByCurrentUser(); + + /** + * 删除公告相关得数据 + */ + void deleteByNotifyId(String notifyId); + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsCommentsManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsCommentsManagerImpl.java new file mode 100644 index 00000000..303fac1c --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsCommentsManagerImpl.java @@ -0,0 +1,49 @@ +package com.dstz.cms.core.manager.impl; + +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.cms.core.helper.CmsHelper; +import com.dstz.cms.core.manager.CmsCommentsManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 评论表 通用服务实现类 + * + * @author niu + * @since 2022-03-04 + */ +@Service("cmsCommentsManager") +public class CmsCommentsManagerImpl extends AbBaseManagerImpl implements CmsCommentsManager { + + @Autowired + private CmsHelper cmsManager; + + /** + * 新增评论 + * + * @param comments 评论对象 + */ + @Override + public int create(CmsComments comments) { + super.create(comments); + return cmsManager.updateCommentsNum(comments.getCommentType(), comments.getMsgId(), true); + } + + /** + * 删除评论 + * + * @param ids 评论ID集合 + */ + @Override + public int removeByIds(Collection ids) { + List cmsCommentsList = getBaseMapper().selectBatchIds(ids.stream().map(Object::toString).collect(Collectors.toList())); + cmsCommentsList.forEach(s -> cmsManager.updateCommentsNum(s.getCommentType(), s.getMsgId(), false)); + return super.removeByIds(ids); + } +} \ No newline at end of file diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFastMenuManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFastMenuManagerImpl.java new file mode 100644 index 00000000..7eae129a --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFastMenuManagerImpl.java @@ -0,0 +1,82 @@ +package com.dstz.cms.core.manager.impl; + +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.authentication.api.SysResourceApi; +import com.dstz.auth.authentication.api.model.ISysResource; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.cms.core.entity.CmsFastMenu; +import com.dstz.cms.core.entity.vo.CmsFastMenuVO; +import com.dstz.cms.core.manager.CmsFastMenuManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * 快捷菜单管理 通用服务实现类 + * + * @author niu + * @since 2022-03-11 + */ +@Service("cmsFastMenuManager") +public class CmsFastMenuManagerImpl extends AbBaseManagerImpl implements CmsFastMenuManager { + + @Autowired + private SysResourceApi sysResourceApi; + + @Override + public List getCmsFastMenuVO(int type) { + List cmsFastMenus = selectByWrapper(Wrappers.lambdaQuery() + .eq(CmsFastMenu::getUserId, UserContextUtils.getUserId()) + .eq(CmsFastMenu::getMobile, type)); + List result = new ArrayList<>(); + for (CmsFastMenu fastMenu : cmsFastMenus) { + ISysResource resource = sysResourceApi.getResourceById(fastMenu.getResourceId()); + if (resource != null) { + CmsFastMenuVO vo = new CmsFastMenuVO(fastMenu.getId(), fastMenu.getUserId(), fastMenu.getResourceId(), resource.getName(), resource.getUrl(), resource.getIcon()); + result.add(vo); + } + } + //如果为移动端,则需判断常用应用里是否包含快捷申请, 不包含就添加上去。 + if (type == 1) { + ISysResource todo = sysResourceApi.getTodoResource(); + if (result.stream().noneMatch(s -> StrUtil.equals(s.getResourceId(), todo.getId()))) { + result.add(new CmsFastMenuVO(todo.getId(), todo.getName(), todo.getUrl(), todo.getIcon())); + } + } + return result; + } + + @Override + public String saveOne(String id) { + CmsFastMenu cmsFastMenu = new CmsFastMenu(id); + create(cmsFastMenu); + return cmsFastMenu.getId(); + } + + /** + * 批量新增快捷菜单 + * + * @param resourceIdList 菜单ID集合 + */ + @Override + public void saveBatch(List resourceIdList) { + removeAll(); + Iterable cmsFastMenuIterable = IterUtil.asIterable(resourceIdList.stream().map(s -> + new CmsFastMenu(UserContextUtils.getUserId(), s)).iterator()); + bulkCreate(cmsFastMenuIterable); + } + + /** + * 清空当前用户的快捷菜单 + */ + @Override + public void removeAll() { + remove(Wrappers.lambdaQuery().eq(CmsFastMenu::getUserId, UserContextUtils.getUserId())); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFrequentUsedManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFrequentUsedManagerImpl.java new file mode 100644 index 00000000..c6e8f90f --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsFrequentUsedManagerImpl.java @@ -0,0 +1,40 @@ +package com.dstz.cms.core.manager.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.cms.core.entity.CmsFrequentUsed; +import com.dstz.cms.core.manager.CmsFrequentUsedManager; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 常用流程管理 通用服务实现类 + * + * @author niu + * @since 2022-03-11 + */ +@Service("cmsFrequentUsedManager") +public class CmsFrequentUsedManagerImpl extends AbBaseManagerImpl implements CmsFrequentUsedManager { + + /** + * 获取当前用户的所有常用流程 + * + * @return List 常用流程集合 + */ + @Override + public List list() { + return selectByWrapper(Wrappers.lambdaQuery().eq(CmsFrequentUsed::getUserId, UserContextUtils.getUserId())); + } + + /** + * 批量新增常用流程 + * + * @param defIdList 常用流程ID集合 + */ + @Override + public void saveBatch(List defIdList) { + defIdList.forEach(s -> create(new CmsFrequentUsed(UserContextUtils.getUserId(), s))); + } +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNewsManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNewsManagerImpl.java new file mode 100644 index 00000000..43eefe38 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNewsManagerImpl.java @@ -0,0 +1,236 @@ +package com.dstz.cms.core.manager.impl; + +import static com.dstz.base.common.constats.InnerMsgEnum.NEWS; +import static com.dstz.cms.api.constant.CmsConstant.COMMENT_TYPE_NEWS; +import static com.dstz.cms.api.constant.CmsConstant.NEWS_MESSAGE_TEMPLATE; +import static com.dstz.cms.api.constant.CmsConstant.TITLE; +import static com.dstz.cms.api.constant.CmsStatusCode.NEWS_DELETE_DISABLED; +import static com.dstz.cms.api.constant.CmsStatusCode.NEWS_READ_ONLY; +import static com.dstz.component.mq.api.constants.JmsTypeEnum.INNER; +import static java.util.stream.Collectors.toList; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.LinkedList; +import java.util.List; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.InnerMsgEnum; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.cms.core.entity.CmsNews; +import com.dstz.cms.core.entity.dto.CmsNewsDTO; +import com.dstz.cms.core.helper.CmsHelper; +import com.dstz.cms.core.manager.CmsCommentsManager; +import com.dstz.cms.core.manager.CmsNewsManager; +import com.dstz.cms.core.mapper.CmsNewsMapper; +import com.dstz.component.msg.api.MsgApi; +import com.dstz.component.msg.api.dto.MsgDTO; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IUser; +import com.dstz.sys.api.constant.SysApiCodes; +import com.google.common.collect.ImmutableMap; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; + +/** + * 新闻表 通用服务实现类 + * + * @author niu + * @since 2022-03-04 + */ +@Service("cmsNewsManager") +public class CmsNewsManagerImpl extends AbBaseManagerImpl implements CmsNewsManager { + + @Autowired + private CmsNewsMapper cmsNewsMapper; + @Autowired + private CmsCommentsManager cmsCommentsManager; + @Autowired + private UserApi userApi; + @Autowired + private CmsHelper cmsHelper; + @Autowired + private MsgApi msgApi; + + /** + * 分页列表 + */ + @Override + public PageListDTO listJson(QueryParamDTO paramDTO) { + return cmsNewsMapper.listJson(new DefaultAbQueryFilter(paramDTO)); + } + + /** + * 首页获取固定两条,精简后的新闻(剔除附件,和其他无用字段) + */ + @Override + public PageListDTO getNewsPage(QueryParamDTO paramDTO) { + return cmsNewsMapper.getNewsPage(new DefaultAbQueryFilter(paramDTO)); + } + + + /** + * 按实体ID获取实体 + * + * @param id 实体ID + * @return CmsNewsDTO 实体Dto记录 + */ + @Override + public CmsNewsDTO details(String id) { + CmsNews cmsNews = super.getById(id); + if (cmsNews.getStatus() == 1) { + //访问数加1 + cmsNews.setVisitNum(cmsNews.getVisitNum() + 1); + super.update(cmsNews); + // cmsInnerMsgManager.updateRead(NEWS, id); + } + return fillingNew(cmsNews); + } + + /** + * 新增或修改新闻 + * + * @param cmsNews 新闻对象 + */ + @Override + public void saveOrUpdate(CmsNews cmsNews) { + validate(cmsNews); + super.createOrUpdate(cmsNews); + } + + /** + * 发布新闻 + * + * @param id 新闻id + */ + @Override + public void releaseNews(String id) { + final IUser currentUser = UserContextUtils.getValidUser(); + + // 更新新闻状态 + CmsNews cmsNews = cmsNewsMapper.selectById(id); + cmsNews.setId(id); + cmsNews.releaseNews(currentUser.getUserId(), currentUser.getFullName()); + cmsNewsMapper.updateById(cmsNews); + + // 获取所有用户 + QueryParamDTO queryParamDTO = new QueryParamDTO(); + queryParamDTO.setLimit(500); + queryParamDTO.setOffset(0); + queryParamDTO.setSearchCount(Boolean.FALSE); + List sysIdentityList = new LinkedList<>(); + PageListDTO pageListDTO; + for (; CollUtil.isNotEmpty(pageListDTO = userApi.queryFilter(queryParamDTO)); queryParamDTO.setOffset(queryParamDTO.getOffset() + queryParamDTO.getLimit())) { + pageListDTO.stream().map(SysIdentity::new).forEach(sysIdentityList::add); + if (pageListDTO.size() != queryParamDTO.getLimit()) { + break; + } + } + CollUtil.clear(pageListDTO); + + //创建关联站内消息 (七个参数,1标题,2模板编号,3接收的用户集合,4业务ID,5消息类型,6发送通道集合,7携带参数) + if (CollUtil.isNotEmpty(sysIdentityList)) { + msgApi.sendMsg(new MsgDTO( + TITLE, + NEWS_MESSAGE_TEMPLATE, + sysIdentityList, + id, + InnerMsgEnum.NEWS.getKey(), + Collections.singletonList(INNER.getType()), + ImmutableMap.of( + "title", cmsNews.getTitle(), + "subject", cmsNews.getTitle(), + "senderName", cmsNews.getReleaseName(), + "sendTime", DateUtil.formatDateTime(cmsNews.getReleaseTime()))) + ); + } + } + + /** + * 下架新闻 + * + * @param id 新闻id + */ + @Override + public void withdrawNews(String id) { + //更改新闻信息 + IUser iUser = UserContextUtils.getUser().get(); + CmsNews cmsNews = super.getById(id); + cmsNews.withdrawNews(iUser.getUserId(), iUser.getFullName()); + update(cmsNews); + //新闻下架后同步站内消息状态为过期 + // cmsInnerMsgManager.expiredRead(NEWS, id); + } + + + /** + * 删除新闻集合 + * + * @param ids 新闻ids集合 + */ + @Override + public int removeByIds(Collection ids) { + List cmsNews = selectByIds(ids.stream().map(Object::toString).collect(toList())); + for (CmsNews cmsNew : cmsNews) { + //如果新闻已发布则不删除 + if (cmsNew.getStatus() == 1) { + throw new BusinessMessage(NEWS_DELETE_DISABLED); + } + //删除附件信息 + cmsHelper.deleteFile(cmsNew.getAttachments()); + //删除图片信息 + cmsHelper.deleteFile(cmsNew.getImages()); + //新闻下架后同步站内消息为过期状态 + // cmsInnerMsgManager.expiredRead(NEWS, cmsNew.getId()); + } + //批量删除新闻 + return super.removeByIds(ids); + } + + /** + * 补充其他表的内容,填充新闻DTO + * + * @param cmsNews 新闻实体对象 + * @return CmsNewsDTO 填充后的DTO对象 + */ + public CmsNewsDTO fillingNew(CmsNews cmsNews) { + CmsNewsDTO cmsNewsDTO = new CmsNewsDTO(cmsCommentsManager.selectByWrapper(Wrappers.lambdaQuery() + .eq(CmsComments::getCommentType, COMMENT_TYPE_NEWS).eq(CmsComments::getMsgId, cmsNews.getId()))); + BeanCopierUtils.copyProperties(cmsNews, cmsNewsDTO); + return cmsNewsDTO; + } + + /** + * 查重操作 + * + * @param cmsNews: 修改后的新对象 + **/ + private void validate(CmsNews cmsNews) { + //过滤状态为已发布的新闻 + if (StrUtil.isNotEmpty(cmsNews.getId()) && getById(cmsNews.getId()).getStatus() == 1) { + throw new BusinessException(NEWS_READ_ONLY); + } + //名称查重 + if (StrUtil.isEmpty(cmsNews.getId()) || !StrUtil.equals(getById(cmsNews.getId()).getTitle(), cmsNews.getTitle())) { + if (selectCount(Wrappers.lambdaQuery(CmsNews.class).eq(CmsNews::getTitle, cmsNews.getTitle())) > 0) { + throw new BusinessMessage(SysApiCodes.NAME_DUPLICATE); + } + } + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyManagerImpl.java new file mode 100644 index 00000000..b28fc446 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyManagerImpl.java @@ -0,0 +1,298 @@ +package com.dstz.cms.core.manager.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.InnerMsgEnum; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.cms.api.constant.CmsStatusCode; +import com.dstz.cms.api.model.FreemarkerData; +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.cms.core.entity.CmsNotify; +import com.dstz.cms.core.entity.CmsNotifyShare; +import com.dstz.cms.core.entity.CmsNotifyUser; +import com.dstz.cms.core.entity.dto.CmsNotifyDTO; +import com.dstz.cms.core.entity.vo.CmsNotifyListVO; +import com.dstz.cms.core.entity.vo.CmsNotifyVO; +import com.dstz.cms.core.helper.CmsHelper; +import com.dstz.cms.core.manager.*; +import com.dstz.cms.core.mapper.CmsNotifyMapper; +import com.dstz.component.mq.api.constants.JmsTypeEnum; +import com.dstz.component.mq.api.model.DefaultJmsDTO; +import com.dstz.component.msg.api.MsgApi; +import com.dstz.component.msg.api.dto.MsgDTO; +import com.dstz.org.api.model.IUser; +import com.google.common.collect.ImmutableMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.dao.DataIntegrityViolationException; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.*; +import java.util.stream.Collectors; + +import static com.dstz.base.common.constats.InnerMsgEnum.NOTIFY; +import static com.dstz.cms.api.constant.CmsConstant.*; +import static com.dstz.cms.api.constant.CmsStatusCode.NOTIFY_DELETE_DISABLED; +import static com.dstz.cms.api.constant.CmsStatusCode.NOTIFY_READ_ONLY; +import static com.dstz.component.mq.api.constants.JmsTypeEnum.INNER; +import static com.dstz.sys.api.constant.SysApiCodes.NAME_DUPLICATE; +import static java.util.stream.Collectors.toList; + + +/** + * 系统公告表 通用服务实现类 + * + * @author niu + * @since 2022-02-28 + */ +@Service("cmsNotifyManager") +public class CmsNotifyManagerImpl extends AbBaseManagerImpl implements CmsNotifyManager { + + @Autowired + private CmsNotifyUserManager cmsNotifyUserManager; + @Autowired + private CmsNotifyShareManager cmsNotifyShareManager; + @Autowired + private CmsCommentsManager cmsCommentsManager; + @Autowired + private CmsNotifyMapper cmsNotifyMapper; + @Autowired + private CmsHelper cmsHelper; + @Autowired + private MsgApi msgApi; + + /** + * 公告分页查询 + * + * @param queryParamDto 查询条件对象 + * @return PageListDTO 公告查询结果分页Page对象 + */ + @Override + public PageListDTO page(QueryParamDTO queryParamDto) { + PageListDTO result = query(new DefaultAbQueryFilter(queryParamDto, null)); + List cmsNotifyVOList = result.getRows().stream().map(this::fillingNotify).collect(toList()); + return new PageListDTO<>(result.getPageSize(), result.getPage(), result.getTotal(), cmsNotifyVOList); + } + + /** + * 查询指定ID的公告内容 + * + * @param id 公告ID + * @return cmsNotifyDto 公告Dto详情对象 + */ + @Override + public CmsNotifyVO details(String id) { + CmsNotify cmsNotify = super.getById(id); + if (STATUS_PUBLISHED == cmsNotify.getStatus()) { + relationOperate(cmsNotify); + } + CmsNotifyVO notify = fillingNotify(cmsNotify); + //填充关联的评论列表 + notify.setCmsCommentsList(cmsCommentsManager.selectByWrapper(Wrappers.lambdaQuery() + .eq(CmsComments::getCommentType, COMMENT_TYPE_NOTIFY).eq(CmsComments::getMsgId, cmsNotify.getId()))); + return notify; + } + + /** + * 新增公告 + * + * @param cmsNotifyDTO 公告DTO对象 + */ + @Override + public void save(CmsNotifyDTO cmsNotifyDTO) { + validate(null, cmsNotifyDTO); + try { + create(cmsNotifyDTO); + } catch (DataIntegrityViolationException e) { + String errorStr = e.getLocalizedMessage(); + if (errorStr.contains("Data too long for column")) { + String str = errorStr.substring(errorStr.indexOf("'") + 1); + String column = str.substring(0, str.indexOf("'")); + throw new BusinessException(CmsStatusCode.COLUMN_TOO_LONG.formatMessage("字段[{}]太长,超出数据库字段限制!", column)); + } else { + throw new BusinessException(CmsStatusCode.SAVE_ERROR); + } + } + cmsNotifyShareManager.saveRelation(cmsNotifyDTO); + } + + /** + * 修改公告 + * + * @param cmsNotifyDTO 修改的的公告DTO对象 + */ + @Override + public void update(CmsNotifyDTO cmsNotifyDTO) { + CmsNotify notify = super.getById(cmsNotifyDTO.getId()); + validate(notify, cmsNotifyDTO); + try { + super.update(cmsNotifyDTO); + } catch (DataIntegrityViolationException e) { + String errorStr = e.getLocalizedMessage(); + if (errorStr.contains("Data too long for column")) { + String str = errorStr.substring(errorStr.indexOf("'") + 1); + String column = str.substring(0, str.indexOf("'")); + throw new BusinessException(CmsStatusCode.COLUMN_TOO_LONG.formatMessage("字段[{}]太长,超出数据库字段限制!", column)); + } else { + throw new BusinessException(CmsStatusCode.SAVE_ERROR); + } + } + //删除旧的部门关联,文件关联等. 并保存新的关联部门 + cmsNotifyShareManager.deleteByNotifyId(notify.getId()); + cmsHelper.deleteFile(cmsNotifyDTO.getAttachments()); + cmsNotifyShareManager.saveRelation(cmsNotifyDTO); + } + + /** + * 查看所属公告列表 (筛选公告关联的组织) + */ + @Override + public PageListDTO getNotifyPage(QueryParamDTO paramDTO) { + DefaultAbQueryFilter queryFilter = new DefaultAbQueryFilter(paramDTO); + queryFilter.addParam("userId", UserContextUtils.getUserId()); + return cmsNotifyMapper.getNotifyPage(queryFilter); + } + + /** + * 查询用户未读公告数量 + */ + @Override + public int queryUnReadCount() { + //当前用户关联的公告集合 + List notifyIdList = cmsNotifyShareManager.getNotifyListByCurrentUser(); + //剔除已读的公告集合 + notifyIdList.removeAll(cmsNotifyUserManager.getNotifyListByCurrentUser()); + if (CollectionUtil.isEmpty(notifyIdList)) { + return 0; + } + long count = selectCount(Wrappers.lambdaQuery().eq(CmsNotify::getStatus, STATUS_PUBLISHED).in(CmsNotify::getId, notifyIdList)); + return Math.toIntExact(count); + } + + /** + * 发布公告 + * + * @param id 公告id + */ + @Override + public void releaseNotify(String id) { + //更改公告信息 + IUser iUser = UserContextUtils.getUser().get(); + CmsNotify cmsNotify = super.getById(id); + cmsNotify.releaseNotify(iUser.getUserId(), iUser.getFullName()); + update(cmsNotify); + //创建关联站内消息 (七个参数,1标题,2模板编号,3接收的用户集合,4业务ID,5消息类型,6发送通道集合,7携带参数) + msgApi.sendMsg(new MsgDTO( + TITLE, + NOTIFY_MESSAGE_TEMPLATE, + cmsNotifyShareManager.getUserListByNotifyId(id), + id, + InnerMsgEnum.NOTIFY.getKey(), + Collections.singletonList(INNER.getType()), + ImmutableMap.of( + "title", cmsNotify.getTitle(), + "subject", cmsNotify.getTitle(), + "senderName", cmsNotify.getReleaseName(), + "sendTime", DateUtil.formatDateTime(cmsNotify.getReleaseTime()))) + ); + } + + /** + * 下架公告 + * + * @param id 公告id + */ + @Override + public void withdrawNotify(String id) { + //更改公告信息 + IUser iUser = UserContextUtils.getUser().get(); + CmsNotify cmsNotify = super.getById(id); + cmsNotify.withdrawNotify(iUser.getUserId(), iUser.getFullName()); + update(cmsNotify); + //下架后删除已读标记,以及同步删除站内消息 + cmsNotifyUserManager.deleteByNotifyId(cmsNotify.getId()); + //将关联的站内消息置为过期状态 + // cmsInnerMsgManager.expiredRead(NOTIFY, id); + } + + /** + * 删除公告集合 + * + * @param ids 公告ids集合 + */ + @Override + public int removeByIds(Collection ids) { + for (CmsNotify cmsNotify : selectByIds(ids.stream().map(Object::toString).collect(Collectors.toList()))) { + if (STATUS_PUBLISHED == cmsNotify.getStatus()) { + throw new BusinessException(NOTIFY_DELETE_DISABLED); + } + //删除关联的组织信息、用户已读数据、绑定文件信息,站内消息等 + cmsNotifyShareManager.deleteByNotifyId(cmsNotify.getId()); + cmsNotifyUserManager.deleteByNotifyId(cmsNotify.getId()); + cmsHelper.deleteFile(cmsNotify.getAttachments()); + // cmsInnerMsgManager.expiredRead(NOTIFY, cmsNotify.getId()); + } + //批量删除公告 + return super.removeByIds(ids); + } + + /** + * 补充其他表的内容,填充公告DTO + * + * @param cmsNotify 公告实体对象 + * @return CmsNotifyDTO 填充后的DTO对象 + */ + private CmsNotifyVO fillingNotify(CmsNotify cmsNotify) { + CmsNotifyVO notifyVO = BeanCopierUtils.transformBean(cmsNotify, CmsNotifyVO.class); + notifyVO.setCmsNotifyShareList(cmsNotifyShareManager.selectByWrapper(Wrappers.lambdaQuery() + .eq(CmsNotifyShare::getNotifyId, cmsNotify.getId()))); + return notifyVO; + } + + /** + * 公告关联的相关操作, 判断是否已读, 创建已读对象,更新已读数量 + * + * @param cmsNotify 公告实体对象 + */ + private void relationOperate(CmsNotify cmsNotify) { + String userId = UserContextUtils.getUserId(); + //当前用户如果未读公告,创建关联已读关系 + if (cmsNotifyUserManager.getNotifyUser(cmsNotify.getId(), userId) == null) { + cmsNotifyUserManager.create(new CmsNotifyUser(cmsNotify.getId(), userId)); + } + // cmsInnerMsgManager.updateRead(NOTIFY, cmsNotify.getId()); + //无论是否已读,阅读量+1 + cmsNotify.setVisitNum(cmsNotify.getVisitNum() + 1); + update(cmsNotify); + } + + /** + * 查重操作 + * + * @param old: 之前的旧对象 + * @param cmsNotify: 修改后的新对象 + **/ + private void validate(CmsNotify old, CmsNotify cmsNotify) { + //过滤状态为已发布的公告 + if (null != old && old.getStatus() == 1) { + throw new BusinessException(NOTIFY_READ_ONLY); + } + //名称查重 + if (old == null || !StrUtil.equals(old.getTitle(), cmsNotify.getTitle())) { + if (selectCount(Wrappers.lambdaQuery(CmsNotify.class).eq(CmsNotify::getTitle, cmsNotify.getTitle())) > 0) { + throw new BusinessMessage(NAME_DUPLICATE); + } + } + } + +} \ No newline at end of file diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyShareManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyShareManagerImpl.java new file mode 100644 index 00000000..3ff4079e --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyShareManagerImpl.java @@ -0,0 +1,94 @@ +package com.dstz.cms.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.cms.core.entity.CmsNotifyShare; +import com.dstz.cms.core.entity.dto.CmsNotifyDTO; +import com.dstz.cms.core.manager.CmsNotifyShareManager; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.UserApi; +import com.dstz.org.api.model.IGroup; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +import static com.dstz.org.api.enums.GroupType.ORG; +import static java.util.stream.Collectors.toList; + +/** + * 公告发布对应部门表 通用服务实现类 + * + * @author niu + * @since 2022-03-01 + */ +@Service("cmsNotifyShareManager") +public class CmsNotifyShareManagerImpl extends AbBaseManagerImpl implements CmsNotifyShareManager { + + @Autowired + private GroupApi groupApi; + @Autowired + private UserApi userApi; + + /** + * 批量保存公告关联的部门关系 + * + * @param cmsNotifyDTO 公告对象 + */ + @Override + public void saveRelation(CmsNotifyDTO cmsNotifyDTO) { + List groupIdList = cmsNotifyDTO.getGroupIdList(); + if (CollectionUtil.isEmpty(groupIdList)) { + return; + } + for (String id : groupIdList) { + String groupName = groupApi.getByGroupId(ORG.getType(), id).getGroupName(); + create(new CmsNotifyShare(cmsNotifyDTO.getId(), id, groupName)); + } + } + + /** + * 通过公告ID删除关联的组织关系 + * + * @param notifyId 公告ID + */ + @Override + public void deleteByNotifyId(String notifyId) { + remove(Wrappers.lambdaQuery().eq(CmsNotifyShare::getNotifyId, notifyId)); + } + + /** + * 获取当前用户获取关联的公告ID集合 + */ + @Override + public List getNotifyListByCurrentUser() { + List groupIdList = groupApi.getByUserId(UserContextUtils.getUserId()).stream().map(IGroup::getGroupId).collect( + toList()); + return selectByWrapper(Wrappers.lambdaQuery().in(CmsNotifyShare::getGroupId, groupIdList + )).stream().map(CmsNotifyShare::getNotifyId).distinct().collect(Collectors.toList()); + } + + /** + * 获取公告关联的用户id集合 + */ + @Override + public List getUserListByNotifyId(String cmsNotifyId) { + //获取公告关联的组织ID集合 + List list = selectByWrapper(Wrappers.lambdaQuery().eq(CmsNotifyShare::getNotifyId, cmsNotifyId)).stream() + .map(CmsNotifyShare::getGroupId).collect(Collectors.toList()); + //获取组织ID集合下的所有用户 + return Optional.ofNullable(userApi.getByGroupTypeAndGroupIds(ORG.getType(), list)) + .map(IterUtil::asIterable) + .map(iter -> CollUtil.map(iter, SysIdentity::new, true)) + .orElseGet(Collections::emptyList); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyUserManagerImpl.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyUserManagerImpl.java new file mode 100644 index 00000000..bfe8e523 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/manager/impl/CmsNotifyUserManagerImpl.java @@ -0,0 +1,56 @@ +package com.dstz.cms.core.manager.impl; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.cms.core.entity.CmsNotifyUser; +import com.dstz.cms.core.manager.CmsNotifyUserManager; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.stream.Collectors; + +/** + * 公告用户关联表 通用服务实现类 + * + * @author niu + * @since 2022-02-28 + */ +@Service("cmsNotifyUserManager") +public class CmsNotifyUserManagerImpl extends AbBaseManagerImpl implements CmsNotifyUserManager { + + /** + * 获取指定公告ID指定用户ID的关联详情 + * + * @param notifyId 公告ID + * @param userId 用户ID + * @return CmsNotifyUser 是否已读公告以及已读时间的关联对象 + */ + @Override + public CmsNotifyUser getNotifyUser(String notifyId, String userId) { + return getBaseMapper().selectOne(Wrappers.lambdaQuery() + .eq(CmsNotifyUser::getNotifyId, notifyId) + .eq(CmsNotifyUser::getUserId, userId)); + } + + /** + * 获取当前用户已读的公告集合 + * + * @return 当前用户已读的公告集合 + */ + @Override + public List getNotifyListByCurrentUser() { + return selectByWrapper(Wrappers.lambdaQuery() + .eq(CmsNotifyUser::getUserId, UserContextUtils.getUserId())).stream() + .map(CmsNotifyUser::getNotifyId).collect(Collectors.toList()); + } + + /** + * 删除公告相关得数据 + */ + @Override + public void deleteByNotifyId(String notifyId) { + remove(Wrappers.lambdaQuery().eq(CmsNotifyUser::getNotifyId, notifyId)); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsCommentsMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsCommentsMapper.java new file mode 100644 index 00000000..9418e780 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsCommentsMapper.java @@ -0,0 +1,18 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.cms.core.entity.CmsComments; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 评论表 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-04 + */ +@Mapper +public interface CmsCommentsMapper extends AbBaseMapper { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFastMenuMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFastMenuMapper.java new file mode 100644 index 00000000..b38d5ba8 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFastMenuMapper.java @@ -0,0 +1,18 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.cms.core.entity.CmsFastMenu; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 快捷菜单管理 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@Mapper +public interface CmsFastMenuMapper extends AbBaseMapper { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFrequentUsedMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFrequentUsedMapper.java new file mode 100644 index 00000000..3fae99f1 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsFrequentUsedMapper.java @@ -0,0 +1,18 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.cms.core.entity.CmsFrequentUsed; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 常用流程管理 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@Mapper +public interface CmsFrequentUsedMapper extends AbBaseMapper { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNewsMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNewsMapper.java new file mode 100644 index 00000000..c7e5fdf4 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNewsMapper.java @@ -0,0 +1,36 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.cms.core.entity.CmsNews; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 新闻表 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-04 + */ +@Mapper +public interface CmsNewsMapper extends AbBaseMapper { + + /** + * 分页查询 + * + * @param defaultAbQueryFilter 查询条件 + * @return 分页数据 + */ + PageListDTO listJson(DefaultAbQueryFilter defaultAbQueryFilter); + + /** + * 首页获取精简后的新闻(剔除附件,和其他无用字段) + * + * @param defaultAbQueryFilter 查询条件 + * @return 新闻集合 + */ + PageListDTO getNewsPage(DefaultAbQueryFilter defaultAbQueryFilter); + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyMapper.java new file mode 100644 index 00000000..0c892921 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyMapper.java @@ -0,0 +1,26 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.cms.core.entity.CmsNotify; +import com.dstz.cms.core.entity.vo.CmsNotifyListVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + *

+ * 系统公告表 Mapper 接口 + *

+ * + * @author niu + * @since 2022-02-28 + */ +@Mapper +public interface CmsNotifyMapper extends AbBaseMapper { + + List queryNotify(String userId); + + PageListDTO getNotifyPage(DefaultAbQueryFilter defaultAbQueryFilter); +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyShareMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyShareMapper.java new file mode 100644 index 00000000..cbfa33c2 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyShareMapper.java @@ -0,0 +1,18 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.cms.core.entity.CmsNotifyShare; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 公告发布对应部门表 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-01 + */ +@Mapper +public interface CmsNotifyShareMapper extends AbBaseMapper { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyUserMapper.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyUserMapper.java new file mode 100644 index 00000000..4fd70803 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/mapper/CmsNotifyUserMapper.java @@ -0,0 +1,18 @@ +package com.dstz.cms.core.mapper; + +import com.dstz.cms.core.entity.CmsNotifyUser; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 公告用户关联表 Mapper 接口 + *

+ * + * @author niu + * @since 2022-02-28 + */ +@Mapper +public interface CmsNotifyUserMapper extends AbBaseMapper { + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsCommentsController.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsCommentsController.java new file mode 100644 index 00000000..a2315f39 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsCommentsController.java @@ -0,0 +1,27 @@ +package com.dstz.cms.core.rest; + + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.cms.core.entity.CmsComments; + +/** + * 评论管理 前端控制器 + * + * @author niu + * @since 2022-03-04 + */ +@RestController + +@RequestMapping(AbAppRestConstant.CMS_SERVICE_PREFIX + "/cmsComments") +public class CmsCommentsController extends AbCrudController { + + @Override + protected String getEntityDesc() { + return "评论管理"; + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFastMenuController.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFastMenuController.java new file mode 100644 index 00000000..e795de2c --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFastMenuController.java @@ -0,0 +1,83 @@ +package com.dstz.cms.core.rest; + + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.cms.core.entity.CmsFastMenu; +import com.dstz.cms.core.entity.vo.CmsFastMenuVO; +import com.dstz.cms.core.manager.CmsFastMenuManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.base.common.constats.AbAppRestConstant.CMS_SERVICE_PREFIX; + +/** + * 快捷菜单管理 前端控制器 + * + * @author niu + * @since 2022-03-11 + */ +@RestController +@RequestMapping(CMS_SERVICE_PREFIX + "/cmsFastMenu") +public class CmsFastMenuController extends AbCrudController { + + @Autowired + private CmsFastMenuManager cmsFastMenuManager; + + @Override + protected String getEntityDesc() { + return "快捷菜单管理"; + } + + /** + * 获取当前用户关联的快捷菜单 + * + * @return List 快捷菜单集合 + */ + @GetMapping("fastMenuList") + public ApiResponse> listByUser() { + return success(cmsFastMenuManager.getCmsFastMenuVO(0)); + } + + /** + * 获取当前用户关联的移动端快捷菜单 + * + * @return List 快捷菜单集合 + */ + @GetMapping("fastMenuMobileList") + public ApiResponse> mobileListByUser() { + return success(cmsFastMenuManager.getCmsFastMenuVO(1)); + } + + /** + * 新增快捷菜单 + * + * @param id 菜单ID + */ + @GetMapping("saveOne") + public ApiResponse saveOne(@RequestParam String id) { + return success(cmsFastMenuManager.saveOne(id)); + } + + /** + * 批量新增快捷菜单 + * + * @param resourceIdList 菜单ID集合 + */ + @PostMapping("saveBatch") + public ApiResponse saveBatch(@RequestBody List resourceIdList) { + return success(() -> cmsFastMenuManager.saveBatch(resourceIdList)); + } + + /** + * 清空当前用户的快捷菜单 + */ + @RequestMapping("removeAll") + public ApiResponse removeAll() { + return success(() -> cmsFastMenuManager.removeAll()); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFrequentUsedController.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFrequentUsedController.java new file mode 100644 index 00000000..fe700270 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsFrequentUsedController.java @@ -0,0 +1,56 @@ +package com.dstz.cms.core.rest; + + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.cms.core.entity.CmsFrequentUsed; +import com.dstz.cms.core.manager.CmsFrequentUsedManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.base.common.constats.AbAppRestConstant.CMS_SERVICE_PREFIX; + +/** + *

+ * 常用流程管理 前端控制器 + *

+ * + * @author niu + * @since 2022-03-11 + */ +@RestController +@RequestMapping(CMS_SERVICE_PREFIX + "/cmsFrequentUsed") +public class CmsFrequentUsedController extends AbCrudController { + + @Autowired + private CmsFrequentUsedManager cmsFrequentUsedManager; + + @Override + protected String getEntityDesc() { + return "常用流程管理"; + } + + /** + * 获取当前用户的所有常用流程 + * + * @return List 常用流程集合 + */ + @RequestMapping("list") + public ApiResponse> list() { + return success(cmsFrequentUsedManager.list()); + } + + /** + * 批量新增常用流程 + * + * @param defIdList 常用流程ID集合 + */ + @RequestMapping("saveBatch") + public ApiResponse saveBatch(@RequestBody List defIdList) { + return success(() -> cmsFrequentUsedManager.saveBatch(defIdList)); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNewsController.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNewsController.java new file mode 100644 index 00000000..45af733d --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNewsController.java @@ -0,0 +1,112 @@ +package com.dstz.cms.core.rest; + + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.api.vo.PageListVO; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.cms.core.entity.CmsNews; +import com.dstz.cms.core.entity.dto.CmsNewsDTO; +import com.dstz.cms.core.manager.CmsNewsManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.base.common.constats.AbAppRestConstant.CMS_SERVICE_PREFIX; + +/** + * 新闻管理 前端控制层 + * + * @author niu + * @since 2022-03-04 + */ +@RestController +@RequestMapping(CMS_SERVICE_PREFIX + "/cmsNews") +public class CmsNewsController extends AbCrudController { + + @Autowired + private CmsNewsManager cmsNewsManager; + + @Override + protected String getEntityDesc() { + return "新闻管理"; + } + + /** + * 分页列表 + */ + @Override + @PostMapping("listJson") + public ApiResponse> listJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + PageListDTO pageList = cmsNewsManager.listJson(queryParamDto); + return ApiResponse.success(pageList); + } + + /** + * 首页获取固定两条,精简后的新闻(剔除附件,和其他无用字段) + */ + @PostMapping("getNewsPage") + public ApiResponse> getNewsPage(@RequestBody QueryParamDTO queryParamDto) { + return success(cmsNewsManager.getNewsPage(queryParamDto)); + } + + /** + * 阅读了指定的新闻 + * + * @param id 新闻ID + + */ + @GetMapping("read") + public ApiResponse read(@RequestParam String id) { + return success(() -> cmsNewsManager.update(null, Wrappers.lambdaUpdate(CmsNews.class) + .eq(CmsNews::getId, id) + .setSql("visit_num_ = visit_num_ + 1"))); + } + + /** + * 查询指定ID的新闻内容 + * + * @param id 新闻ID + * @return ApiResponse 通用返回对象, 包含展示的新闻详情 + */ + @GetMapping("getOne") + public ApiResponse getById(@RequestParam String id) { + return success(cmsNewsManager.details(id)); + } + + /** + * 新增或修改新闻 + * + * @param cmsNews 新闻对象 + * @return 接口响应对象 ApiResponse + */ + @RequestMapping("saveOrUpdate") + public ApiResponse saveOrUpdate(@RequestBody CmsNews cmsNews) { + return success(() -> cmsNewsManager.saveOrUpdate(cmsNews)); + } + + /** + * 发布新闻 + * + * @param id 新闻id + */ + @RequestMapping("releaseNews/{id}") + public ApiResponse releaseNews(@PathVariable String id) { + return success(() -> cmsNewsManager.releaseNews(id)); + } + + /** + * 下架新闻 + * + * @param id 新闻id + */ + @RequestMapping("withdrawNews/{id}") + public ApiResponse withdrawNews(@PathVariable String id) { + return success(() -> cmsNewsManager.withdrawNews(id)); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNotifyController.java b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNotifyController.java new file mode 100644 index 00000000..f4566714 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/java/com/dstz/cms/core/rest/CmsNotifyController.java @@ -0,0 +1,120 @@ +package com.dstz.cms.core.rest; + + +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.api.vo.PageListVO; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.cms.core.entity.CmsNotify; +import com.dstz.cms.core.entity.dto.CmsNotifyDTO; +import com.dstz.cms.core.entity.vo.CmsNotifyListVO; +import com.dstz.cms.core.entity.vo.CmsNotifyVO; +import com.dstz.cms.core.manager.CmsNotifyManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.base.common.constats.AbAppRestConstant.CMS_SERVICE_PREFIX; + +/** + * 系统公告表 前端控制器 + * + * @author niu + * @since 2022-02-28 + */ +@RestController +@RequestMapping(CMS_SERVICE_PREFIX + "/cmsNotify") +public class CmsNotifyController extends AbCrudController { + + @Autowired + private CmsNotifyManager cmsNotifyManager; + + @Override + protected String getEntityDesc() { + return "系统公告模块"; + } + + /** + * 分页查询 + * + * @param queryParamDto 查询对象 + * @return PageListDTO 展示的公告列表 + */ + @RequestMapping("listJson") + @Override + public ApiResponse> listJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + return success(cmsNotifyManager.page(queryParamDto)); + } + + /** + * 查询指定ID的公告内容 + * + * @param id 公告ID + * @return ApiResponse 通用返回对象, 包含展示的公告详情 + */ + @GetMapping("getOne") + public ApiResponse getById(@RequestParam("id") String id) { + return success(cmsNotifyManager.details(id)); + } + + /** + * 查看所属公告列表 (筛选公告关联的组织) + */ + @RequestMapping("getNotifyPage") + public ApiResponse> getNotifyPage(@RequestBody QueryParamDTO queryParamDto) { + return success(cmsNotifyManager.getNotifyPage(queryParamDto)); + } + + /** + * 创建公告 + * + * @param cmsNotifyDTO 新增的公告DTO对象 + */ + @RequestMapping("saveDto") + public ApiResponse saveDto(@RequestBody CmsNotifyDTO cmsNotifyDTO) { + return success(() -> cmsNotifyManager.save(cmsNotifyDTO)); + } + + /** + * 修改公告 + * + * @param cmsNotifyDTO 修改的的公告DTO对象 + */ + @RequestMapping("updateDto") + public ApiResponse updateDto(@RequestBody CmsNotifyDTO cmsNotifyDTO) { + return success(() -> cmsNotifyManager.update(cmsNotifyDTO)); + } + + + /** + * 查询用户未读公告数量 + */ + @RequestMapping("queryUnReadCount") + public ApiResponse queryUnReadCount() { + return success(cmsNotifyManager.queryUnReadCount()); + } + + /** + * 发布公告 + * + * @param id 公告id + */ + @RequestMapping("releaseNotify/{id}") + public ApiResponse releaseNotify(@PathVariable String id) { + return success(() -> cmsNotifyManager.releaseNotify(id)); + } + + /** + * 下架公告 + * + * @param id 公告id + */ + @RequestMapping("withdrawNotify/{id}") + public ApiResponse withdrawNotify(@PathVariable String id) { + return success(() -> cmsNotifyManager.withdrawNotify(id)); + } + +} diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsChartMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsChartMapper.xml new file mode 100644 index 00000000..6706613b --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsChartMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentBorrowMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentBorrowMapper.xml new file mode 100644 index 00000000..dc5b5a10 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentBorrowMapper.xml @@ -0,0 +1,73 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentMapper.xml new file mode 100644 index 00000000..0b9dca1f --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsDocumentMapper.xml @@ -0,0 +1,162 @@ + + + + + + + + + + + + id_ + , name_, parent_id_,create_org_name_,type_, files_,file_type_, sn_, file_size_ ,outline_,c.rights_id_,read_num_,borrow_num_,rights_name_,create_by_,creator_,create_time_ + + + c + . + id_ + , c. name_, c. parent_id_, c.create_org_name_, c.files_, c.file_type_, file_size_ , c.outline_, c.rights_id_, c.read_num_, c.borrow_num_, c.rights_name_, c.create_by_, c.creator_, c.create_time_ + + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsHomeMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsHomeMapper.xml new file mode 100644 index 00000000..605b9726 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsHomeMapper.xml @@ -0,0 +1,33 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNewsMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNewsMapper.xml new file mode 100644 index 00000000..5295c007 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNewsMapper.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNotifyMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNotifyMapper.xml new file mode 100644 index 00000000..db39e587 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsNotifyMapper.xml @@ -0,0 +1,36 @@ + + + + + + + + + + + + + + + + + + distinct n.id_ , n.title_, n.type_id_, n.release_time_, n.comments_num_, n.visit_num_, m.status_ isRead + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsPortalMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsPortalMapper.xml new file mode 100644 index 00000000..f4c88181 --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsPortalMapper.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsUserConfigMapper.xml b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsUserConfigMapper.xml new file mode 100644 index 00000000..8fbe746c --- /dev/null +++ b/ab-cms/ab-cms-core/src/main/resources/com/dstz/cms/core/mapper/CmsUserConfigMapper.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/ab-cms/pom.xml b/ab-cms/pom.xml new file mode 100644 index 00000000..285587e7 --- /dev/null +++ b/ab-cms/pom.xml @@ -0,0 +1,18 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-cms + pom + + ab-cms-api + ab-cms-core + + diff --git a/ab-code-generator/pom.xml b/ab-code-generator/pom.xml new file mode 100644 index 00000000..604230ce --- /dev/null +++ b/ab-code-generator/pom.xml @@ -0,0 +1,76 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-code-generator + + + + org.springframework.boot + spring-boot-starter + + + mysql + mysql-connector-java + + + com.baomidou + mybatis-plus-generator + ${mybatis-plus.version} + + + com.baomidou + mybatis-plus-boot-starter + + + org.freemarker + freemarker + + + cn.hutool + hutool-core + + + org.springframework.boot + spring-boot-configuration-processor + + + + + ${artifactId} + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + \ No newline at end of file diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorApp.java b/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorApp.java new file mode 100644 index 00000000..7c1fbd17 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorApp.java @@ -0,0 +1,32 @@ +package com.dstz.code.generator; + +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.dstz.code.generator.core.AbCodeGenerator; +import com.dstz.code.generator.core.AbCodeGeneratorCommandLine; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.ConfigurableApplicationContext; + +/** + * AB 代码生成器 + * + * @author wacxhs + */ +@EnableConfigurationProperties(AbCodeGeneratorProperties.class) +@SpringBootApplication(exclude = MybatisPlusAutoConfiguration.class) +public class AbCodeGeneratorApp { + + public static void main(String[] args) { + ConfigurableApplicationContext applicationContext = SpringApplication.run(AbCodeGeneratorApp.class, args); + AbCodeGeneratorProperties codeGeneratorProperties = applicationContext.getBean(AbCodeGeneratorProperties.class); + AbCodeGenerator codeGenerator = applicationContext.getBean(AbCodeGenerator.class); + if (Boolean.TRUE.equals(codeGeneratorProperties.getEnableGui())) { + //TODO GUI + } else { + new AbCodeGeneratorCommandLine(codeGenerator).run(); + } + } + + +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorProperties.java b/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorProperties.java new file mode 100644 index 00000000..bf75f030 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/AbCodeGeneratorProperties.java @@ -0,0 +1,225 @@ +package com.dstz.code.generator; + +import com.baomidou.mybatisplus.annotation.IdType; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 生成器配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.code-generator") +public class AbCodeGeneratorProperties { + + /** + * 实体配置 + */ + private final EntityProperties entity = new EntityProperties(); + /** + * Mapper 配置 + */ + private final MapperProperties mapper = new MapperProperties(); + /** + * Manager 配置 + */ + private final ManagerProperties manager = new ManagerProperties(); + /** + * Controller 配置 + */ + private final ControllerProperties controller = new ControllerProperties(); + /** + * 启用GUI + */ + private Boolean enableGui = Boolean.FALSE; + + public Boolean getEnableGui() { + return enableGui; + } + + public void setEnableGui(Boolean enableGui) { + this.enableGui = enableGui; + } + + public EntityProperties getEntity() { + return entity; + } + + public MapperProperties getMapper() { + return mapper; + } + + public ManagerProperties getManager() { + return manager; + } + + public ControllerProperties getController() { + return controller; + } + + /** + * Entity 配置 + */ + public static final class EntityProperties { + + /** + * 基类 + */ + private String superClass = "com.dstz.base.entity.AbModel"; + + /** + * 乐观锁列名(数据库) + */ + private String versionColumnName = "rev_"; + + /** + * 乐观锁属性名(实体) + */ + private String versionPropertyName = "rev"; + + /** + * 主键类型 + */ + private IdType idType = IdType.ASSIGN_ID; + + public String getSuperClass() { + return superClass; + } + + public void setSuperClass(String superClass) { + this.superClass = superClass; + } + + public String getVersionColumnName() { + return versionColumnName; + } + + public void setVersionColumnName(String versionColumnName) { + this.versionColumnName = versionColumnName; + } + + public String getVersionPropertyName() { + return versionPropertyName; + } + + public void setVersionPropertyName(String versionPropertyName) { + this.versionPropertyName = versionPropertyName; + } + + public IdType getIdType() { + return idType; + } + + public void setIdType(IdType idType) { + this.idType = idType; + } + } + + /** + * Mapper 配置 + */ + public static final class MapperProperties { + + /** + * 基类 + */ + private String superClass = "com.dstz.base.mapper.AbBaseMapper"; + + public String getSuperClass() { + return superClass; + } + + public void setSuperClass(String superClass) { + this.superClass = superClass; + } + } + + /** + * Manager 配置 + */ + public static final class ManagerProperties { + + /** + * 通用接口类 + */ + private String interfaceClass = "com.dstz.base.manager.AbBaseManager"; + + /** + * 通用接口实现类 + */ + private String interfaceImplClass = "com.dstz.base.manager.impl.AbBaseManagerImpl"; + + /** + * 接口文件名格式化,%s:实体名 + */ + private String interfaceFileNameFormat = "%sManager"; + + /** + * 接口实现文件名格式化,%s:实体名 + */ + private String interfaceImplFileNameFormat = "%sManagerImpl"; + + public String getInterfaceClass() { + return interfaceClass; + } + + public void setInterfaceClass(String interfaceClass) { + this.interfaceClass = interfaceClass; + } + + public String getInterfaceImplClass() { + return interfaceImplClass; + } + + public void setInterfaceImplClass(String interfaceImplClass) { + this.interfaceImplClass = interfaceImplClass; + } + + public String getInterfaceFileNameFormat() { + return interfaceFileNameFormat; + } + + public void setInterfaceFileNameFormat(String interfaceFileNameFormat) { + this.interfaceFileNameFormat = interfaceFileNameFormat; + } + + public String getInterfaceImplFileNameFormat() { + return interfaceImplFileNameFormat; + } + + public void setInterfaceImplFileNameFormat(String interfaceImplFileNameFormat) { + this.interfaceImplFileNameFormat = interfaceImplFileNameFormat; + } + } + + /** + * Controller 配置 + */ + public static final class ControllerProperties { + + /** + * 基类 + */ + private String superClass = "com.dstz.base.web.controller.AbCrudController"; + + /** + * 开启 @RestController 注解 + */ + private Boolean enableRestStyle = Boolean.TRUE; + + public String getSuperClass() { + return superClass; + } + + public void setSuperClass(String superClass) { + this.superClass = superClass; + } + + public Boolean getEnableRestStyle() { + return enableRestStyle; + } + + public void setEnableRestStyle(Boolean enableRestStyle) { + this.enableRestStyle = enableRestStyle; + } + } +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGenerator.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGenerator.java new file mode 100644 index 00000000..37a48b98 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGenerator.java @@ -0,0 +1,237 @@ +package com.dstz.code.generator.core; + +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.generator.AutoGenerator; +import com.baomidou.mybatisplus.generator.config.DataSourceConfig; +import com.baomidou.mybatisplus.generator.config.GlobalConfig; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.PackageConfig; +import com.baomidou.mybatisplus.generator.config.StrategyConfig; +import com.baomidou.mybatisplus.generator.config.TemplateConfig; +import com.baomidou.mybatisplus.generator.config.rules.DateType; +import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine; +import com.baomidou.mybatisplus.generator.fill.Column; +import com.dstz.code.generator.AbCodeGeneratorProperties; +import org.springframework.boot.autoconfigure.jdbc.DataSourceProperties; +import org.springframework.stereotype.Component; + +import java.io.File; +import java.util.Map; + + +/** + * 代码生成器 + * + * @author wacxhs + */ +@Component +public class AbCodeGenerator { + + private final DataSourceProperties dataSourceProperties; + + private final AbCodeGeneratorProperties codeGeneratorProperties; + + public AbCodeGenerator(DataSourceProperties dataSourceProperties, AbCodeGeneratorProperties codeGeneratorProperties) { + this.dataSourceProperties = dataSourceProperties; + this.codeGeneratorProperties = codeGeneratorProperties; + } + + + public void run(AbCodeGeneratorModel codeGeneratorModel) { + new Runner(codeGeneratorModel).run(); + } + + + private final class Runner implements Runnable { + + private final AbCodeGeneratorModel codeGeneratorModel; + + public Runner(AbCodeGeneratorModel codeGeneratorModel) { + this.codeGeneratorModel = codeGeneratorModel; + } + + @Override + public void run() { + AutoGenerator autoGenerator = new AutoGenerator(dataSourceConfig()); + // 全局配置 + autoGenerator.global(globalConfig()); + // 模板配置 + autoGenerator.template(templateConfig()); + // 包信息 + autoGenerator.packageInfo(packageConfig(codeGeneratorModel.getPackageName())); + + // 策略配置 + StrategyConfig.Builder strategyConfigBuilder = strategyConfig(); + if (ArrayUtil.isNotEmpty(codeGeneratorModel.getIncludeTable())) { + strategyConfigBuilder.addInclude(codeGeneratorModel.getIncludeTable()); + } + if (ArrayUtil.isNotEmpty(codeGeneratorModel.getExcludeTable())) { + strategyConfigBuilder.addExclude(codeGeneratorModel.getExcludeTable()); + } + + //忽略表前缀配置 + strategyConfigBuilder.addTablePrefix(""); + + autoGenerator.strategy(strategyConfigBuilder.build()); + + // 生成执行 + autoGenerator.execute(new FreemarkerTemplateEngine()); + + moveMapperXml(autoGenerator.getConfig().getPathInfo()); + } + + private void moveMapperXml(Map pathInfo) { + final String searchStr = StrUtil.join(File.separator, "src", "main", "java"); + String mapperXmlPath = pathInfo.get(OutputFile.mapperXml); + int index = mapperXmlPath.lastIndexOf(searchStr); + File mapperXmlResourceDir = new File(mapperXmlPath.substring(0, index) + searchStr.replace("java", "resources") + mapperXmlPath.substring(index + searchStr.length())); + if (!mapperXmlResourceDir.exists()) { + mapperXmlResourceDir.mkdirs(); + } + + // 迁移文件 + for (File file : new File(mapperXmlPath).listFiles((dir, name) -> name.endsWith(".xml"))) { + FileUtil.move(file, mapperXmlResourceDir, true); + } + } + + GlobalConfig globalConfig() { + GlobalConfig.Builder builder = new GlobalConfig.Builder(); + // 覆盖已生成文件 + builder.fileOverride(); + // 禁止打开输出目录 + builder.disableOpenDir(); + // 作者名 + builder.author(codeGeneratorModel.getAuthor()); + // 时间策略 + builder.dateType(DateType.ONLY_DATE); + // 注释日期 + builder.commentDate("yyyy-MM-dd"); + + File rootDir; + if (StrUtil.isNotBlank(codeGeneratorModel.getOutputDir())) { + rootDir = new File(codeGeneratorModel.getOutputDir()); + } else { + rootDir = new File(getClass().getResource("/").getFile()).getParentFile(); + } + + // 指定输出目录 + File outputDir = new File(rootDir, StrUtil.join(File.separator, "code-generator", "src", "main", "java")); + if (!outputDir.exists()) { + outputDir.mkdirs(); + } + builder.outputDir(outputDir.getAbsolutePath()); + return builder.build(); + } + + DataSourceConfig dataSourceConfig() { + DataSourceConfig.Builder builder = new DataSourceConfig.Builder(dataSourceProperties.getUrl(), dataSourceProperties.getUsername(), dataSourceProperties.getPassword()); + // 数据库查询 + // builder.dbQuery(); + // 数据库 schema(部分数据库适用) + // builder.schema(); + // 数据库类型转换器 + // builder.typeConvert(); + // 数据库关键字处理器 + // builder.keyWordsHandler(); + return builder.build(); + } + + TemplateConfig templateConfig() { + TemplateConfig.Builder builder = new TemplateConfig.Builder(); + builder.service("/templates/manager.java"); + builder.serviceImpl("/templates/managerImpl.java"); + return builder.build(); + } + + PackageConfig packageConfig(String packageName) { + PackageConfig.Builder builder = new PackageConfig.Builder(); + // 上级包名 + builder.parent(packageName); + // manger + builder.service("manager"); + builder.serviceImpl("manager.impl"); + builder.xml("mapper"); + return builder.build(); + } + + StrategyConfig.Builder strategyConfig() { + StrategyConfig.Builder builder = new StrategyConfig.Builder(); + + // 开启跳过视图 + builder.enableSkipView(); + + // 实体字段填充 + Column[] entityTableFills = new Column[]{ + new Column("create_by_", FieldFill.INSERT), + new Column("creator_", FieldFill.INSERT), + new Column("create_org_id_", FieldFill.INSERT), + new Column("create_time_", FieldFill.INSERT), + new Column("update_by_", FieldFill.INSERT_UPDATE), + new Column("updater_", FieldFill.INSERT_UPDATE), + new Column("update_time_", FieldFill.INSERT_UPDATE), + new Column("rev_", FieldFill.INSERT) + }; + + builder + + // 实体策略配置 + .entityBuilder() + // 禁用生成 serialVersionUID + .disableSerialVersionUID() + // 开启链式模型 + // .enableChainModel() + // 开启 Boolean 类型字段移除 is 前缀 + .enableRemoveIsPrefix() + // 开启生成实体时生成字段注解 + .enableTableFieldAnnotation() + // 乐观锁字段名(数据库) + .versionColumnName(codeGeneratorProperties.getEntity().getVersionColumnName()) + // 乐观锁属性名(实体) + .versionPropertyName(codeGeneratorProperties.getEntity().getVersionPropertyName()) + // 开启生成字段常量 + // .enableColumnConstant() + // 开启 ActiveRecord 模型 + .enableActiveRecord() + // 设置父类 + .superClass(codeGeneratorProperties.getEntity().getSuperClass()) + // 添加父类公共字段 + // .addSuperEntityColumns("id_", "create_by_", "create_time_", "update_by_", "updater_", "update_time_", "rev") + // 添加表字段填充 + .addTableFills(entityTableFills) + // 全局主键类型 + .idType(IdType.ASSIGN_ID); + + // manager 策略配置 + builder.serviceBuilder() + // 设置 manager 接口父类 + .superServiceClass(codeGeneratorProperties.getManager().getInterfaceClass()) + // 设置 manager 实现类父类 + .superServiceImplClass(codeGeneratorProperties.getManager().getInterfaceImplClass()) + .formatServiceFileName(codeGeneratorProperties.getManager().getInterfaceFileNameFormat()) + .formatServiceImplFileName(codeGeneratorProperties.getManager().getInterfaceImplFileNameFormat()); + + // Mapper 策略配置 + builder.mapperBuilder() + // 设置父类 + .superClass(codeGeneratorProperties.getMapper().getSuperClass()) + // 开启 @Mapper 注解 + .enableMapperAnnotation() + .enableBaseResultMap(); + + // Controller 策略配置 + builder.controllerBuilder() + // 开启驼峰转连字符 + // .enableHyphenStyle() + // 开启生成@RestController 控制器 + .enableRestStyle() + .superClass(codeGeneratorProperties.getController().getSuperClass()); + + return builder; + } + } +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorCommandLine.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorCommandLine.java new file mode 100644 index 00000000..c7983a64 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorCommandLine.java @@ -0,0 +1,88 @@ +package com.dstz.code.generator.core; + +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; + +import java.util.Objects; +import java.util.Scanner; + +public class AbCodeGeneratorCommandLine { + + private final AbCodeGenerator codeGenerator; + + public AbCodeGeneratorCommandLine(AbCodeGenerator codeGenerator) { + this.codeGenerator = codeGenerator; + } + + public void run() { + AbCodeGeneratorModel codeGeneratorModel = new AbCodeGeneratorModel(); + Scanner scanner = new Scanner(System.in); + + System.out.println(StrUtil.center("填写信息", 30, '*')); + + System.out.print("保存目录(可选):"); + codeGeneratorModel.setOutputDir(StrUtil.trim(scanner.nextLine())); + + System.out.print("作者(可选):"); + codeGeneratorModel.setAuthor(StrUtil.trim(scanner.nextLine())); + + for (int i = 0; ; i++) { + if (i > 0) { + System.out.println(StrUtil.center("填写信息", 30, '*')); + codeGeneratorModel.setPackageName(null); + codeGeneratorModel.setIncludeTable(null); + codeGeneratorModel.setExcludeTable(null); + } + + // 填写包名 + while (StrUtil.isBlank(codeGeneratorModel.getPackageName())) { + System.out.print("包名(必填):"); + codeGeneratorModel.setPackageName(StrUtil.trim(scanner.nextLine())); + } + + System.out.print("指定表(可选,多表逗号(,)分隔):"); + String line = StrUtil.trimToNull(scanner.nextLine()); + codeGeneratorModel.setIncludeTable(Objects.isNull(line) ? null : line.split(StrUtil.COMMA)); + + if (ArrayUtil.isEmpty(codeGeneratorModel.getIncludeTable())) { + System.out.print("排除表(可选,多表逗号(,)分隔):"); + line = StrUtil.trimToNull(scanner.nextLine()); + codeGeneratorModel.setExcludeTable(Objects.isNull(line) ? null : line.split(StrUtil.COMMA)); + } + + System.out.println(StrUtil.center("生成信息", 30, '*')); + System.out.printf("保存目录:%s\n", codeGeneratorModel.getOutputDir()); + System.out.printf("作者:%s\n", codeGeneratorModel.getAuthor()); + System.out.printf("包名:%s\n", codeGeneratorModel.getPackageName()); + System.out.printf("指定表:%s\n", StrUtil.nullToEmpty(ArrayUtil.join(codeGeneratorModel.getIncludeTable(), StrUtil.COMMA))); + System.out.printf("排除表:%s\n", StrUtil.nullToEmpty(ArrayUtil.join(codeGeneratorModel.getExcludeTable(), StrUtil.COMMA))); + + System.out.println(StrUtil.repeat('*', 30)); + + System.out.print("(Y:确认 N:重新填写 E:退出):"); + + String cmd = StrUtil.trimToEmpty(scanner.nextLine()); + if (StrUtil.equalsIgnoreCase(cmd, "Y")) { + invokeGenerator(codeGeneratorModel); + System.out.println(StrUtil.repeat('*', 30)); + System.out.print("是否继续(Y/N):"); + if (!StrUtil.equalsIgnoreCase(StrUtil.trimToEmpty(scanner.nextLine()), "Y")) { + System.exit(0); + } + } else if (StrUtil.equalsIgnoreCase(cmd, "E")) { + System.exit(0); + } + } + } + + private void invokeGenerator(AbCodeGeneratorModel codeGeneratorModel) { + try { + codeGenerator.run(codeGeneratorModel); + } catch (AlertMessageException e) { + System.err.println(StrUtil.center("错误信息", 30, '*')); + System.out.println(e.getMessage()); + } + } + + +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorGui.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorGui.java new file mode 100644 index 00000000..48750bc9 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorGui.java @@ -0,0 +1,28 @@ +package com.dstz.code.generator.core; + +import javax.swing.*; +import java.awt.*; + +public class AbCodeGeneratorGui extends JFrame { + + + public AbCodeGeneratorGui() throws HeadlessException { + setTitle("代码生成器"); + setVisible(true); + initial(); + } + + public static void main(String[] args) { + EventQueue.invokeLater(AbCodeGeneratorGui::new); + } + + private void initial() { + + + + + + } + + +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorModel.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorModel.java new file mode 100644 index 00000000..a823fe68 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AbCodeGeneratorModel.java @@ -0,0 +1,76 @@ +package com.dstz.code.generator.core; + +import cn.hutool.core.util.StrUtil; + +/** + * 生成器参数对象 + * + * @author wacxhs + */ +public class AbCodeGeneratorModel { + + /** + * 输出目录 + */ + private String outputDir; + + /** + * 作者 + */ + private String author; + + /** + * 包名 + */ + private String packageName; + + /** + * 指定表,支持通配符 + */ + private String[] includeTable; + + /** + * 排除表,支持通配符 + */ + private String[] excludeTable; + + public String getOutputDir() { + return StrUtil.blankToDefault(outputDir, Constant.DEFAULT_OUTPUT_DIR); + } + + public void setOutputDir(String outputDir) { + this.outputDir = outputDir; + } + + public String getAuthor() { + return StrUtil.blankToDefault(author, System.getProperty("user.name")); + } + + public void setAuthor(String author) { + this.author = author; + } + + public String getPackageName() { + return packageName; + } + + public void setPackageName(String packageName) { + this.packageName = packageName; + } + + public String[] getIncludeTable() { + return includeTable; + } + + public void setIncludeTable(String[] includeTable) { + this.includeTable = includeTable; + } + + public String[] getExcludeTable() { + return excludeTable; + } + + public void setExcludeTable(String[] excludeTable) { + this.excludeTable = excludeTable; + } +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/AlertMessageException.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AlertMessageException.java new file mode 100644 index 00000000..bc4f9507 --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/AlertMessageException.java @@ -0,0 +1,9 @@ +package com.dstz.code.generator.core; + +public class AlertMessageException extends RuntimeException { + + + public AlertMessageException(String message) { + super(message, null, false, false); + } +} diff --git a/ab-code-generator/src/main/java/com/dstz/code/generator/core/Constant.java b/ab-code-generator/src/main/java/com/dstz/code/generator/core/Constant.java new file mode 100644 index 00000000..821ccf9c --- /dev/null +++ b/ab-code-generator/src/main/java/com/dstz/code/generator/core/Constant.java @@ -0,0 +1,20 @@ +package com.dstz.code.generator.core; + +import cn.hutool.core.util.StrUtil; + +import java.io.File; + +public class Constant { + + static final String DEFAULT_OUTPUT_DIR = getDefaultOutputDir(); + + private static String getDefaultOutputDir() { + File currentDir = new File(Constant.class.getResource("/").getFile()); + if (currentDir.isDirectory()) { + return new File(currentDir.getParentFile(), StrUtil.join(File.separator, "code-generator", "src", "main", "java")).getAbsolutePath(); + } else { + return StrUtil.join(File.separator, System.getProperty("user.dir"), "code-generator", "src", "main", "java"); + } + } + +} diff --git a/ab-code-generator/src/main/resources/application.yml b/ab-code-generator/src/main/resources/application.yml new file mode 100644 index 00000000..c1ee9b05 --- /dev/null +++ b/ab-code-generator/src/main/resources/application.yml @@ -0,0 +1,9 @@ +spring: + datasource: + url: jdbc:mysql://localhost:3306/agilebpm_5?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: root +logging: + level: + com: + baomidou: DEBUG \ No newline at end of file diff --git a/ab-code-generator/src/main/resources/templates/controller.java.ftl b/ab-code-generator/src/main/resources/templates/controller.java.ftl new file mode 100644 index 00000000..ebe43300 --- /dev/null +++ b/ab-code-generator/src/main/resources/templates/controller.java.ftl @@ -0,0 +1,45 @@ +package ${package.Controller}; + + +import org.springframework.web.bind.annotation.RequestMapping; + +<#if restControllerStyle> +import org.springframework.web.bind.annotation.RestController; +<#else> +import org.springframework.stereotype.Controller; + +<#if superControllerClassPackage??> +import ${superControllerClassPackage}; + +<#if superControllerClass??> +import ${package.Entity}.${entity}; + +/** + *

+ * ${table.comment!} 前端控制器 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if restControllerStyle> +@RestController +<#else> +@Controller + +@RequestMapping("<#if package.ModuleName?? && package.ModuleName != "">/${package.ModuleName}/<#if controllerMappingHyphenStyle?? && controllerMappingHyphenStyle>${controllerMappingHyphen}<#else>${table.entityPath}") +<#if kotlin> +class ${table.controllerName}<#if superControllerClass??> : ${superControllerClass}() +<#else> +<#if superControllerClass??> +public class ${table.controllerName} extends ${superControllerClass}<${entity}> { +<#else> +public class ${table.controllerName} { + + + @Override + protected String getEntityDesc() { + return "${table.comment!}"; + } +} + diff --git a/ab-code-generator/src/main/resources/templates/entity.java.ftl b/ab-code-generator/src/main/resources/templates/entity.java.ftl new file mode 100644 index 00000000..93d2c86d --- /dev/null +++ b/ab-code-generator/src/main/resources/templates/entity.java.ftl @@ -0,0 +1,148 @@ +package ${package.Entity}; + +<#list table.importPackages as pkg> +import ${pkg}; + +<#if swagger> +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; + +<#if entityLombokModel> +import lombok.Getter; +import lombok.Setter; + <#if chainModel> +import lombok.experimental.Accessors; + + +<#if activeRecord> +import java.io.Serializable; + + +/** + *

+ * ${table.comment!} + *

+ * + * @author ${author} + * @since ${date} + */ +<#if entityLombokModel> +@Getter +@Setter + <#if chainModel> +@Accessors(chain = true) + + +<#if table.convert> +@TableName("${schemaName}${table.name}") + +<#if swagger> +@ApiModel(value = "${entity}对象", description = "${table.comment!}") + +<#if superEntityClass??> +public class ${entity} extends ${superEntityClass}<#if activeRecord><${entity}> { +<#elseif activeRecord> +public class ${entity} extends Model<${entity}> { +<#elseif entitySerialVersionUID> +public class ${entity} implements Serializable { +<#else> +public class ${entity} { + +<#if entitySerialVersionUID> + + private static final long serialVersionUID = 1L; + +<#-- ---------- BEGIN 字段循环遍历 ----------> +<#list table.fields as field> + <#if field.keyFlag> + <#assign keyPropertyName="${field.propertyName}"/> + + + <#if field.comment!?length gt 0> + <#if swagger> + @ApiModelProperty("${field.comment}") + <#else> + /** + * ${field.comment} + */ + + + <#if field.keyFlag> + <#-- 主键 --> + <#if field.keyIdentityFlag> + @TableId(value = "${field.annotationColumnName}", type = IdType.AUTO) + <#elseif idType??> + @TableId(value = "${field.annotationColumnName}", type = IdType.${idType}) + <#elseif field.convert> + @TableId("${field.annotationColumnName}") + + <#-- 普通字段 --> + <#elseif field.fill??> + <#-- ----- 存在字段填充设置 -----> + <#if field.convert> + @TableField(value = "${field.annotationColumnName}", fill = FieldFill.${field.fill}) + <#else> + @TableField(fill = FieldFill.${field.fill}) + + <#elseif field.convert> + @TableField("${field.annotationColumnName}") + + <#-- 乐观锁注解 --> + <#if field.versionField> + @Version + + <#-- 逻辑删除注解 --> + <#if field.logicDeleteField> + @TableLogic + + private ${field.propertyType} ${field.propertyName}; + +<#assign commonColumnNames=["id", "id_", "create_by_", "create_time_", "update_by_", "updater_", "update_time_", "rev_"]> +<#------------ END 字段循环遍历 ----------> +<#if !entityLombokModel> + <#list table.fields as field> + <#if field.propertyType == "boolean"> + <#assign getprefix="is"/> + <#else> + <#assign getprefix="get"/> + + <#if commonColumnNames?seq_contains(field.name?lower_case)> + <#assign methodOverrideAnnotationString="\n @Override"/> + <#else> + <#assign methodOverrideAnnotationString=""/> + + ${methodOverrideAnnotationString} + public ${field.propertyType} ${getprefix}${field.capitalName}() { + return ${field.propertyName}; + } + ${methodOverrideAnnotationString} + <#if chainModel> + public ${entity} set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + <#else> + public void set${field.capitalName}(${field.propertyType} ${field.propertyName}) { + + this.${field.propertyName} = ${field.propertyName}; + <#if chainModel> + return this; + + } + + + +<#if entityColumnConstant> + <#list table.fields as field> + public static final String ${field.name?upper_case} = "${field.name}"; + + + +<#if activeRecord> + @Override + public Serializable pkVal() { + <#if keyPropertyName??> + return this.${keyPropertyName}; + <#else> + return null; + + } + +} diff --git a/ab-code-generator/src/main/resources/templates/manager.java.ftl b/ab-code-generator/src/main/resources/templates/manager.java.ftl new file mode 100644 index 00000000..411c33d7 --- /dev/null +++ b/ab-code-generator/src/main/resources/templates/manager.java.ftl @@ -0,0 +1,20 @@ +package ${package.Service}; + +import ${package.Entity}.${entity}; +import ${superServiceClassPackage}; + +/** + *

+ * ${table.comment!} 通用业务类 + *

+ * + * @author ${author} + * @since ${date} + */ +<#if kotlin> +interface ${table.serviceName} : ${superServiceClass}<${entity}> +<#else> +public interface ${table.serviceName} extends ${superServiceClass}<${entity}> { + +} + diff --git a/ab-code-generator/src/main/resources/templates/managerImpl.java.ftl b/ab-code-generator/src/main/resources/templates/managerImpl.java.ftl new file mode 100644 index 00000000..9c293e37 --- /dev/null +++ b/ab-code-generator/src/main/resources/templates/managerImpl.java.ftl @@ -0,0 +1,24 @@ +package ${package.ServiceImpl}; + +import ${package.Entity}.${entity}; +import ${package.Mapper}.${table.mapperName}; +import ${package.Service}.${table.serviceName}; +import ${superServiceImplClassPackage}; +import org.springframework.stereotype.Service; + +/** + * ${table.comment!} 通用服务实现类 + * + * @author ${author} + * @since ${date} + */ +@Service("${table.serviceImplName?replace("Impl", "")?uncap_first}") +<#if kotlin> +open class ${table.serviceImplName} : ${superServiceImplClass}<${table.mapperName}, ${entity}>(), ${table.serviceName} { + +} +<#else> +public class ${table.serviceImplName} extends ${superServiceImplClass}<${entity}> implements ${table.serviceName} { + +} + diff --git a/ab-code-generator/src/main/resources/templates/mapper.xml.ftl b/ab-code-generator/src/main/resources/templates/mapper.xml.ftl new file mode 100644 index 00000000..cabe7a96 --- /dev/null +++ b/ab-code-generator/src/main/resources/templates/mapper.xml.ftl @@ -0,0 +1,39 @@ + + + + +<#if enableCache> + + + + +<#if baseResultMap> + + +<#list table.fields as field> +<#if field.keyFlag><#--生成主键排在第一位--> + + + +<#list table.commonFields as field><#--生成公共字段 --> + + +<#list table.fields as field> +<#if !field.keyFlag><#--生成普通字段 --> + + + + + + +<#if baseColumnList> + + +<#list table.commonFields as field> + ${field.columnName}, + + ${table.fieldNames} + + + + diff --git a/ab-component/ab-component-j2cache/pom.xml b/ab-component/ab-component-j2cache/pom.xml new file mode 100644 index 00000000..79a71969 --- /dev/null +++ b/ab-component/ab-component-j2cache/pom.xml @@ -0,0 +1,52 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-j2cache + + + 2.8.4-release + + + + + com.dstz + ab-base-common + + + net.oschina.j2cache + j2cache-core + ${j2cache.version} + + + fastjson + com.alibaba + + + + + org.springframework.boot + spring-boot-starter + + + com.fasterxml.jackson.core + jackson-databind + + + cn.hutool + hutool-extra + + + org.springframework.boot + spring-boot-starter-data-redis + provided + + + diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/AbJ2Cache.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/AbJ2Cache.java new file mode 100644 index 00000000..ce5abd9b --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/AbJ2Cache.java @@ -0,0 +1,63 @@ +package com.dstz.component.j2cache; + +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.utils.CastUtils; +import net.oschina.j2cache.CacheChannel; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ReflectionUtils; + +import java.util.concurrent.Callable; + +/** + * j2cache 缓存实现 + * + * @author wacxhs + */ +public class AbJ2Cache implements ICache { + + @Autowired + private CacheChannel cacheChannel; + + @Override + public V getIfPresent(String region, String key) { + return CastUtils.cast(cacheChannel.get(region, key).getValue()); + } + + @Override + public V get(String region, String key, Callable loader) { + Object value = cacheChannel.get(region, key, k -> { + try { + return loader.call(); + } catch (Exception e) { + ReflectionUtils.rethrowRuntimeException(e); + return null; + } + }).getValue(); + return CastUtils.cast(value); + } + + @Override + public void put(String region, String key, Object value) { + cacheChannel.set(region, key, value); + } + + @Override + public void invalidate(String region, String... key) { + cacheChannel.evict(region, key); + } + + @Override + public void invalidateRegion(String region) { + cacheChannel.clear(region); + } + + @Override + public void invalidateAll() { + cacheChannel.regions().stream().map(CacheChannel.Region::getName).forEach(this::invalidateRegion); + } + + @Override + public boolean exists(String region, String key) { + return cacheChannel.exists(region, key); + } +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/J2CacheChannelFactoryBean.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/J2CacheChannelFactoryBean.java new file mode 100644 index 00000000..44fd459d --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/J2CacheChannelFactoryBean.java @@ -0,0 +1,145 @@ +package com.dstz.component.j2cache; + +import net.oschina.j2cache.CacheChannel; +import net.oschina.j2cache.J2CacheBuilder; +import net.oschina.j2cache.J2CacheConfig; +import org.springframework.beans.factory.FactoryBean; + +import java.util.Properties; + +/** + * cacheChannel bean工厂 + * + * @author wacxhs + */ +public class J2CacheChannelFactoryBean implements FactoryBean { + + /** + * 序列化 + */ + private String serialization; + + /** + * 广播类 + */ + private String broadcastProviderClass; + + /** + * 一级缓存实现类 + */ + private String cacheL1ProviderClass; + + /** + * 二级缓存实现类 + */ + private String cacheL2ProviderClass; + + /** + * 是否缓存空对象 + */ + private Boolean defaultCacheNullObject; + + /** + * 一级缓存配置属性 + */ + private Properties cacheL1Properties; + + /** + * 二级缓存配置 + */ + private Properties cacheL2Properties; + + /** + * 广播配置 + */ + private Properties broadcastProperties; + + @Override + public CacheChannel getObject() { + J2CacheConfig config = new J2CacheConfig(); + config.setSerialization(serialization); + config.setBroadcast(broadcastProviderClass); + config.setL1CacheName(cacheL1ProviderClass); + config.setL2CacheName(cacheL2ProviderClass); + config.setSyncTtlToRedis(Boolean.FALSE); + config.setDefaultCacheNullObject(Boolean.TRUE.equals(defaultCacheNullObject)); + config.setL1CacheProperties(cacheL1Properties); + config.setL2CacheProperties(cacheL2Properties); + config.setBroadcastProperties(broadcastProperties); + return J2CacheBuilder.init(config).getChannel(); + } + + @Override + public Class getObjectType() { + return CacheChannel.class; + } + + @Override + public boolean isSingleton() { + return true; + } + + public String getSerialization() { + return serialization; + } + + public void setSerialization(String serialization) { + this.serialization = serialization; + } + + public String getBroadcastProviderClass() { + return broadcastProviderClass; + } + + public void setBroadcastProviderClass(String broadcastProviderClass) { + this.broadcastProviderClass = broadcastProviderClass; + } + + public String getCacheL1ProviderClass() { + return cacheL1ProviderClass; + } + + public void setCacheL1ProviderClass(String cacheL1ProviderClass) { + this.cacheL1ProviderClass = cacheL1ProviderClass; + } + + public String getCacheL2ProviderClass() { + return cacheL2ProviderClass; + } + + public void setCacheL2ProviderClass(String cacheL2ProviderClass) { + this.cacheL2ProviderClass = cacheL2ProviderClass; + } + + public Boolean getDefaultCacheNullObject() { + return defaultCacheNullObject; + } + + public void setDefaultCacheNullObject(Boolean defaultCacheNullObject) { + this.defaultCacheNullObject = defaultCacheNullObject; + } + + public Properties getCacheL1Properties() { + return cacheL1Properties; + } + + public void setCacheL1Properties(Properties cacheL1Properties) { + this.cacheL1Properties = cacheL1Properties; + } + + public Properties getCacheL2Properties() { + return cacheL2Properties; + } + + public void setCacheL2Properties(Properties cacheL2Properties) { + this.cacheL2Properties = cacheL2Properties; + } + + public Properties getBroadcastProperties() { + return broadcastProperties; + } + + public void setBroadcastProperties(Properties broadcastProperties) { + this.broadcastProperties = broadcastProperties; + } +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheBroadcastPropertyKeyConstant.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheBroadcastPropertyKeyConstant.java new file mode 100644 index 00000000..1d64f230 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheBroadcastPropertyKeyConstant.java @@ -0,0 +1,24 @@ +package com.dstz.component.j2cache.constant; + +/** + * j2cache 二级缓存属性键常量 + * + * @author wacxhs + */ +public class J2CacheBroadcastPropertyKeyConstant { + + /** + * 广播是否开启 + */ + public static final String OPEN = "open"; + + /** + * 通道名称 + */ + public static final String CHANNEL = "channel"; + + /** + * redis listener container bean id + */ + public static final String REDIS_MESSAGE_LISTENER_CONTAINER_BEAN_ID = "redisMessageListenerContainerBeanId"; +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheL2PropertyKeyConstant.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheL2PropertyKeyConstant.java new file mode 100644 index 00000000..58d4030b --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/constant/J2CacheL2PropertyKeyConstant.java @@ -0,0 +1,19 @@ +package com.dstz.component.j2cache.constant; + +/** + * j2cache 二级缓存属性键常量 + * + * @author wacxhs + */ +public class J2CacheL2PropertyKeyConstant { + + /** + * 是否开启 + */ + public static final String CACHE_OPEN = "cacheOpen"; + + /** + * 命名空间 + */ + public static final String NAMESPACE = "namespace"; +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisPubSubClusterPolicy.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisPubSubClusterPolicy.java new file mode 100644 index 00000000..5741e301 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisPubSubClusterPolicy.java @@ -0,0 +1,102 @@ +package com.dstz.component.j2cache.redis; + +import cn.hutool.core.util.BooleanUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.j2cache.constant.J2CacheBroadcastPropertyKeyConstant; +import net.oschina.j2cache.CacheProviderHolder; +import net.oschina.j2cache.Command; +import net.oschina.j2cache.cluster.ClusterPolicy; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.data.redis.connection.Message; +import org.springframework.data.redis.connection.MessageListener; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.StringRedisSerializer; + +import java.util.Properties; + +/** + * spring redis key失效通知策略 + * + * @author wacxhs + */ +public class RedisPubSubClusterPolicy implements ClusterPolicy, MessageListener { + + private static final Logger logger = LoggerFactory.getLogger(RedisPubSubClusterPolicy.class); + + private final int LOCAL_COMMAND_ID = Command.genRandomSrc(); + + private RedisTemplate redisTemplate; + + private CacheProviderHolder holder; + + /** + * 消息与订阅通道名称 + */ + private String channel; + + /** + * 广播开启 + */ + private boolean broadcastOpen; + + @SuppressWarnings("unchecked") + @Override + public void connect(Properties props, CacheProviderHolder holder) { + this.holder = holder; + this.broadcastOpen = BooleanUtil.toBoolean(props.getProperty(J2CacheBroadcastPropertyKeyConstant.OPEN)); + // 获取消息通道名称 + this.channel = props.getProperty(J2CacheBroadcastPropertyKeyConstant.CHANNEL); + if (this.broadcastOpen) { + redisTemplate = RedisTemplateUtils.createRedisTemplate(); + // 注册监听器 + final String redisMessageListenerContainer = props.getProperty(J2CacheBroadcastPropertyKeyConstant.REDIS_MESSAGE_LISTENER_CONTAINER_BEAN_ID); + (StrUtil.isEmpty(redisMessageListenerContainer) ? + SpringUtil.getBean(RedisMessageListenerContainer.class) : + SpringUtil.getBean(redisMessageListenerContainer, RedisMessageListenerContainer.class)).addMessageListener(this, ChannelTopic.of(this.channel)); + } + } + + @Override + public void publish(Command cmd) { + cmd.setSrc(LOCAL_COMMAND_ID); + if (logger.isDebugEnabled()) { + logger.debug("broadcastOpen: {}, publish cmd:{}", broadcastOpen, JsonUtils.toJSONString(cmd)); + } + if (broadcastOpen) { + redisTemplate.convertAndSend(channel, JsonUtils.toJSONBytes(cmd)); + } + } + + @Override + public void onMessage(Message message, byte[] pattern) { + if (logger.isDebugEnabled()) { + logger.debug("receive command {}", StringRedisSerializer.UTF_8.deserialize(message.getBody())); + } + handleCommand(JsonUtils.parseObject(message.getBody(), Command.class)); + } + + @Override + public void disconnect() { + + } + + @Override + public void evict(String region, String... keys) { + holder.getLevel1Cache(region).evict(keys); + } + + @Override + public void clear(String region) { + holder.getLevel1Cache(region).clear(); + } + + @Override + public boolean isLocalCommand(Command cmd) { + return cmd.getSrc() == LOCAL_COMMAND_ID; + } +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisTemplateUtils.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisTemplateUtils.java new file mode 100644 index 00000000..cea04257 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/RedisTemplateUtils.java @@ -0,0 +1,20 @@ +package com.dstz.component.j2cache.redis; + +import cn.hutool.extra.spring.SpringUtil; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.RedisSerializer; + +class RedisTemplateUtils { + + public static RedisTemplate createRedisTemplate() { + RedisTemplate redisTemplate = new RedisTemplate<>(); + redisTemplate.setEnableDefaultSerializer(false); + redisTemplate.setConnectionFactory(SpringUtil.getBean(RedisConnectionFactory.class)); + redisTemplate.setDefaultSerializer(RedisSerializer.string()); + redisTemplate.afterPropertiesSet(); + redisTemplate.setKeySerializer(RedisSerializer.string()); + return redisTemplate; + } + +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCache.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCache.java new file mode 100644 index 00000000..bca4ddf8 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCache.java @@ -0,0 +1,115 @@ +package com.dstz.component.j2cache.redis; + +import cn.hutool.core.util.StrUtil; +import net.oschina.j2cache.Level2Cache; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; + +import java.nio.charset.StandardCharsets; +import java.time.Duration; +import java.util.*; +import java.util.stream.Collectors; + +/** + * spring redis cache + * + * @author wacxhs + */ +class SpringRedisCache implements Level2Cache { + + private final String namespace; + + private final String region; + + private final Duration timeout; + + private final RedisTemplate redisTemplate; + + public SpringRedisCache(String namespace, String region, Duration timeout, RedisTemplate redisTemplate) { + this.namespace = namespace; + this.region = region; + this.timeout = timeout; + this.redisTemplate = redisTemplate; + } + + @Override + public boolean supportTTL() { + return true; + } + + private String getCacheKey(String key) { + if (StrUtil.isEmpty(namespace)) { + return StrUtil.concat(true, region, StrUtil.COLON, key); + } else { + return StrUtil.concat(true, namespace, StrUtil.COLON, region, StrUtil.COLON, key); + } + } + + @Override + public byte[] getBytes(String key) { + return (byte[]) redisTemplate.boundValueOps(getCacheKey(key)).get(); + } + + @Override + public List getBytes(Collection keys) { + return keys.stream().map(this::getBytes).collect(Collectors.toList()); + } + + @Override + public void setBytes(String key, byte[] bytes) { + redisTemplate.boundValueOps(getCacheKey(key)).set(bytes, timeout); + } + + @Override + public void setBytes(Map cacheMap) { + cacheMap.forEach((key, bytes) -> setBytes(getCacheKey(key), bytes)); + } + + @Override + public Collection keys() { + List keys; + try (RedisConnection redisConnection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) { + ScanOptions scanOptions = ScanOptions.scanOptions().match(getCacheKey(StrUtil.EMPTY)).build(); + keys = new LinkedList<>(); + for (Cursor cursor = redisConnection.scan(scanOptions); cursor.hasNext(); ) { + keys.add(new String(cursor.next(), StandardCharsets.UTF_8)); + } + } + return keys; + } + + @Override + public void evict(String... keys) { + List keyList = Arrays.stream(keys).map(this::getCacheKey).collect(Collectors.toList()); + redisTemplate.delete(keyList); + } + + @Override + public void clear() { + try (RedisConnection redisConnection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) { + ScanOptions scanOptions = ScanOptions.scanOptions().match(getCacheKey(StrUtil.EMPTY)).build(); + int deleteKeyCount = 0; + LinkedList keys = new LinkedList<>(); + for (Cursor cursor = redisConnection.scan(scanOptions); cursor.hasNext(); deleteKeyCount++) { + keys.add((String) redisTemplate.getKeySerializer().deserialize(cursor.next())); + if (deleteKeyCount % 1000 == 0) { + redisTemplate.delete(keys); + keys.clear(); + } + } + if (!keys.isEmpty()) { + redisTemplate.delete(keys); + } + } + } + + public String getRegion() { + return region; + } + + public Duration getTimeout() { + return timeout; + } +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCacheProvider.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCacheProvider.java new file mode 100644 index 00000000..5ec07838 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/redis/SpringRedisCacheProvider.java @@ -0,0 +1,79 @@ +package com.dstz.component.j2cache.redis; + +import cn.hutool.core.util.BooleanUtil; +import com.dstz.base.common.cache.CacheRegion; +import com.dstz.component.j2cache.constant.J2CacheL2PropertyKeyConstant; +import net.oschina.j2cache.*; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.Collection; +import java.util.List; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * ab redis cache 缓存提供者,与spring整合 + * + * @author wacxhs + */ +public class SpringRedisCacheProvider implements CacheProvider { + + private final ConcurrentHashMap regionCacheMap = new ConcurrentHashMap<>(); + + private boolean cacheOpen; + + private Level2Cache nullCache; + + @Override + public String name() { + return "redis"; + } + + + @Override + public int level() { + return CacheObject.LEVEL_2; + } + + @Override + public Cache buildCache(String regionName, CacheExpiredListener listener) { + return buildCache(regionName, Long.MAX_VALUE, listener); + } + + @Override + public Cache buildCache(String regionName, long timeToLiveInSeconds, CacheExpiredListener listener) { + if (cacheOpen) { + return regionCacheMap.get(regionName); + } else { + return nullCache; + } + } + + @Override + public Collection regions() { + return regionCacheMap.values().stream().map(item -> new CacheChannel.Region(item.getRegion(), Long.MAX_VALUE, item.getTimeout().getSeconds())).collect(Collectors.toList()); + } + + @SuppressWarnings("unchecked") + @Override + public void start(Properties props) { + this.cacheOpen = BooleanUtil.toBoolean(props.getProperty(J2CacheL2PropertyKeyConstant.CACHE_OPEN)); + if (this.cacheOpen) { + final RedisTemplate redisTemplate = RedisTemplateUtils.createRedisTemplate(); + final String namespace = props.getProperty(J2CacheL2PropertyKeyConstant.NAMESPACE); + List cacheRegionList = (List) props.get(CacheRegion.class); + this.regionCacheMap.clear(); + for (CacheRegion cacheRegion : cacheRegionList) { + this.regionCacheMap.put(cacheRegion.getRegion(), new SpringRedisCache(namespace, cacheRegion.getRegion(), cacheRegion.getExpiration(), redisTemplate)); + } + } else { + this.nullCache = new NullCache(); + } + } + + @Override + public void stop() { + this.regionCacheMap.clear(); + } +} diff --git a/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/serialization/JacksonJsonSerializer.java b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/serialization/JacksonJsonSerializer.java new file mode 100644 index 00000000..080aa397 --- /dev/null +++ b/ab-component/ab-component-j2cache/src/main/java/com/dstz/component/j2cache/serialization/JacksonJsonSerializer.java @@ -0,0 +1,31 @@ +package com.dstz.component.j2cache.serialization; + +import com.dstz.base.common.serializer.AbGenericJackson2JsonRedisSerializer; +import net.oschina.j2cache.util.Serializer; + +import java.io.IOException; + +/** + * jackson json 序列化器 + * + * @author wacxhs + */ +public class JacksonJsonSerializer implements Serializer { + + private final AbGenericJackson2JsonRedisSerializer serializer = new AbGenericJackson2JsonRedisSerializer(); + + @Override + public String name() { + return "json"; + } + + @Override + public byte[] serialize(Object obj) throws IOException { + return serializer.serialize(obj); + } + + @Override + public Object deserialize(byte[] dataBytes) throws IOException { + return serializer.deserialize(dataBytes); + } +} diff --git a/ab-component/ab-component-mq-api/pom.xml b/ab-component/ab-component-mq-api/pom.xml new file mode 100644 index 00000000..1f794ea4 --- /dev/null +++ b/ab-component/ab-component-mq-api/pom.xml @@ -0,0 +1,19 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + ab-component-mq-api + + + + com.dstz + ab-base-common + + + \ No newline at end of file diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/JmsHandler.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/JmsHandler.java new file mode 100644 index 00000000..2c21a7e2 --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/JmsHandler.java @@ -0,0 +1,31 @@ +package com.dstz.component.mq.api; + +import com.dstz.component.mq.api.model.JmsDTO; + +import java.io.Serializable; + +/** + *
+ * 所有消费者 均需要实现该接口
+ * 通过type 获取具体的处理者 + *
+ * + * @author lightning + */ +public interface JmsHandler { + + /** + * 得到消息类型 + * + * @return 消息类型 + */ + String getType(); + + /** + * 处理消息 + * + * @param message 消息传输类型 + * @return 是否成功处理 + */ + boolean handlerMessage(JmsDTO message); +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsDestinationConstant.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsDestinationConstant.java new file mode 100644 index 00000000..aee64cd9 --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsDestinationConstant.java @@ -0,0 +1,15 @@ +package com.dstz.component.mq.api.constants; + +/** + * 消息队列名称 + * + * @author lightning + */ +public interface JmsDestinationConstant { + + /** + * 默认名称 + */ + String DEFAULT_NAME = "message"; + +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsTypeEnum.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsTypeEnum.java new file mode 100644 index 00000000..32c0c2aa --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/constants/JmsTypeEnum.java @@ -0,0 +1,25 @@ +package com.dstz.component.mq.api.constants; + +/** + * 发送消息类型枚举 + */ +public enum JmsTypeEnum { + INNER("inner"), + EMAIL("email"), + DING_DING("dingding"), + SMS("sms"), + WEI_XIN("weixin"), + WEI_XIN_QY("weixinQy") + ; + + private final String type; + + JmsTypeEnum(String type) { + this.type = type; + } + + public String getType() { + return type; + } + +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/DefaultJmsDTO.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/DefaultJmsDTO.java new file mode 100644 index 00000000..248f5665 --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/DefaultJmsDTO.java @@ -0,0 +1,50 @@ +package com.dstz.component.mq.api.model; + +import com.dstz.component.mq.api.constants.JmsTypeEnum; + +import java.io.Serializable; + +/** + * 默认的消息 + * + * @param + * @author lightning + */ +public class DefaultJmsDTO implements JmsDTO { + + + private String type; + + private T data; + + public DefaultJmsDTO() { + } + + public DefaultJmsDTO(String type, T data) { + this.type = type; + this.data = data; + } + + public DefaultJmsDTO(JmsTypeEnum jmsTypeEnum, T data) { + this.type = jmsTypeEnum.getType(); + this.data = data; + } + + @Override + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public String getType() { + return type; + } +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/JmsDTO.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/JmsDTO.java new file mode 100644 index 00000000..f2df3e4d --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/JmsDTO.java @@ -0,0 +1,26 @@ +package com.dstz.component.mq.api.model; + +import java.io.Serializable; + +/** + * 消息传输对象 + * + * @param + * @author lightning + */ +public interface JmsDTO extends Serializable { + + /** + * 具体消费者的标识 + * + * @return 消费者标识 + */ + String getType(); + + /** + * 消费者的数据对象 + * + * @return 消费数据 + */ + T getData(); +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/msg/NotifyMessage.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/msg/NotifyMessage.java new file mode 100644 index 00000000..6561d588 --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/model/msg/NotifyMessage.java @@ -0,0 +1,131 @@ +package com.dstz.component.mq.api.model.msg; + +import com.dstz.base.common.constats.InnerMsgEnum; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.org.api.model.IUser; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * 通知消息的DTO + * + * @author lightning + */ +public class NotifyMessage implements Serializable { + private String subject; + private String htmlContent; + private String textContent; + private IUser sender; + private List receivers; + private String businessId; + private InnerMsgEnum innerMsgEnum; + private Map extendVars = new HashMap(); + + + public NotifyMessage() { + } + + public NotifyMessage(String subject, String htmlContent, IUser sender, List receivers) { + this.subject = subject; + this.sender = sender; + this.receivers = receivers; + this.htmlContent = htmlContent; + } + + public NotifyMessage(String subject, String htmlContent, IUser sender, List receivers, String businessId) { + this.subject = subject; + this.sender = sender; + this.receivers = receivers; + this.htmlContent = htmlContent; + this.businessId = businessId; + } + + + public NotifyMessage(String subject, String htmlContent, IUser sender, List receivers, String businessId,InnerMsgEnum innerMsgEnum) { + this.subject = subject; + this.sender = sender; + this.receivers = receivers; + this.htmlContent = htmlContent; + this.businessId = businessId; + this.innerMsgEnum = innerMsgEnum; + } + + public NotifyMessage(String subject, String htmlContent, String textContent, IUser sender, List receivers, String businessId,InnerMsgEnum innerMsgEnum) { + this.subject = subject; + this.sender = sender; + this.receivers = receivers; + this.htmlContent = htmlContent; + this.businessId = businessId; + this.textContent = textContent; + this.innerMsgEnum = innerMsgEnum; + } + + + + public InnerMsgEnum getInnerMsgEnum() { + return innerMsgEnum; + } + + public void setInnerMsgEnum(InnerMsgEnum innerMsgEnum) { + this.innerMsgEnum = innerMsgEnum; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public IUser getSender() { + return sender; + } + + public void setSender(IUser sender) { + this.sender = sender; + } + + public List getReceivers() { + return receivers; + } + + public void setReceivers(List receivers) { + this.receivers = receivers; + } + + public Map getExtendVars() { + return extendVars; + } + + public void setExtendVars(Map extendVars) { + this.extendVars = extendVars; + } + + public String getHtmlContent() { + return htmlContent; + } + + public void setHtmlContent(String htmlContent) { + this.htmlContent = htmlContent; + } + + public String getTextContent() { + return textContent; + } + + public void setTextContent(String textContent) { + this.textContent = textContent; + } + + public String getBusinessId() { + return businessId; + } + + public void setBusinessId(String businessId) { + this.businessId = businessId; + } +} diff --git a/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/producer/JmsProducer.java b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/producer/JmsProducer.java new file mode 100644 index 00000000..05fa772d --- /dev/null +++ b/ab-component/ab-component-mq-api/src/main/java/com/dstz/component/mq/api/producer/JmsProducer.java @@ -0,0 +1,28 @@ +package com.dstz.component.mq.api.producer; + +import com.dstz.component.mq.api.model.JmsDTO; + +import java.util.List; + +/** + * 消息发送提供者 + * + * @author lightning + */ +public interface JmsProducer { + + /** + * 发送到队列中 + * + * @param message 发送消息 + */ + void sendToQueue(JmsDTO message); + + /** + * 发送列表到队列中 + * + * @param messages 发送消息集 + */ + void sendToQueue(List messages); + +} \ No newline at end of file diff --git a/ab-component/ab-component-mq-engine/pom.xml b/ab-component/ab-component-mq-engine/pom.xml new file mode 100644 index 00000000..4ebf63ff --- /dev/null +++ b/ab-component/ab-component-mq-engine/pom.xml @@ -0,0 +1,46 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-mq-engine + + + + com.dstz + ab-base-web + + + org.springframework + spring-jms + + + com.dstz + ab-component-mq-api + ${project.version} + + + org.springframework.data + spring-data-redis + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-starter-activemq + provided + + + + + + + diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueAutoConfiguration.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueAutoConfiguration.java new file mode 100644 index 00000000..e86334a9 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueAutoConfiguration.java @@ -0,0 +1,32 @@ +package com.dstz.component.mq.engine.config; + +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; + +/** + * 缓存相关配置 + * + * @author lightning + */ +@Configuration +@Import(AbMessageQueueAutoConfiguration.AbCacheConfigurationSelector.class) +@EnableConfigurationProperties({ AbSimpleMessageQueueProperties.class}) +public class AbMessageQueueAutoConfiguration { + + + public static class AbCacheConfigurationSelector implements ImportSelector { + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + AbMessageQueueType[] types = AbMessageQueueType.values(); + String[] imports = new String[types.length]; + for (int i = 0; i < types.length; i++) { + imports[i] = types[i].getConfigurationClass().getName(); + } + return imports; + } + } + +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConditional.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConditional.java new file mode 100644 index 00000000..f08cce70 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConditional.java @@ -0,0 +1,31 @@ +package com.dstz.component.mq.engine.config; + +import com.dstz.base.common.constats.StrPool; +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.ClassMetadata; +/** + * 根据配置加载消息类型 + * + * @author lightning + */ +class AbMessageQueueConditional extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String sourceClass = ""; + if (metadata instanceof ClassMetadata) { + sourceClass = ((ClassMetadata) metadata).getClassName(); + } + String value = context.getEnvironment().getProperty("ab.simple-mq.message-queue-type"); + if (StringUtils.isEmpty(value)) { + return sourceClass.equals(AbMessageQueueType.SYNCHRONOUS.getConfigurationClass().getName()) ? ConditionOutcome.match() : ConditionOutcome.noMatch(value + " cache type"); + } + value = value.replace(StrPool.DASHED, StrPool.UNDERLINE); + AbMessageQueueType abMessageQueueType = AbMessageQueueType.valueOf(value.toUpperCase()); + return sourceClass.equals(abMessageQueueType.getConfigurationClass().getName()) ? ConditionOutcome.match() : ConditionOutcome.noMatch(value + " cache type"); + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConfiguration.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConfiguration.java new file mode 100644 index 00000000..024f891a --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueConfiguration.java @@ -0,0 +1,55 @@ +package com.dstz.component.mq.engine.config; + +import javax.jms.ConnectionFactory; + +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.jms.annotation.EnableJms; +import org.springframework.jms.listener.DefaultMessageListenerContainer; +import org.springframework.jms.listener.adapter.MessageListenerAdapter; + +import com.dstz.component.mq.api.constants.JmsDestinationConstant; +import com.dstz.component.mq.api.producer.JmsProducer; +import com.dstz.component.mq.engine.consumer.CommonMessageQueueConsumer; +import com.dstz.component.mq.engine.producer.CommonMessageQueueProducer; + +/** + * 通用消息队列自动装配 + * + * @author lightning + */ +@Conditional(AbMessageQueueConditional.class) +@EnableJms +public class AbMessageQueueConfiguration { + + @Bean + public JmsProducer jmsProducer() { + return new CommonMessageQueueProducer(); + } + + @Bean + public CommonMessageQueueConsumer messageQueueConsumer() { + + return new CommonMessageQueueConsumer(); + } + + @Bean + public MessageListenerAdapter commonMessageQueueConsumerListenerAdapter(CommonMessageQueueConsumer messageQueueConsumer) { + MessageListenerAdapter messageListenerAdapter = new MessageListenerAdapter(); + messageListenerAdapter.setDelegate(messageQueueConsumer); + return messageListenerAdapter; + } + + @Bean + public DefaultMessageListenerContainer messageListenerContainer(ConnectionFactory connectionFactory, + @Qualifier("commonMessageQueueConsumerListenerAdapter") MessageListenerAdapter messageListenerAdapter) { + DefaultMessageListenerContainer messageListenerContainer = new DefaultMessageListenerContainer(); + messageListenerContainer.setDestinationName(JmsDestinationConstant.DEFAULT_NAME); + messageListenerContainer.setMessageListener(messageListenerAdapter); + messageListenerContainer.setConnectionFactory(connectionFactory); + return messageListenerContainer; + } + + +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueType.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueType.java new file mode 100644 index 00000000..7b19bbb6 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbMessageQueueType.java @@ -0,0 +1,47 @@ +package com.dstz.component.mq.engine.config; + +/** + * 消息队列类型 + * + * @author lightning + */ +public enum AbMessageQueueType { + + /** + * redis + */ + REDIS(AbRedisMessageQueueConfiguration.class), + + /** + * java消息队列 + */ + JMS(AbMessageQueueConfiguration.class), + + /** + * 同步方式 + */ + SYNCHRONOUS(AbSynchronousMessageQueueConfiguration.class), + + /** + * 事务消息 + */ + // TRANSACTION_MESSAGE(TxmMessageQueueConfiguration.class), + + /** + * 丢弃消息 + */ + DISCARD(DiscardMessageQueueConfiguration.class); + + /** + * 配置类 + */ + private Class configurationClass; + + AbMessageQueueType(Class configurationClass) { + this.configurationClass = configurationClass; + } + + public Class getConfigurationClass() { + return configurationClass; + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConfiguration.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConfiguration.java new file mode 100644 index 00000000..c3dab304 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConfiguration.java @@ -0,0 +1,34 @@ +package com.dstz.component.mq.engine.config; + +import com.dstz.component.mq.engine.consumer.RedisMessageQueueConsumer; +import com.dstz.component.mq.api.producer.JmsProducer; +import com.dstz.component.mq.engine.producer.RedisMessageQueueProducer; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.data.redis.core.RedisTemplate; + +/** + * redis 消息队列自动装配 + * + * @author lightning + */ +@Conditional(AbMessageQueueConditional.class) +@ConditionalOnClass(RedisTemplate.class) +@EnableConfigurationProperties(AbRedisMessageQueueConsumerProperties.class) +public class AbRedisMessageQueueConfiguration { + + @Bean + public JmsProducer jmsProducer() { + return new RedisMessageQueueProducer(); + } + + @Bean + public RedisMessageQueueConsumer redisMessageQueueConsumer(AbRedisMessageQueueConsumerProperties properties) { + RedisMessageQueueConsumer redisMessageQueueConsumer = new RedisMessageQueueConsumer(); + redisMessageQueueConsumer.setRedisTemplateBeanName(properties.getRedisTemplateBeanName()); + return redisMessageQueueConsumer; + } + +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConsumerProperties.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConsumerProperties.java new file mode 100644 index 00000000..2eb42363 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbRedisMessageQueueConsumerProperties.java @@ -0,0 +1,25 @@ +package com.dstz.component.mq.engine.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * Redis消息队列消费属性配置 + * + * @author lightning + */ +@ConfigurationProperties(prefix = "ab.simple-mq.redis-consumer") +public class AbRedisMessageQueueConsumerProperties { + + /** + * redisTemplate 容器中名称 + */ + private String redisTemplateBeanName = "redisTemplate"; + + public String getRedisTemplateBeanName() { + return redisTemplateBeanName; + } + + public void setRedisTemplateBeanName(String redisTemplateBeanName) { + this.redisTemplateBeanName = redisTemplateBeanName; + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSimpleMessageQueueProperties.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSimpleMessageQueueProperties.java new file mode 100644 index 00000000..278ec708 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSimpleMessageQueueProperties.java @@ -0,0 +1,25 @@ +package com.dstz.component.mq.engine.config; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 消息队列配置 + * + * @author lightning + */ +@ConfigurationProperties(prefix = "ab.simple-mq") +public class AbSimpleMessageQueueProperties { + + /** + * 消息队列方式 + */ + private AbMessageQueueType messageQueueType = AbMessageQueueType.SYNCHRONOUS; + + public AbMessageQueueType getMessageQueueType() { + return messageQueueType; + } + + public void setMessageQueueType(AbMessageQueueType messageQueueType) { + this.messageQueueType = messageQueueType; + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSynchronousMessageQueueConfiguration.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSynchronousMessageQueueConfiguration.java new file mode 100644 index 00000000..45180aa9 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/AbSynchronousMessageQueueConfiguration.java @@ -0,0 +1,27 @@ +package com.dstz.component.mq.engine.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; + +import com.dstz.component.mq.api.producer.JmsProducer; +import com.dstz.component.mq.engine.producer.SynchronousQueueProducer; + +/** + * 同步消息队列配置 + * + * @author lightning + */ +@Conditional(AbMessageQueueConditional.class) +public class AbSynchronousMessageQueueConfiguration { + + /** + * 默认消息发送提供者 + * + * @return 消息发送提供端 + */ + @Bean + public JmsProducer jmsProducer() { + return new SynchronousQueueProducer(); + } + +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/DiscardMessageQueueConfiguration.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/DiscardMessageQueueConfiguration.java new file mode 100644 index 00000000..f1670cbc --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/config/DiscardMessageQueueConfiguration.java @@ -0,0 +1,20 @@ +package com.dstz.component.mq.engine.config; + +import com.dstz.component.mq.api.producer.JmsProducer; +import com.dstz.component.mq.engine.producer.DiscardQueueProducer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; + +/** + * discard message queue configuration + * + * @author lightning + */ +@Conditional(AbMessageQueueConditional.class) +public class DiscardMessageQueueConfiguration { + + @Bean + public JmsProducer jmsProducer() { + return new DiscardQueueProducer(); + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqEngineConstant.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqEngineConstant.java new file mode 100644 index 00000000..4690d005 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqEngineConstant.java @@ -0,0 +1,13 @@ +package com.dstz.component.mq.engine.constants; + +public class MqEngineConstant { + + private MqEngineConstant() throws IllegalAccessException { + throw new IllegalAccessException(); + } + + /** + * ApplicationContextEventId + */ + public static final String APPLICATION_CONTEXT_EVENT_ID = "bootstrap"; +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqExceptionCodeConstant.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqExceptionCodeConstant.java new file mode 100644 index 00000000..58bca9cb --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/constants/MqExceptionCodeConstant.java @@ -0,0 +1,33 @@ +package com.dstz.component.mq.engine.constants; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * 消息队列名称 + * + * @author lightning + */ +public enum MqExceptionCodeConstant implements IBaseCode { + + SEND_ERROR("send_error", "消息发送失败,{}"), + ; + + private final String code; + + private final String message; + + MqExceptionCodeConstant(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getMessage() { + return message; + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/AbstractMessageQueue.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/AbstractMessageQueue.java new file mode 100644 index 00000000..bf9ae486 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/AbstractMessageQueue.java @@ -0,0 +1,56 @@ +package com.dstz.component.mq.engine.consumer; + +import com.dstz.component.mq.api.JmsHandler; +import com.dstz.component.mq.engine.constants.MqEngineConstant; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; + +import java.io.Serializable; +import java.util.Collections; +import java.util.Map; +import java.util.stream.Collectors; + +/** + * 抽象公共消息队列 + * + * @author lightning + */ +public abstract class AbstractMessageQueue implements ApplicationListener { + + private ApplicationContext applicationContext; + + /** + * 注册消息处理器 + */ + private Map> registerJmsHandler = Collections.emptyMap(); + + @SuppressWarnings("unchecked") + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + if (event.getApplicationContext().getParent() == null || MqEngineConstant.APPLICATION_CONTEXT_EVENT_ID.equals(event.getApplicationContext().getParent().getId())) { + this.applicationContext = event.getApplicationContext(); + this.registerJmsHandler = applicationContext.getBeansOfType(JmsHandler.class).values().stream().collect(Collectors.toMap(JmsHandler::getType, o -> o)); + containerInitialCompleteAfter(); + } + } + + /** + * 容器初始化完成后 + */ + protected void containerInitialCompleteAfter() { + } + + protected ApplicationContext getApplicationContext() { + return applicationContext; + } + + /** + * 获取消息处理实现 + * + * @return 消息处理实现 + */ + protected JmsHandler getJmsHandler(String type) { + return this.registerJmsHandler.get(type); + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/CommonMessageQueueConsumer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/CommonMessageQueueConsumer.java new file mode 100644 index 00000000..05088078 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/CommonMessageQueueConsumer.java @@ -0,0 +1,60 @@ +package com.dstz.component.mq.engine.consumer; + +import com.dstz.component.mq.api.JmsHandler; +import com.dstz.component.mq.api.model.JmsDTO; +import org.springframework.util.ReflectionUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import java.io.Serializable; + +/** + * 通用消息队列消费 + * + * @author lightning + */ +public class CommonMessageQueueConsumer extends AbstractMessageQueue { + + + private static final Logger LOGGER = LoggerFactory.getLogger(CommonMessageQueueConsumer.class); + + /** + * 加载完成 + */ + private volatile boolean loadComplete; + + private final byte[] monitor = new byte[0]; + + @Override + protected void containerInitialCompleteAfter() { + this.loadComplete = true; + synchronized (monitor) { + monitor.notifyAll(); + } + } + + /** + * 处理消息 + * + * @param jmsDTO jmsDTO + */ + public void handleMessage(JmsDTO jmsDTO) { + //防止还未加载成功,消息已经调用 + while (!this.loadComplete) { + synchronized (monitor) { + try { + monitor.wait(500L); + } catch (InterruptedException e) { + LOGGER.error("Interrupted!", e); + Thread.currentThread().interrupt(); + ReflectionUtils.rethrowRuntimeException(e); + } + } + } + JmsHandler jmsHandler = getJmsHandler(jmsDTO.getType()); + if (jmsHandler == null) { + LOGGER.warn("{} no handler", jmsDTO.getType()); + return; + } + jmsHandler.handlerMessage(jmsDTO); + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/RedisMessageQueueConsumer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/RedisMessageQueueConsumer.java new file mode 100644 index 00000000..72b19527 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/consumer/RedisMessageQueueConsumer.java @@ -0,0 +1,132 @@ +package com.dstz.component.mq.engine.consumer; + +import cn.hutool.core.thread.ThreadFactoryBuilder; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.api.JmsHandler; +import com.dstz.component.mq.api.constants.JmsDestinationConstant; +import com.dstz.component.mq.api.model.JmsDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.DisposableBean; +import org.springframework.data.redis.core.BoundListOperations; +import org.springframework.data.redis.core.RedisTemplate; + +import java.io.Serializable; +import java.util.Objects; +import java.util.concurrent.RejectedExecutionHandler; +import java.util.concurrent.SynchronousQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +/** + * Redis消息队列消费 + * + * @author lightning + */ +@SuppressWarnings("ALL") +public class RedisMessageQueueConsumer extends AbstractMessageQueue implements DisposableBean { + + private static final Logger LOGGER = LoggerFactory.getLogger(RedisMessageQueueConsumer.class); + + /** + * reidsTemplate 容器中名称 + */ + private String redisTemplateBeanName; + + /** + * 消息队列 + */ + private BoundListOperations messageQueue; + + /** + * thread poll executor + */ + private ThreadPoolExecutor threadPoolExecutor; + + /** + * redis queue listener + */ + private RedisQueueListener redisQueueListener; + + + public void setRedisTemplateBeanName(String redisTemplateBeanName) { + this.redisTemplateBeanName = redisTemplateBeanName; + } + + @Override + public void destroy() throws Exception { + redisQueueListener.interrupt(); + threadPoolExecutor.shutdown(); + threadPoolExecutor.awaitTermination(Integer.MAX_VALUE, TimeUnit.MILLISECONDS); + } + + @SuppressWarnings("unchecked") + @Override + protected void containerInitialCompleteAfter() { + if (StrUtil.isEmpty(this.redisTemplateBeanName)) { + this.messageQueue = getApplicationContext().getBean(RedisTemplate.class).boundListOps(JmsDestinationConstant.DEFAULT_NAME); + } else { + this.messageQueue = getApplicationContext().getBean(redisTemplateBeanName, RedisTemplate.class).boundListOps(JmsDestinationConstant.DEFAULT_NAME); + } + RejectedExecutionHandler rejectedExecutionHandler = (r, executor) -> { + try { + if (!executor.isShutdown()) { + executor.getQueue().put(r); + } + } catch (InterruptedException e) { + LOGGER.error(e.getMessage(), e); + Thread.currentThread().interrupt(); + } + }; + threadPoolExecutor = new ThreadPoolExecutor(1, Runtime.getRuntime().availableProcessors() * 2 + 1, 5, TimeUnit.MINUTES, new SynchronousQueue<>(), ThreadFactoryBuilder.create().setNamePrefix("redis-queue-handler-").build(), rejectedExecutionHandler); + redisQueueListener = new RedisQueueListener(); + redisQueueListener.setDaemon(true); + redisQueueListener.start(); + } + + + /** + * Redis队列监听器 + */ + private class RedisQueueListener extends Thread { + + + private void submitData(Object jmsDTO) { + if (Objects.isNull(jmsDTO)) { + LOGGER.warn("redis queue {} pop data is null", JmsDestinationConstant.DEFAULT_NAME); + return; + } + if (!(jmsDTO instanceof JmsDTO)) { + LOGGER.warn("Types of data that cannot be processed class: {}", jmsDTO.getClass()); + return; + } + JmsDTO data = (JmsDTO) jmsDTO; + JmsHandler jmsHandler = getJmsHandler(data.getType()); + if (Objects.isNull(jmsHandler)) { + LOGGER.warn("{} no handler", data.getType()); + return; + } + threadPoolExecutor.execute(() -> { + try { + jmsHandler.handlerMessage(data); + } catch (Exception e) { + LOGGER.error("{}, msgType: {} processing error, processing data: {}", Thread.currentThread().getName(), data.getType(), JsonUtils.toJSONString(data), e); + } + }); + } + + + @Override + public void run() { + while (!Thread.interrupted()) { + try { + submitData(messageQueue.leftPop(RandomUtil.getRandom().nextInt(800, 1000), TimeUnit.MILLISECONDS)); + } catch (Exception e) { + LOGGER.warn("listen redis queue {} exception", JmsDestinationConstant.DEFAULT_NAME, e); + } + } + } + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/CommonMessageQueueProducer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/CommonMessageQueueProducer.java new file mode 100644 index 00000000..8ddd1f54 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/CommonMessageQueueProducer.java @@ -0,0 +1,54 @@ +package com.dstz.component.mq.engine.producer; + +import cn.hutool.core.collection.CollectionUtil; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.engine.constants.MqExceptionCodeConstant; +import com.dstz.component.mq.api.constants.JmsDestinationConstant; +import com.dstz.component.mq.api.model.JmsDTO; +import com.dstz.component.mq.api.producer.JmsProducer; +import org.apache.commons.lang3.reflect.MethodUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jms.core.JmsTemplate; + +import java.util.List; + +/** + * 通用消息队列消息发送具体实现 + * + * @author lightning + */ +public class CommonMessageQueueProducer implements JmsProducer { + + private static final Logger LOGGER = LoggerFactory.getLogger(CommonMessageQueueProducer.class); + + @Autowired + private JmsTemplate jmsTemplate; + + @Override + public void sendToQueue(JmsDTO message) { + if (message == null) { + LOGGER.info("传入参数为空, 跳过执行"); + return; + } + try { + MethodUtils.invokeMethod(jmsTemplate, "convertAndSend", JmsDestinationConstant.DEFAULT_NAME, message); + } catch (Exception e) { + LOGGER.warn("JMS发送失败, 发送参数:{}", JsonUtils.toJSONString(message)); + throw new BusinessException(MqExceptionCodeConstant.SEND_ERROR, e); + } + } + + @Override + public void sendToQueue(List messages) { + if (CollectionUtil.isEmpty(messages)) { + LOGGER.info("传入参数为空, 跳过执行"); + return; + } + for (JmsDTO jmsDTO : messages) { + sendToQueue(jmsDTO); + } + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/DiscardQueueProducer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/DiscardQueueProducer.java new file mode 100644 index 00000000..677f948d --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/DiscardQueueProducer.java @@ -0,0 +1,45 @@ +package com.dstz.component.mq.engine.producer; + +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.engine.constants.MqExceptionCodeConstant; +import com.dstz.component.mq.api.model.JmsDTO; +import com.dstz.component.mq.api.producer.JmsProducer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collections; +import java.util.List; + +/** + * 抛弃队列提供者 + * + * @author lightning + */ +public class DiscardQueueProducer implements JmsProducer { + + private static final Logger logger = LoggerFactory.getLogger(DiscardQueueProducer.class); + + @Override + public void sendToQueue(JmsDTO message) throws ApiException { + try { + if (logger.isInfoEnabled()) { + sendToQueue(Collections.singletonList(message)); + } + } catch (Exception e) { + throw new BusinessException(MqExceptionCodeConstant.SEND_ERROR, e); + } + } + + @Override + public void sendToQueue(List messages) { + try { + if (logger.isInfoEnabled()) { + logger.info("discard message: {}", JsonUtils.toJSONString(messages)); + } + } catch (Exception e) { + throw new BusinessException(MqExceptionCodeConstant.SEND_ERROR, e); + } + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/RedisMessageQueueProducer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/RedisMessageQueueProducer.java new file mode 100644 index 00000000..16965a6e --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/RedisMessageQueueProducer.java @@ -0,0 +1,52 @@ +package com.dstz.component.mq.engine.producer; + +import cn.hutool.core.collection.CollectionUtil; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.api.constants.JmsDestinationConstant; +import com.dstz.component.mq.api.model.JmsDTO; +import com.dstz.component.mq.api.producer.JmsProducer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.RedisTemplate; + +import java.util.List; + +/** + * Redis消息队列提供实现 + * + * @author lightning + */ +public class RedisMessageQueueProducer implements JmsProducer { + + private static final Logger LOGGER = LoggerFactory.getLogger(RedisMessageQueueProducer.class); + + @Autowired + private RedisTemplate redisTemplate; + + @SuppressWarnings("unchecked") + @Override + public void sendToQueue(JmsDTO message) { + if (message == null) { + LOGGER.info("传入参数为空, 跳过执行"); + return; + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(JsonUtils.toJSONString(message)); + } + redisTemplate.boundListOps(JmsDestinationConstant.DEFAULT_NAME).rightPush(message); + } + + @SuppressWarnings("unchecked") + @Override + public void sendToQueue(List messages) { + if (CollectionUtil.isEmpty(messages)) { + LOGGER.info("传入参数为空, 跳过执行"); + return; + } + if (LOGGER.isDebugEnabled()) { + LOGGER.debug(JsonUtils.toJSONString(messages)); + } + redisTemplate.boundListOps(JmsDestinationConstant.DEFAULT_NAME).rightPushAll(messages.toArray()); + } +} diff --git a/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/SynchronousQueueProducer.java b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/SynchronousQueueProducer.java new file mode 100644 index 00000000..e641f319 --- /dev/null +++ b/ab-component/ab-component-mq-engine/src/main/java/com/dstz/component/mq/engine/producer/SynchronousQueueProducer.java @@ -0,0 +1,50 @@ +package com.dstz.component.mq.engine.producer; + +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.component.mq.engine.constants.MqExceptionCodeConstant; +import com.dstz.component.mq.engine.consumer.AbstractMessageQueue; +import com.dstz.component.mq.api.JmsHandler; +import com.dstz.component.mq.api.model.JmsDTO; +import com.dstz.component.mq.api.producer.JmsProducer; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.List; + +/** + * 同步队列发送,内部应用 + * + * @author lightning + */ +public class SynchronousQueueProducer extends AbstractMessageQueue implements JmsProducer { + + private static final Logger logger = LoggerFactory.getLogger(SynchronousQueueProducer.class); + + @SuppressWarnings("unchecked") + @Override + public void sendToQueue(JmsDTO message) { + try { + JmsHandler jmsHandler = getJmsHandler(message.getType()); + if (jmsHandler == null) { + logger.warn("{} handler did not find a processing implementation", message.getType()); + return; + } + jmsHandler.handlerMessage(message); + } catch (Exception e) { + throw new BusinessException(MqExceptionCodeConstant.SEND_ERROR.formatMessage(e.getMessage())); + } + } + + @Override + public void sendToQueue(List messages) { + try { + if (messages != null && !messages.isEmpty()) { + for (JmsDTO jmsDTO : messages) { + sendToQueue(jmsDTO); + } + } + } catch (Exception e) { + throw new BusinessException(MqExceptionCodeConstant.SEND_ERROR, e); + } + } +} diff --git a/ab-component/ab-component-msg-api/pom.xml b/ab-component/ab-component-msg-api/pom.xml new file mode 100644 index 00000000..9a5aef75 --- /dev/null +++ b/ab-component/ab-component-msg-api/pom.xml @@ -0,0 +1,25 @@ + + + + ab-component + com.dstz + 2.5.0 + + + 4.0.0 + ab-component-msg-api + + + + + + + com.dstz + ab-base-common + + + + + diff --git a/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MessageTemplateApi.java b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MessageTemplateApi.java new file mode 100644 index 00000000..db5afbd9 --- /dev/null +++ b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MessageTemplateApi.java @@ -0,0 +1,23 @@ +package com.dstz.component.msg.api; + +import com.dstz.component.msg.api.vo.MessageTemplateVO; + +/** + *

+ * 消息模板api + *

+ * + * @author lightning + * @since 2022-11-18 + */ +public interface MessageTemplateApi { + + + /** + * 根据code查询模板信息 + * + * @param code + * @return + */ + MessageTemplateVO getTemplateByCode(String code); +} diff --git a/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MsgApi.java b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MsgApi.java new file mode 100644 index 00000000..82ac63c8 --- /dev/null +++ b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/MsgApi.java @@ -0,0 +1,39 @@ +package com.dstz.component.msg.api; + +import cn.hutool.core.util.ObjectUtil; +import com.dstz.component.msg.api.dto.MsgDTO; + +import java.util.List; + +/** + * 消息发送实现 + * + * @author lightning + */ +public interface MsgApi { + + /** + * 发送消息 + * + * @param msgDTO + */ + void sendMsg(MsgDTO msgDTO); + + /** + * 批量发送消息 + * + * @param msgDTOList + */ + void sendMsg(List msgDTOList); + + + /** + * 模板转换 + * eg 把 请审阅 ${myParam} 转换为 请审阅 xxx请假申请 + * @param templateStr 要解析的字符串 + * @param templateCode 模板编码 + * @param obj 系统参数 + * @return + */ + String convertTemplateStr(String templateStr, String templateCode, Object obj); +} diff --git a/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/dto/MsgDTO.java b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/dto/MsgDTO.java new file mode 100644 index 00000000..d07d1c1c --- /dev/null +++ b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/dto/MsgDTO.java @@ -0,0 +1,140 @@ +package com.dstz.component.msg.api.dto; + +import com.dstz.base.common.identityconvert.SysIdentity; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 模板消息dto + *

+ * + * @author lightning + * @since 2022-11-21 + */ +public class MsgDTO implements Serializable { + //标题 + private String subject; + + //模板key + private String templateCode; + + //接收者 + private List receivers; + + //业务id + private String businessId; + + //业务类型 参考com.dstz.base.common.constats.InnerMsgEnum + private String innerMsgType; + + //发送消息类型, 消息具体实现如email + private List msgType; + + //格式化所需系统参数 + private Object object; + + //扩展参数 + private Map extendVars = new HashMap(); + + public MsgDTO() { + } + + public MsgDTO(String subject, String templateCode, List receivers, List msgType, Object object) { + this.subject = subject; + this.templateCode = templateCode; + this.receivers = receivers; + this.msgType = msgType; + this.object = object; + } + + + public MsgDTO(String subject, String templateCode, List receivers, String businessId, String innerMsgType, List msgType, Object object) { + this.subject = subject; + this.templateCode = templateCode; + this.receivers = receivers; + this.businessId = businessId; + this.innerMsgType = innerMsgType; + this.msgType = msgType; + this.object = object; + } + + public MsgDTO(String subject, String templateCode, List receivers, String businessId, String innerMsgType, List msgType, Object object, Map extendVars) { + this.subject = subject; + this.templateCode = templateCode; + this.receivers = receivers; + this.businessId = businessId; + this.innerMsgType = innerMsgType; + this.msgType = msgType; + this.object = object; + this.extendVars = extendVars; + } + + public Object getObject() { + return object; + } + + public void setObject(Object object) { + this.object = object; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public String getTemplateCode() { + return templateCode; + } + + public void setTemplateCode(String templateCode) { + this.templateCode = templateCode; + } + + + public List getReceivers() { + return receivers; + } + + public void setReceivers(List receivers) { + this.receivers = receivers; + } + + public String getBusinessId() { + return businessId; + } + + public void setBusinessId(String businessId) { + this.businessId = businessId; + } + + public String getInnerMsgType() { + return innerMsgType; + } + + public void setInnerMsgType(String innerMsgType) { + this.innerMsgType = innerMsgType; + } + + public Map getExtendVars() { + return extendVars; + } + + public void setExtendVars(Map extendVars) { + this.extendVars = extendVars; + } + + public List getMsgType() { + return msgType; + } + + public void setMsgType(List msgType) { + this.msgType = msgType; + } +} diff --git a/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/CardTemplateData.java b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/CardTemplateData.java new file mode 100644 index 00000000..bafcc4eb --- /dev/null +++ b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/CardTemplateData.java @@ -0,0 +1,44 @@ +package com.dstz.component.msg.api.vo; + +import java.io.Serializable; + +/** + *

+ * 消息模板dto + *

+ * + * @author lightning + * @since 2022-11-17 + */ +public class CardTemplateData implements Serializable { + private String cardTitle; + private String cardUrl; + private String cardContent; + + public CardTemplateData() { + } + + public String getCardTitle() { + return cardTitle; + } + + public void setCardTitle(String cardTitle) { + this.cardTitle = cardTitle; + } + + public String getCardUrl() { + return cardUrl; + } + + public void setCardUrl(String cardUrl) { + this.cardUrl = cardUrl; + } + + public String getCardContent() { + return cardContent; + } + + public void setCardContent(String cardContent) { + this.cardContent = cardContent; + } +} diff --git a/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/MessageTemplateVO.java b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/MessageTemplateVO.java new file mode 100644 index 00000000..2fddc11b --- /dev/null +++ b/ab-component/ab-component-msg-api/src/main/java/com/dstz/component/msg/api/vo/MessageTemplateVO.java @@ -0,0 +1,111 @@ +package com.dstz.component.msg.api.vo; + +import java.io.Serializable; +import java.util.List; + +/** + *

+ * 消息模板dto + *

+ * + * @author lightning + * @since 2022-11-17 + */ +public class MessageTemplateVO implements Serializable { + + /** + * 模板编码 + */ + private String code; + + /** + * 模板名称 + */ + private String name; + + /** + * 模板描述 + */ + private String desc; + + /** + * html模板配置 + */ + private String htmlTemplate; + + /** + * 应用模板配置 + */ + private String appTemplate; + + /** + * 卡片模板配置 + */ + private String cardTemplate; + + /** + * 模板参数 + */ + private String templateParamJson; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getHtmlTemplate() { + return htmlTemplate; + } + + public void setHtmlTemplate(String htmlTemplate) { + this.htmlTemplate = htmlTemplate; + } + + + public String getAppTemplate() { + return appTemplate; + } + + public void setAppTemplate(String appTemplate) { + this.appTemplate = appTemplate; + } + + + public String getTemplateParamJson() { + return templateParamJson; + } + + public void setTemplateParamJson(String templateParamJson) { + this.templateParamJson = templateParamJson; + } + + public String getCardTemplate() { + return cardTemplate; + } + + public void setCardTemplate(String cardTemplate) { + this.cardTemplate = cardTemplate; + } +} + + + diff --git a/ab-component/ab-component-msg-engine/pom.xml b/ab-component/ab-component-msg-engine/pom.xml new file mode 100644 index 00000000..e8cf0986 --- /dev/null +++ b/ab-component/ab-component-msg-engine/pom.xml @@ -0,0 +1,46 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-msg-engine + + + + com.dstz + ab-component-mq-engine + ${project.version} + + + com.dstz + ab-org-api + ${project.version} + + + + cn.hutool + hutool-http + ${hutool.version} + + + + com.dstz + ab-component-msg-api + ${project.version} + + + + + com.aliyun + aliyun-java-sdk-core + 4.4.6 + + + + \ No newline at end of file diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MessageTemplateApiImpl.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MessageTemplateApiImpl.java new file mode 100644 index 00000000..0e7bd6f1 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MessageTemplateApiImpl.java @@ -0,0 +1,63 @@ +package com.dstz.component.mq.msg.engine.api.impl; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.msg.engine.constants.MsgCackeKeyConstant; +import com.dstz.component.mq.msg.engine.core.entity.MessageTemplate; +import com.dstz.component.mq.msg.engine.core.entity.model.TemplateParamData; +import com.dstz.component.mq.msg.engine.core.manager.MessageTemplateManager; +import com.dstz.component.msg.api.MessageTemplateApi; +import com.dstz.component.msg.api.vo.CardTemplateData; +import com.dstz.component.msg.api.vo.MessageTemplateVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.Map; +import java.util.stream.Collectors; + +import static com.dstz.component.mq.msg.engine.constants.MsgEngineStatusCode.GET_DATA_BY_CODE_IS_NULL; +import static com.dstz.component.mq.msg.engine.constants.MsgEngineStatusCode.PARAM_TEMPLATE_CODE_MISS; + +/** + * 业务消息实现 + * + * @author lightning + */ +@Service +public class MessageTemplateApiImpl implements MessageTemplateApi { + + @Autowired + MessageTemplateManager messageTemplateManager; + + /** + * 根据code从缓存获取模板信息 + * + * @param code + * @return + */ + @Cacheable(cacheNames = AbCacheRegionConstant.MSG_REGION, key = MsgCackeKeyConstant.ET_MESSAGE_TEMPLATE_LIST_RECEIVE_EL) + public MessageTemplateVO getTemplateByCodeInCache(String code) { + Assert.notBlank(code, () -> new BusinessMessage(PARAM_TEMPLATE_CODE_MISS)); + MessageTemplate messageTemplate = messageTemplateManager.selectOne(Wrappers.lambdaQuery(MessageTemplate.class).eq(MessageTemplate::getCode, code)); + Assert.notNull(messageTemplate, () -> new BusinessMessage(GET_DATA_BY_CODE_IS_NULL.formatDefaultMessage(code))); + MessageTemplateVO templateVO = new MessageTemplateVO(); + BeanCopierUtils.copyProperties(messageTemplate, templateVO); + if (StrUtil.isNotBlank(messageTemplate.getTemplateParam())) { + Map paramMap = JsonUtils.parseArray(messageTemplate.getTemplateParam(), TemplateParamData.class).stream().collect(Collectors.toMap(TemplateParamData::getKey, TemplateParamData::getValue)); + templateVO.setTemplateParamJson(JsonUtils.toJSONString(paramMap)); + } + return templateVO; + } + + @Override + public MessageTemplateVO getTemplateByCode(String code) { + return ObjectUtil.clone(this.getTemplateByCodeInCache(code)); + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MsgApiImpl.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MsgApiImpl.java new file mode 100644 index 00000000..26d94739 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/api/impl/MsgApiImpl.java @@ -0,0 +1,135 @@ +package com.dstz.component.mq.msg.engine.api.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.ReUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.freemark.impl.FreemarkerEngine; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.api.model.DefaultJmsDTO; +import com.dstz.component.mq.api.model.JmsDTO; +import com.dstz.component.mq.api.producer.JmsProducer; +import com.dstz.component.mq.msg.engine.dto.MsgImplDTO; +import com.dstz.component.msg.api.MessageTemplateApi; +import com.dstz.component.msg.api.MsgApi; +import com.dstz.component.msg.api.dto.MsgDTO; +import com.dstz.component.msg.api.vo.CardTemplateData; +import com.dstz.component.msg.api.vo.MessageTemplateVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.regex.Pattern; + +import static com.dstz.component.mq.msg.engine.constants.MsgEngineStatusCode.PARAM_TEMPLATE_CODE_MISS; + +/** + * 业务消息实现 + * + * @author lightning + */ +@Service +public class MsgApiImpl implements MsgApi { + + @Autowired + JmsProducer jmsProducer; + + @Autowired + MessageTemplateApi messageTemplateApi; + + @Autowired + FreemarkerEngine freemarkerEngine; + + private Pattern TMP_PARAM_REGEX = Pattern.compile(".*\\$\\{.*\\}.*"); + + /** + * 单条发送 + * + * @param msgDTO + */ + @Override + public void sendMsg(MsgDTO msgDTO) { + if (CollUtil.isNotEmpty(msgDTO.getReceivers())) { + jmsProducer.sendToQueue(dealMsgDTO(msgDTO)); + } + } + + /** + * 批量发送 + * + * @param msgDTOList + */ + @Override + public void sendMsg(List msgDTOList) { + List jmsDTOList = CollUtil.newArrayList(); + msgDTOList.forEach(e -> { + if (CollUtil.isNotEmpty(e.getReceivers())) { + jmsDTOList.addAll(dealMsgDTO(e)); + } + }); + jmsProducer.sendToQueue(jmsDTOList); + } + + public String convertTemplateParam(String templateParam, Object object) { + if (StrUtil.isNotBlank(templateParam) && ReUtil.isMatch(TMP_PARAM_REGEX, templateParam)) { + return freemarkerEngine.parseByString(templateParam, object); + } + return templateParam; + } + + public String convertTemplateStr(String templateStr, String paramStr) { + if (StrUtil.isNotBlank(templateStr) && ReUtil.isMatch(TMP_PARAM_REGEX, templateStr)) { + Map paramMap = JsonUtils.parseObject(paramStr, Map.class); + return freemarkerEngine.parseByString(templateStr, paramMap); + } + return templateStr; + } + + @Override + public String convertTemplateStr(String templateStr, String templateCode, Object obj) { + MessageTemplateVO messageTemplateVO = messageTemplateApi.getTemplateByCode(templateCode); + if (StrUtil.isNotBlank(messageTemplateVO.getTemplateParamJson()) && ReUtil.isMatch(TMP_PARAM_REGEX, messageTemplateVO.getTemplateParamJson())) { + return convertTemplateStr(templateStr, convertTemplateParam(messageTemplateVO.getTemplateParamJson(), obj)); + } + return templateStr; + } + + /** + * 处理请求参数 + * + * @param msgDTO + * @return + */ + private List dealMsgDTO(MsgDTO msgDTO) { + MessageTemplateVO messageTemplateVO = messageTemplateApi.getTemplateByCode(msgDTO.getTemplateCode()); + String htmlTemplate = messageTemplateVO.getHtmlTemplate(); + String cardTemplate = messageTemplateVO.getCardTemplate(); + //格式化用户自定义参数 + String paramJson = this.convertTemplateParam(messageTemplateVO.getTemplateParamJson(), msgDTO.getObject()); + Map paramMap = MapUtil.newHashMap(); + + if (StrUtil.isNotBlank(paramJson)) { + paramMap = JsonUtils.parseObject(paramJson, Map.class); + //格式化模板消息 + if (StrUtil.isNotBlank(htmlTemplate) && ReUtil.isMatch(TMP_PARAM_REGEX, htmlTemplate)) { + htmlTemplate = freemarkerEngine.parseByString(messageTemplateVO.getHtmlTemplate(), paramMap); + } + if (StrUtil.isNotBlank(cardTemplate) && ReUtil.isMatch(TMP_PARAM_REGEX, cardTemplate)) { + cardTemplate = freemarkerEngine.parseByString(messageTemplateVO.getCardTemplate(), paramMap); + } + //格式化标题 + if (StrUtil.isNotBlank(msgDTO.getSubject()) && ReUtil.isMatch(TMP_PARAM_REGEX, msgDTO.getSubject())) { + msgDTO.setSubject(freemarkerEngine.parseByString(msgDTO.getSubject(), paramMap)); + } + } + CardTemplateData cardTemplateData = StrUtil.isNotBlank(cardTemplate) ? JsonUtils.parseObject(cardTemplate, CardTemplateData.class) : new CardTemplateData(); + MsgImplDTO msgImplDTO = new MsgImplDTO(msgDTO.getSubject(), paramMap, htmlTemplate, cardTemplateData, msgDTO.getReceivers(), msgDTO.getBusinessId(), msgDTO.getInnerMsgType(), msgDTO.getExtendVars()); + List msgDTOList = new LinkedList<>(); + msgDTO.getMsgType().forEach(e -> msgDTOList.add(new DefaultJmsDTO<>(e, msgImplDTO))); + return msgDTOList; + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgCackeKeyConstant.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgCackeKeyConstant.java new file mode 100644 index 00000000..e0ea5b60 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgCackeKeyConstant.java @@ -0,0 +1,21 @@ +package com.dstz.component.mq.msg.engine.constants; + +/** + * 系统缓存key定义 + * + * @author lightning + * @since 2022-08-17 + */ +public class MsgCackeKeyConstant { + + /** + * 消息模板方法缓存key SpEL表达式 + */ + public static final String GET_MESSAGE_TEMPLATE_LIST = "'getMessageTemplateListEl:'"; + + /** + * 字典方法缓存key SpEL表达式 + */ + public static final String ET_MESSAGE_TEMPLATE_LIST_RECEIVE_EL = GET_MESSAGE_TEMPLATE_LIST + ".concat(#root.args[0])"; + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgEngineStatusCode.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgEngineStatusCode.java new file mode 100644 index 00000000..fdc2aaef --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/constants/MsgEngineStatusCode.java @@ -0,0 +1,40 @@ +package com.dstz.component.mq.msg.engine.constants; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * msg实现相关响应码 + * + * @author lightning + */ +public enum MsgEngineStatusCode implements IBaseCode { + + + + PARAM_TEMPLATE_CODE_MISS("param_template_code_miss","模板code参数缺失"), + GET_DATA_BY_CODE_IS_NULL("get_data_by_code_is_null","根据code未查到消息模板数据code:{}"), + + EMAIL_SEND_ERROR("email_send_error", "邮件发送失败 {}"), + + + ; + + + private final String code; + private final String desc; + + MsgEngineStatusCode(String code, String description) { + this.code = code; + this.desc = description; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return desc; + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/MessageTemplate.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/MessageTemplate.java new file mode 100644 index 00000000..d7f43785 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/MessageTemplate.java @@ -0,0 +1,281 @@ +package com.dstz.component.mq.msg.engine.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; + +import javax.validation.constraints.NotEmpty; +import java.util.Date; +import java.io.Serializable; + +/** + *

+ * + *

+ * + * @author lightning + * @since 2022-11-14 + */ +@TableName("ab_message_template") +public class MessageTemplate extends AbModel { + + /** + * 模板id + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 模板编码 + */ + @NotEmpty(message = "模板编码不能为空!") + @TableField("code_") + private String code; + + /** + * 模板类型 + */ + @TableField("type_") + private Integer type; + + /** + * 模板名称 + */ + @TableField("name_") + @NotEmpty(message = "模板名称不能为空!") + private String name; + + /** + * 模板描述 + */ + @TableField("desc_") + private String desc; + + /** + * html模板配置 + */ + @TableField("html_template_") + private String htmlTemplate; + + /** + * 卡片模板配置 + */ + @TableField("card_template_") + private String cardTemplate; + + /** + * 应用模板配置 + */ + @TableField("app_template_") + private String appTemplate; + + /** + * 模板参数 + */ + @TableField("template_param_") + private String templateParam; + + /** + * 是否启用 + */ + @TableField("enabled_") + private Integer enabled; + + /** + * 是否删除 + */ + @TableField("is_delete_") + private Integer isDelete; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getHtmlTemplate() { + return htmlTemplate; + } + + public void setHtmlTemplate(String htmlTemplate) { + this.htmlTemplate = htmlTemplate; + } + + public String getCardTemplate() { + return cardTemplate; + } + + public void setCardTemplate(String cardTemplate) { + this.cardTemplate = cardTemplate; + } + + public String getAppTemplate() { + return appTemplate; + } + + public void setAppTemplate(String appTemplate) { + this.appTemplate = appTemplate; + } + + public String getTemplateParam() { + return templateParam; + } + + public void setTemplateParam(String templateParam) { + this.templateParam = templateParam; + } + + public Integer getEnabled() { + return enabled; + } + + public void setEnabled(Integer enabled) { + this.enabled = enabled; + } + + public Integer getIsDelete() { + return isDelete; + } + + public void setIsDelete(Integer isDelete) { + this.isDelete = isDelete; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/model/TemplateParamData.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/model/TemplateParamData.java new file mode 100644 index 00000000..1cfeeb41 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/entity/model/TemplateParamData.java @@ -0,0 +1,30 @@ +package com.dstz.component.mq.msg.engine.core.entity.model; + +/** + *

+ * 消息模板dto + *

+ * + * @author lightning + * @since 2022-11-17 + */ +public class TemplateParamData { + private String key; + private String value; + + public String getKey() { + return key; + } + + public void setKey(String key) { + this.key = key; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/MessageTemplateManager.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/MessageTemplateManager.java new file mode 100644 index 00000000..d4d09b05 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/MessageTemplateManager.java @@ -0,0 +1,17 @@ +package com.dstz.component.mq.msg.engine.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.component.mq.msg.engine.core.entity.MessageTemplate; + +/** + *

+ * 通用业务类 + *

+ * + * @author lightning + * @since 2022-11-14 + */ +public interface MessageTemplateManager extends AbBaseManager { + + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/impl/MessageTemplateManagerImpl.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/impl/MessageTemplateManagerImpl.java new file mode 100644 index 00000000..9aa9f342 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/manager/impl/MessageTemplateManagerImpl.java @@ -0,0 +1,17 @@ +package com.dstz.component.mq.msg.engine.core.manager.impl; + +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.component.mq.msg.engine.core.entity.MessageTemplate; +import com.dstz.component.mq.msg.engine.core.manager.MessageTemplateManager; +import org.springframework.stereotype.Service; + +/** + * 通用服务实现类 + * + * @author lightning + * @since 2022-11-14 + */ +@Service("messageTemplateManager") +public class MessageTemplateManagerImpl extends AbBaseManagerImpl implements MessageTemplateManager { + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/mapper/MessageTemplateMapper.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/mapper/MessageTemplateMapper.java new file mode 100644 index 00000000..3a792dd8 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/core/mapper/MessageTemplateMapper.java @@ -0,0 +1,18 @@ +package com.dstz.component.mq.msg.engine.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.component.mq.msg.engine.core.entity.MessageTemplate; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author lightning + * @since 2022-11-14 + */ +@Mapper +public interface MessageTemplateMapper extends AbBaseMapper { + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/dto/MsgImplDTO.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/dto/MsgImplDTO.java new file mode 100644 index 00000000..82e11efb --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/dto/MsgImplDTO.java @@ -0,0 +1,127 @@ +package com.dstz.component.mq.msg.engine.dto; + +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.component.msg.api.vo.CardTemplateData; + +import java.io.Serializable; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + *

+ * 模板消息业务实现dto + *

+ * + * @author lightning + * @since 2022-11-21 + */ +public class MsgImplDTO implements Serializable { + //标题 + private String subject; + + //模板参数 + private Map templateParam; + + //html模板 + private String htmlTemplate; + + //卡片模板 + private CardTemplateData cardTemplateData; + + //接收者 + private List receivers; + + //业务id + private String businessId; + + //业务类型 参考com.dstz.base.common.constats.InnerMsgEnum + private String innerMsgType; + + //扩展参数 + private Map extendVars = new HashMap(); + + public MsgImplDTO() { + } + + public MsgImplDTO(String subject,String htmlTemplate, List receivers) { + this.subject = subject; + this.htmlTemplate = htmlTemplate; + this.receivers = receivers; + } + + public MsgImplDTO(String subject,Map templateParam, String htmlTemplate, CardTemplateData cardTemplateData, List receivers, String businessId, String innerMsgType, Map extendVars) { + this.subject = subject; + this.templateParam = templateParam; + this.htmlTemplate = htmlTemplate; + this.cardTemplateData = cardTemplateData; + this.receivers = receivers; + this.businessId = businessId; + this.innerMsgType = innerMsgType; + this.extendVars = extendVars; + } + + public String getSubject() { + return subject; + } + + public void setSubject(String subject) { + this.subject = subject; + } + + public Map getTemplateParam() { + return templateParam; + } + + public void setTemplateParam(Map templateParam) { + this.templateParam = templateParam; + } + + public String getHtmlTemplate() { + return htmlTemplate; + } + + public void setHtmlTemplate(String htmlTemplate) { + this.htmlTemplate = htmlTemplate; + } + + public CardTemplateData getCardTemplateData() { + return cardTemplateData; + } + + public void setCardTemplateData(CardTemplateData cardTemplateData) { + this.cardTemplateData = cardTemplateData; + } + + public List getReceivers() { + return receivers; + } + + public void setReceivers(List receivers) { + this.receivers = receivers; + } + + public String getBusinessId() { + return businessId; + } + + public void setBusinessId(String businessId) { + this.businessId = businessId; + } + + public String getInnerMsgType() { + return innerMsgType; + } + + public void setInnerMsgType(String innerMsgType) { + this.innerMsgType = innerMsgType; + } + + public Map getExtendVars() { + return extendVars; + } + + public void setExtendVars(Map extendVars) { + this.extendVars = extendVars; + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/MailHandler.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/MailHandler.java new file mode 100644 index 00000000..55cc723a --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/MailHandler.java @@ -0,0 +1,79 @@ + +package com.dstz.component.mq.msg.engine.handler; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.identityconvert.IdentityConvert; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.msg.engine.dto.MsgImplDTO; +import com.dstz.component.mq.msg.engine.util.EmailUtil; +import com.dstz.component.third.msg.engine.handler.AbsNotifyMessageHandler; +import com.dstz.org.api.model.IUser; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import static com.dstz.component.mq.api.constants.JmsTypeEnum.EMAIL; +import static com.dstz.component.mq.msg.engine.constants.MsgEngineStatusCode.EMAIL_SEND_ERROR; + + +/** + * 邮件消息处理器。 + * + * @author lightning + */ + +@Component("mailHandler") +public class MailHandler extends AbsNotifyMessageHandler { + + private static final Logger LOGGER = LoggerFactory.getLogger(MailHandler.class); + @Autowired + IdentityConvert identityConvert; + + @Override + public String getType() { + return EMAIL.getType(); + } + + + @Override + public String getTitle() { + return "邮件"; + } + + @Override + public boolean getIsDefault() { + return true; + } + + + @Override + public boolean getSupportHtml() { + return true; + } + + @Override + public boolean sendMessage(MsgImplDTO notifMessage) { + for (IUser reciver : identityConvert.convert2Users(notifMessage.getReceivers())) { + String email = reciver.getAttrValue("email", String.class); + if (StrUtil.isEmpty(email)) { + continue; + } + //优先从普通文本框获取内容, 为空时再获取富文本 + String text = notifMessage.getHtmlTemplate(); + String content = text.replace(PARAM_USERNAME, reciver.getFullName()); + + try { + EmailUtil.send(email, notifMessage.getSubject(), content); + } catch (Exception e) { + LOGGER.error("发送邮件失败:{}, 发送参数:{}", e.getMessage(), JsonUtils.toJSONString(notifMessage), e); + throw new BusinessException(EMAIL_SEND_ERROR.formatDefaultMessage(e.getMessage())); + } + } + LOGGER.debug("发送邮件成功 :{}", JsonUtils.toJSONString(notifMessage)); + return true; + } + +} + diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/SmsHandler.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/SmsHandler.java new file mode 100644 index 00000000..167e26d2 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/handler/SmsHandler.java @@ -0,0 +1,143 @@ +package com.dstz.component.mq.msg.engine.handler; + +import com.dstz.base.common.identityconvert.IdentityConvert; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.mq.msg.engine.dto.MsgImplDTO; +import com.dstz.component.third.msg.engine.handler.AbsNotifyMessageHandler; +import com.dstz.org.api.model.IUser; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import static com.dstz.component.mq.api.constants.JmsTypeEnum.SMS; + +/** + * 短消息发送处理器 + * + * @author lightning + */ +@Component("smsHandler") +public class SmsHandler extends AbsNotifyMessageHandler { + @Autowired + IdentityConvert identityConvert; + + @Override + public String getType() { + return SMS.getType(); + } + + @Override + public boolean sendMessage(MsgImplDTO message) { + for (IUser user : identityConvert.convert2Users(message.getReceivers())) { + System.err.print(JsonUtils.toJSONString(message)); + System.err.println(user.getAttrValue("mobile", String.class) + "请自行对接短信推送API"); + /* + * IAcsClient client = getIacsClient(); if (client == null || + * StrUtil.isEmpty(user.getAttrValue("mobile", String.class))) { + * LOGGER.warn("IAcsClient is null or user mobile is empty"); continue; } + * //优先从普通文本框获取内容, 为空时再获取富文本 String text = message.getHtmlTemplate(); String + * content = text.replace(PARAM_USERNAME, user.getFullName()); + * + * CommonRequest smsRequest = getCommonRequest(); + * + * smsRequest.putQueryParameter("PhoneNumbers", user.getAttrValue("mobile", + * String.class)); + * + * setTemplateParam(smsRequest, user, content); + * + * + * try { CommonResponse response = client.getCommonResponse(smsRequest); + * JsonNode jsonNode = JsonUtils.toJSONNode(response.getData()); + * + * + * if (!"OK".equals(jsonNode.get("Code"))) { LOGGER.error("发送短信失败:" + + * response.getData()); iAcsClient = null; } } catch (Exception e) { iAcsClient + * = null; LOGGER.error("发送短信失败" + e.getMessage(), e); } + */ + } + return true; + } + + /* *//** + * 支持短信中自定义参数与模板。 否则使用默认模板 【 ${name}您好,您有新的任务${subject},请及时查阅。】 {$subject} + * 即为消息体内容 + * + * @param smsRequest + * @param user + * @param content + */ + + /* + * private void setTemplateParam(CommonRequest smsRequest, IUser user, String + * content) { + * + * if (content.startsWith("{")) { JsonNode templateParam = + * JsonUtils.toJSONNode(content); if (templateParam.has("templateCode")) { + * smsRequest.putQueryParameter("TemplateCode", + * templateParam.get("templateCode").toString()); } + * smsRequest.putQueryParameter("TemplateParam", content); return; } ObjectNode + * templateParam = JsonUtils.createObjectNode(); templateParam.put("name", + * user.getFullName()); templateParam.put("subject", content); + * smsRequest.putQueryParameter("TemplateParam", + * JsonUtils.toJSONString(templateParam)); + * + * } + * + *//** + * 默认的请求 + * + * @return + *//* + * private CommonRequest getCommonRequest() { String regionId = + * PropertyEnum.ALI_YUN_REGION_ID.getPropertyValue(String.class); String + * sysDomain = PropertyEnum.ALI_YUN_SYS_DOMAIN.getPropertyValue(String.class); + * String signName = + * PropertyEnum.ALI_YUN_SIGN_NAME.getPropertyValue(String.class); String + * templateCode = + * PropertyEnum.ALI_YUN_TEMPLATE_CODE.getPropertyValue(String.class); + * + * CommonRequest request = new CommonRequest(); + * request.setSysMethod(MethodType.POST); request.setSysDomain(sysDomain); + * request.setSysVersion("2017-05-25"); request.setSysAction("SendSms"); + * request.putQueryParameter("RegionId", regionId); + * + * request.putQueryParameter("SignName", signName); + * request.putQueryParameter("TemplateCode", templateCode); + * + * return request; } + * + * IAcsClient iAcsClient = null; + * + * private IAcsClient getIacsClient() { if (iAcsClient != null) { return + * iAcsClient; } + * + * String regionId = + * PropertyEnum.ALI_YUN_REGION_ID.getPropertyValue(String.class); String + * accessKeyId = + * PropertyEnum.ALI_YUN_ACCESS_KEY_ID.getPropertyValue(String.class); String + * accessSecret = + * PropertyEnum.ALI_YUN_ACCESS_SECRET.getPropertyValue(String.class); if + * (StrUtil.isEmpty(accessKeyId) || StrUtil.isEmpty(accessSecret)) { LOGGER. + * warn("Not configured system property aliyun_accessKeyId、aliyun_accessSecret" + * ); return null; } + * + * DefaultProfile profile = DefaultProfile.getProfile(regionId, accessKeyId, + * accessSecret); IAcsClient client = new DefaultAcsClient(profile); + * this.iAcsClient = client; return client; } + */ + + @Override + public String getTitle() { + return "短信"; + } + + @Override + public boolean getIsDefault() { + return false; + } + + @Override + public boolean getSupportHtml() { + return true; + } + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/rest/controller/MessageTemplateController.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/rest/controller/MessageTemplateController.java new file mode 100644 index 00000000..b811bec6 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/rest/controller/MessageTemplateController.java @@ -0,0 +1,115 @@ +package com.dstz.component.mq.msg.engine.rest.controller; + + +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.component.mq.msg.engine.constants.MsgCackeKeyConstant; +import com.dstz.component.mq.msg.engine.core.entity.MessageTemplate; +import com.dstz.component.mq.msg.engine.core.manager.MessageTemplateManager; +import com.dstz.component.msg.api.MessageTemplateApi; +import com.dstz.component.msg.api.vo.MessageTemplateVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.List; + +/** + *

+ * 前端控制器 + *

+ * + * @author lightning + * @since 2022-11-14 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/messageTemplate/") +public class MessageTemplateController extends AbCrudController { + + @Autowired + MessageTemplateManager messageTemplateManager; + + @Autowired + ICache iCache; + + @Autowired + MessageTemplateApi messageTemplateApi; + + @Override + protected String getEntityDesc() { + return ""; + } + + /** + * 获取可用模板 + * + * @return + */ + @RequestMapping(value = "getEnabledTemplate", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse> getEnabledTemplate() { + return ApiResponse.success(messageTemplateManager.selectByWrapper(Wrappers.lambdaQuery(MessageTemplate.class).eq(MessageTemplate::getEnabled, NumberPool.BOOLEAN_TRUE))); + } + + /** + * 获取可用模板 + * + * @return + */ + @RequestMapping(value = "getTemplateByCode", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse getTemplateByCode(@RequestParam(name = "code") String code) { + return ApiResponse.success(messageTemplateApi.getTemplateByCode(code)); + } + + /** + * 保存实体数据 + * + * @param entity 实体 + * @return 接口响应-实体ID + */ + @RequestMapping(value = "save", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse save(@Valid @RequestBody MessageTemplate entity) { + String desc; + if (StringUtils.hasLength(entity.getId())) { + desc = "更新%s成功"; + messageTemplateManager.update(entity); + } else { + desc = "添加%s成功"; + messageTemplateManager.create(entity); + } + iCache.invalidate(AbCacheRegionConstant.MSG_REGION, MsgCackeKeyConstant.GET_MESSAGE_TEMPLATE_LIST + entity.getCode()); + return ApiResponse.success(entity.getId()).withMessage(String.format(desc, getEntityDesc())); + } + + /** + * 实体批量删除 + * + * @param id 实体ID,多个,分隔 + * @return 接口响应 + */ + @RequestMapping(value = "remove", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse remove(@RequestParam(name = "id") String id) { + List ids = StrUtil.split(id, CharUtil.COMMA); + List messageTemplateList = messageTemplateManager.selectByIds(ids); + messageTemplateList.forEach(e -> { + iCache.invalidate(AbCacheRegionConstant.MSG_REGION, MsgCackeKeyConstant.GET_MESSAGE_TEMPLATE_LIST + e.getCode()); + }); + invokeBeforeCheck("removeCheck", ids); + abBaseManager.removeByIds(ids); + final String message = String.format("删除%s成功", getEntityDesc()); + return ApiResponse.success().withMessage(message); + } + + +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/util/EmailUtil.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/util/EmailUtil.java new file mode 100644 index 00000000..d609d6ec --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/mq/msg/engine/util/EmailUtil.java @@ -0,0 +1,77 @@ +package com.dstz.component.mq.msg.engine.util; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.extra.mail.MailAccount; +import cn.hutool.extra.mail.MailUtil; +import com.dstz.base.common.property.PropertyEnum; + + +/** + * 发邮件工具类 + * + * @author lightning + */ +public class EmailUtil { + + private static MailAccount account; + + /** + *
+     * 用ab的邮箱发邮件
+     * 
+ * + * @param email 目标邮件 eg:aschs@qq.com + * @param subject 主题 + * @param content 内容(内容支持html) + */ + public static void send(String email, String subject, String content) { + MailUtil.send(account(), CollUtil.newArrayList(email), subject, content, true); + } + + private static MailAccount account() { + if (account != null) { + return account; + } + + MailAccount mailAccount = new MailAccount(); + mailAccount.setHost(PropertyEnum.EMAIL_HOST.getPropertyValue(String.class)); + mailAccount.setPort(PropertyEnum.EMAIL_PORT.getPropertyValue(Integer.class)); + mailAccount.setFrom(PropertyEnum.EMAIL_ADDRESS.getPropertyValue(String.class)); + mailAccount.setUser(PropertyEnum.EMAIL_NICKNAME.getPropertyValue(String.class)); + mailAccount.setPass(PropertyEnum.EMAIL_PASSWORD.getPropertyValue(String.class)); + mailAccount.setSslEnable(PropertyEnum.EMAIL_SSL.getPropertyValue(Boolean.class)); + setAccount(mailAccount); + return mailAccount; + } + + /** + * spring boot项目启动的时候设置参数 + * + * @param account + */ + public static void setAccount(MailAccount account) { + EmailUtil.account = account; + } + + public static void main(String[] args) { + MailAccount mailAccount = new MailAccount(); + String host = "smtp.qq.com"; + int port = 465; + boolean isSSL = true; + String user = "956699386@qq.com"; + String from = "956699386@qq.com"; + String pass = "wubmeeilxudybajh"; + mailAccount.setHost(host); + mailAccount.setPort(port); + mailAccount.setFrom(from); + mailAccount.setUser(user); + mailAccount.setPass(pass); + mailAccount.setSslEnable(isSSL); + mailAccount.setDebug(true); + setAccount(mailAccount); + + String subject = "任务待办通知"; + String content = "

您有新的待办需要审批:管工发起poc流程

"; + MailUtil.send(mailAccount, "511322311@qq.com", subject, content, true); + } +} diff --git a/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/third/msg/engine/handler/AbsNotifyMessageHandler.java b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/third/msg/engine/handler/AbsNotifyMessageHandler.java new file mode 100644 index 00000000..9362e8f5 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/java/com/dstz/component/third/msg/engine/handler/AbsNotifyMessageHandler.java @@ -0,0 +1,66 @@ +package com.dstz.component.third.msg.engine.handler; + +import com.dstz.base.common.identityconvert.IdentityConvert; +import com.dstz.component.mq.api.JmsHandler; +import com.dstz.component.mq.api.model.JmsDTO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.Serializable; + +/** + * 做消息类型的公共逻辑 :如日志等 + * + * @param + * @author lightning + */ +public abstract class AbsNotifyMessageHandler implements JmsHandler { + + protected static final Logger LOGGER = LoggerFactory.getLogger(AbsNotifyMessageHandler.class); + + public static final String PARAM_USERNAME = "$userName"; + + @Autowired + IdentityConvert identityConvert; + + /** + * 消息类型名 + * + * @return + */ + public abstract String getTitle(); + + /** + * 是否默认选中 + * + * @return + */ + public boolean getIsDefault() { + return false; + } + + /** + * 是否支持 HTML 内容 + * + * @return + */ + public boolean getSupportHtml() { + return true; + } + + + @Override + public boolean handlerMessage(JmsDTO message) { + return sendMessage(message.getData()); + } + + /** + * 发送消息处理器具体实现 不同消息的发送 + * + * @param data + * @return + */ + public abstract boolean sendMessage(T data); + +} diff --git a/ab-component/ab-component-msg-engine/src/main/resources/com/dstz/component/mq/msg/engine/MessageTemplateMapper.xml b/ab-component/ab-component-msg-engine/src/main/resources/com/dstz/component/mq/msg/engine/MessageTemplateMapper.xml new file mode 100644 index 00000000..4d060143 --- /dev/null +++ b/ab-component/ab-component-msg-engine/src/main/resources/com/dstz/component/mq/msg/engine/MessageTemplateMapper.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-component/ab-component-pubsub-api/pom.xml b/ab-component/ab-component-pubsub-api/pom.xml new file mode 100644 index 00000000..701babc0 --- /dev/null +++ b/ab-component/ab-component-pubsub-api/pom.xml @@ -0,0 +1,22 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + ab-component-pubsub-api + + + + + + + com.dstz + ab-base-common + + + diff --git a/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessagePublisher.java b/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessagePublisher.java new file mode 100644 index 00000000..9486578a --- /dev/null +++ b/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessagePublisher.java @@ -0,0 +1,18 @@ +package com.dstz.component.pubsub; + +/** + * ab message 消息发布 + * + * @author wacxhs + */ +public interface AbMessagePublisher { + + /** + * 消息发布 + * + * @param channel 发布通道 + * @param body 消息体 + */ + void publish(String channel, Object body); + +} diff --git a/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessageSubscriber.java b/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessageSubscriber.java new file mode 100644 index 00000000..55098973 --- /dev/null +++ b/ab-component/ab-component-pubsub-api/src/main/java/com/dstz/component/pubsub/AbMessageSubscriber.java @@ -0,0 +1,23 @@ +package com.dstz.component.pubsub; + +/** + * 消息订阅者 + * + * @author wacxhs + */ +public interface AbMessageSubscriber { + + /** + * 获取消息订阅通道 + * + * @return 消息订阅通道 + */ + String getChannel(); + + /** + * 订阅消息 + * + * @param body 订阅消息体 + */ + void subscribe(T body); +} diff --git a/ab-component/ab-component-pubsub-core/pom.xml b/ab-component/ab-component-pubsub-core/pom.xml new file mode 100644 index 00000000..c5a2b0ad --- /dev/null +++ b/ab-component/ab-component-pubsub-core/pom.xml @@ -0,0 +1,36 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + ab-component-pubsub-core + + + + + + + com.dstz + ab-base-common + + + com.dstz + ab-component-pubsub-api + ${project.version} + + + org.springframework.boot + spring-boot-starter + + + org.springframework.data + spring-data-redis + provided + + + diff --git a/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/AbMessagePubSubAutoConfiguration.java b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/AbMessagePubSubAutoConfiguration.java new file mode 100644 index 00000000..41d8845d --- /dev/null +++ b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/AbMessagePubSubAutoConfiguration.java @@ -0,0 +1,72 @@ +package com.dstz.component.pubsub; + +import com.dstz.base.common.utils.CastUtils; +import com.dstz.component.pubsub.local.AbLocalMessagePublishProvider; +import com.dstz.component.pubsub.redis.RedisMessagePublishProvider; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.ChannelTopic; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.time.Duration; +import java.util.stream.Collectors; + +/** + * 消息发布订阅自动装配 + * + * @author wacxhs + */ +@Configuration(proxyBeanMethods = false) +public class AbMessagePubSubAutoConfiguration { + + @Bean + @ConditionalOnMissingBean + public AbMessagePublisher localMessagePublishProvider(ObjectProvider> subscriberObjectProvider) { + return new AbLocalMessagePublishProvider(subscriberObjectProvider.stream().collect(Collectors.toList())); + } + + + @ConditionalOnBean(RedisConnectionFactory.class) + @Configuration + protected static class AbRedisMessagePubsubConfiguration { + + private void registerMessageListener(RedisMessageListenerContainer redisMessageListenerContainer, ObjectProvider> subscriberObjectProvider) { + GenericJackson2JsonRedisSerializer jackson2JsonRedisSerializer = new GenericJackson2JsonRedisSerializer(); + for (AbMessageSubscriber abMessageSubscriber : subscriberObjectProvider) { + redisMessageListenerContainer.addMessageListener((message, pattern) -> abMessageSubscriber.subscribe(CastUtils.cast(jackson2JsonRedisSerializer.deserialize(message.getBody()))), new ChannelTopic(abMessageSubscriber.getChannel())); + } + } + + @Bean + public RedisMessagePublishProvider redisMessagePublishProvider(RedisConnectionFactory redisConnectionFactory, ObjectProvider> subscriberObjectProvider, @Autowired(required = false) RedisMessageListenerContainer redisMessageListenerContainer) { + if (redisMessageListenerContainer != null) { + registerMessageListener(redisMessageListenerContainer, subscriberObjectProvider); + } + return new RedisMessagePublishProvider(redisConnectionFactory); + } + + @ConditionalOnMissingBean + @Bean + public RedisMessageListenerContainer redisMessageListenerContainer(ObjectProvider> subscriberObjectProvider) { + ThreadPoolTaskExecutor taskExecutor = new TaskExecutorBuilder() + .corePoolSize(1) + .keepAlive(Duration.ofSeconds(60)) + .threadNamePrefix("redis-message-task-") + .maxPoolSize(Runtime.getRuntime().availableProcessors() * 2 + 1) + .queueCapacity(1000) + .build(); + RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); + redisMessageListenerContainer.setTaskExecutor(taskExecutor); + registerMessageListener(redisMessageListenerContainer, subscriberObjectProvider); + return redisMessageListenerContainer; + } + } +} diff --git a/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/local/AbLocalMessagePublishProvider.java b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/local/AbLocalMessagePublishProvider.java new file mode 100644 index 00000000..f906fab5 --- /dev/null +++ b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/local/AbLocalMessagePublishProvider.java @@ -0,0 +1,38 @@ +package com.dstz.component.pubsub.local; + +import com.dstz.base.common.utils.CastUtils; +import com.dstz.component.pubsub.AbMessagePublisher; +import com.dstz.component.pubsub.AbMessageSubscriber; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.Collection; + +/** + * 基于本地实现的发布与订阅,纯内存 + * + * @author wacxhs + */ +public class AbLocalMessagePublishProvider implements AbMessagePublisher { + + private static final Logger logger = LoggerFactory.getLogger(AbLocalMessagePublishProvider.class); + + private final Collection> subscribers; + + public AbLocalMessagePublishProvider(Collection> subscribers) { + this.subscribers = subscribers; + } + + @Override + public void publish(String channel, Object body) { + for (AbMessageSubscriber subscriber : subscribers) { + if (subscriber.getChannel().equals(channel)) { + try { + subscriber.subscribe(CastUtils.cast(body)); + } catch (Throwable e) { + logger.error(e.getMessage(), e); + } + } + } + } +} diff --git a/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/redis/RedisMessagePublishProvider.java b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/redis/RedisMessagePublishProvider.java new file mode 100644 index 00000000..9c161a21 --- /dev/null +++ b/ab-component/ab-component-pubsub-core/src/main/java/com/dstz/component/pubsub/redis/RedisMessagePublishProvider.java @@ -0,0 +1,29 @@ +package com.dstz.component.pubsub.redis; + +import com.dstz.component.pubsub.AbMessagePublisher; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; + +/** + * redis 消息发布实现 + * + * @author wacxhs + */ +public class RedisMessagePublishProvider implements AbMessagePublisher { + + private final RedisTemplate redisTemplate; + + public RedisMessagePublishProvider(RedisConnectionFactory redisConnectionFactory) { + redisTemplate = new RedisTemplate<>(); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setKeySerializer(RedisSerializer.string()); + redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer()); + } + + @Override + public void publish(String channel, Object body) { + redisTemplate.convertAndSend(channel, body); + } +} diff --git a/ab-component/ab-component-redis/pom.xml b/ab-component/ab-component-redis/pom.xml new file mode 100644 index 00000000..c03f7b0c --- /dev/null +++ b/ab-component/ab-component-redis/pom.xml @@ -0,0 +1,28 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-redis + + + + org.springframework.boot + spring-boot-starter-data-redis + + + com.dstz + ab-base-common + + + org.springframework + spring-context + + + diff --git a/ab-component/ab-component-redis/src/main/java/com/dstz/component/redis/cache/AbRedisCache.java b/ab-component/ab-component-redis/src/main/java/com/dstz/component/redis/cache/AbRedisCache.java new file mode 100644 index 00000000..7744ddf5 --- /dev/null +++ b/ab-component/ab-component-redis/src/main/java/com/dstz/component/redis/cache/AbRedisCache.java @@ -0,0 +1,135 @@ +package com.dstz.component.redis.cache; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.cache.CacheRegion; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.utils.CastUtils; +import org.springframework.data.redis.connection.RedisConnection; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; +import org.springframework.data.redis.serializer.JdkSerializationRedisSerializer; +import org.springframework.data.redis.serializer.RedisSerializer; +import org.springframework.util.ReflectionUtils; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; + +/** + * redis缓存实现 + * + * @author wacxhs + */ +public class AbRedisCache implements ICache { + + private final Map regionExpire; + + private final RedisTemplate redisTemplate; + + private final ConcurrentHashMap keyLocks = new ConcurrentHashMap<>(); + + public AbRedisCache(List cacheRegionList, RedisConnectionFactory redisConnectionFactory) { + this.regionExpire = cacheRegionList.stream().collect(Collectors.toMap(CacheRegion::getRegion, CacheRegion::getExpiration)); + this.redisTemplate = new RedisTemplate<>(); + redisTemplate.setEnableDefaultSerializer(false); + redisTemplate.setConnectionFactory(redisConnectionFactory); + redisTemplate.setDefaultSerializer(RedisSerializer.string()); + redisTemplate.afterPropertiesSet(); + redisTemplate.setKeySerializer(RedisSerializer.string()); + redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer()); + } + + private Duration getCacheExpire(String region) { + Duration duration = regionExpire.get(region); + Assert.notNull(duration, () -> new IllegalArgumentException(String.format("Cache Region %s is not configured", region))); + return duration; + } + + private String getCacheKey(String region, String key) { + return StrUtil.concat(true, region, StrPool.COLON, key); + } + + @Override + public V getIfPresent(String region, String key) { + String cacheKey = getCacheKey(region, key); + return CastUtils.cast(redisTemplate.opsForValue().get(cacheKey)); + } + + @Override + public V get(String region, String key, Callable loader) { + final String cacheKey = getCacheKey(region, key); + V cacheValue = getIfPresent(region, key); + if (cacheValue == null) { + synchronized (keyLocks.computeIfAbsent(cacheKey, k -> new Object())) { + cacheValue = getIfPresent(region, key); + if (cacheValue == null) { + try { + cacheValue = loader.call(); + } catch (Exception e) { + ReflectionUtils.rethrowRuntimeException(e); + } + Boolean putOk = redisTemplate.opsForValue().setIfAbsent(cacheKey, cacheValue, getCacheExpire(region)); + if (!Boolean.TRUE.equals(putOk)) { + cacheValue = getIfPresent(region, key); + } + } + } + } + return cacheValue; + } + + @Override + public void put(String region, String key, Object value) { + String cacheKey = getCacheKey(region, key); + Duration cacheExpire = getCacheExpire(region); + redisTemplate.opsForValue().set(cacheKey, value, cacheExpire); + } + + @Override + public void invalidate(String region, String... keys) { + if (keys.length == 1) { + redisTemplate.delete(getCacheKey(region, keys[0])); + } else { + redisTemplate.delete(Arrays.stream(keys).map(key -> getCacheKey(region, key)).collect(Collectors.toSet())); + } + } + + @Override + public void invalidateRegion(String region) { + ScanOptions scanOptions = ScanOptions.scanOptions().match(getCacheKey(region, "*")).build(); + try (RedisConnection connection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) { + int deleteCount = 1; + LinkedList keys = new LinkedList<>(); + for (Cursor cursor = connection.scan(scanOptions); cursor.hasNext(); deleteCount++) { + keys.add((String) redisTemplate.getKeySerializer().deserialize(cursor.next())); + if (deleteCount % 1000 == 0) { + redisTemplate.delete(keys); + keys.clear(); + } + } + if (!keys.isEmpty()) { + redisTemplate.delete(keys); + } + } + } + + @Override + public void invalidateAll() { + try (RedisConnection redisConnection = Objects.requireNonNull(redisTemplate.getConnectionFactory()).getConnection()) { + redisConnection.flushDb(); + } + } + + @Override + public boolean exists(String region, String key) { + String cacheKey = getCacheKey(region, key); + Long expire = redisTemplate.getExpire(cacheKey); + return Objects.nonNull(expire) && expire > 0L; + } +} diff --git a/ab-component/ab-component-upload-api/pom.xml b/ab-component/ab-component-upload-api/pom.xml new file mode 100644 index 00000000..7424b4a0 --- /dev/null +++ b/ab-component/ab-component-upload-api/pom.xml @@ -0,0 +1,19 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-upload-api + + + com.dstz + ab-base-common + + + \ No newline at end of file diff --git a/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/IUploader.java b/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/IUploader.java new file mode 100644 index 00000000..d3648f4c --- /dev/null +++ b/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/IUploader.java @@ -0,0 +1,57 @@ +package com.dstz.component.upload.api; + +import java.io.InputStream; + +/** + *
+ * 描述:系统的上传流接口
+ * 
+ * + * @author lightning + */ +public interface IUploader { + /** + *
+	 * 类型
+	 * 
+ * + * @return + */ + String type(); + + + /** + *
+	 * 上传流
+	 * 
+ * + * @param is + * 流 + * @param name + * 流名 + * @param type + * 文件类型 + * @return 流路径,take的时候以这个路径能获取到对应的流 + */ + String upload(InputStream is, String name,String type); + + + /** + *
+	 * 根据流路径 获取流
+	 * 
+ * + * @param path + * @return + */ + InputStream take(String path); + + /** + *
+	 * 删除流
+	 * 
+ * + * @param path + */ + void remove(String path); +} diff --git a/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/UploaderFactory.java b/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/UploaderFactory.java new file mode 100644 index 00000000..c6275c99 --- /dev/null +++ b/ab-component/ab-component-upload-api/src/main/java/com/dstz/component/upload/api/UploaderFactory.java @@ -0,0 +1,51 @@ +package com.dstz.component.upload.api; + +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.property.PropertyEnum; + +import java.util.Map; +import java.util.Map.Entry; + +/** + *
+ * 描述:上传器工厂
+ * 
+ * + * @author lightning + */ +public class UploaderFactory { + private UploaderFactory() { + + } + + /** + *
+     * 获取上传器
+     * 
+ * + * @param type + * @return + */ + public static IUploader getUploader(String type) { + Map map = SpringUtil.getBeansOfType(IUploader.class); + for (Entry entry : map.entrySet()) { + if (entry.getValue().type().equals(type)) { + return entry.getValue(); + } + } + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR.formatMessage("找不到类型[{}]的上传器的实现类", type)); + } + + /** + *
+     * 返回默认的上传器
+     * 
+ * + * @return + */ + public static IUploader getDefault() { + return getUploader(PropertyEnum.UPLOADER_DEFAULT.getYamlValue(String.class)); + } +} diff --git a/ab-component/ab-component-upload-engine/pom.xml b/ab-component/ab-component-upload-engine/pom.xml new file mode 100644 index 00000000..fc31a4d8 --- /dev/null +++ b/ab-component/ab-component-upload-engine/pom.xml @@ -0,0 +1,46 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-component-upload-engine + + + + com.dstz + ab-base-web + + + com.dstz + ab-component-upload-api + ${project.version} + + + com.aliyun.oss + aliyun-sdk-oss + 3.8.0 + provided + + + org.springframework + spring-jdbc + + + org.springframework.boot + spring-boot-autoconfigure + + + io.minio + minio + 7.1.0 + provided + + + + \ No newline at end of file diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/AliOssConfiguration.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/AliOssConfiguration.java new file mode 100644 index 00000000..9c2dfb10 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/AliOssConfiguration.java @@ -0,0 +1,23 @@ +package com.dstz.component.uploader.config; + +import com.dstz.component.uploader.oss.AliOssProperties; +import com.dstz.component.uploader.oss.AliOssUploader; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +/** + * 阿里oss配置 + * @author lightning + */ +@Configuration +@ConditionalOnClass(com.aliyun.oss.OSS.class) +@EnableConfigurationProperties(AliOssProperties.class) +public class AliOssConfiguration { + + + @Bean + public AliOssUploader aliOssUploader(){ + return new AliOssUploader(); + } +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/MinioConfiguration.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/MinioConfiguration.java new file mode 100644 index 00000000..1448a4b6 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/MinioConfiguration.java @@ -0,0 +1,40 @@ +package com.dstz.component.uploader.config; + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.component.uploader.minio.MinioProperties; +import com.dstz.component.uploader.minio.MinioTemplate; +import com.dstz.component.uploader.minio.MinioUploader; +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +/** + * minio配置 + * @author lightning + */ +@Configuration +@ConditionalOnMissingBean(MinioTemplate.class) +@EnableConfigurationProperties(MinioProperties.class) +public class MinioConfiguration { + + @Autowired + private MinioProperties minioProperties; + + @Bean + @ConditionalOnProperty(name = "uploader.default",havingValue = "minio") + public MinioTemplate minioTemplate() { + return new MinioTemplate(minioProperties.getEndpoint(),minioProperties.getAccessKey(),minioProperties.getSecretKey()); + } + + @Bean + @ConditionalOnProperty(name = "uploader.default",havingValue = "minio") + public MinioUploader minioUploader() { + return new MinioUploader(); + } + +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/OrdinaryConfiguration.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/OrdinaryConfiguration.java new file mode 100644 index 00000000..7c12f5d3 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/config/OrdinaryConfiguration.java @@ -0,0 +1,21 @@ +package com.dstz.component.uploader.config; + +import com.dstz.component.uploader.ordinary.OrdinaryProperties; +import com.dstz.component.uploader.ordinary.OrdinaryUploader; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * 本地磁盘配置 + * @author lightning + */ +@Configuration +@EnableConfigurationProperties(OrdinaryProperties.class) +public class OrdinaryConfiguration { + + @Bean + public OrdinaryUploader ordinaryUploader() { + return new OrdinaryUploader(); + } +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/constant/UploadCodes.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/constant/UploadCodes.java new file mode 100644 index 00000000..dd4e3eae --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/constant/UploadCodes.java @@ -0,0 +1,46 @@ +package com.dstz.component.uploader.constant; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * @author lightning + * @since 2023-05-04 + */ +public enum UploadCodes implements IBaseCode { + //minio + MINIO_ERROR("minio_error", "minio异常:{}"), + MINIO_BUCKET_EXISTS_ERROR("minio_bucket_exists_error", "判断minio bucket是否存在接口错误:{}"), + + MINIO_CONF_MISS("minio_conf_miss", "minio缺少配置 :{}"), + MINIO_GET_FILE_ERROR("minio_get_file_error", "minio获取文件失败:{}"), + MINIO_DEL_FILE_ERROR("minio_del_file_error", "minio移除文件失败:{}"), + CREATE_BUCKET_ERROR("create_bucket_error", "创建minio bucket接口错误:{}"), + MINIO_GET_ALL_BUCKET_FILE("minio_get_all_bucket_file", "minio 获取存储桶中的所有对象异常:{}"), + MINIO_GET_OBJ_FILE_ERROR("minio_get_obj_file_error", "minio 获取对象的元数据错误:{}"), + + MINIO_GET_OBJ_URL_ERROR("minio_get_obj_url_error", "minio 文件访问路径错误:{}"), + + + ; + + private final String code; + + private final String message; + + UploadCodes(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getMessage() { + return this.message; + } + + +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/db/DbUploader.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/db/DbUploader.java new file mode 100644 index 00000000..8525c379 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/db/DbUploader.java @@ -0,0 +1,58 @@ +package com.dstz.component.uploader.db; + +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.IdGeneratorUtils; +import com.dstz.component.upload.api.IUploader; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Service; + +import java.io.InputStream; + +/** + *
+ * 描述:数据库上传器
+ * 把流转成bytes放到数据库中
+ * 
+ * + * @author lightning + */ +@Service +public class DbUploader implements IUploader { + @Autowired + JdbcTemplate jdbcTemplate; + + @Override + public String type() { + return "db"; + } + + @Override + public String upload(InputStream is, String name,String type) { + try { + final String id = IdGeneratorUtils.nextId(); + jdbcTemplate.update("INSERT INTO db_uploader VALUES (?,?)", ps -> { + ps.setString(1, id); + ps.setBinaryStream(2, is); + }); + return id; + } catch (Exception e) { + if (e.getCause().toString().contains("You can change this value on the server by setting the 'max_allowed_packet' variable")) { + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR.formatMessage("附件内容过大(数据库级别)")); + } + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR.formatDefaultMessage(e)); + } + } + + @Override + public InputStream take(String path) { + return jdbcTemplate.queryForObject("SELECT bytes_ FROM db_uploader WHERE id_ = ?", (rs, rowNum) -> rs.getBinaryStream("bytes_"), path); + } + + @Override + public void remove(String path) { + jdbcTemplate.update("delete from db_uploader where id_ = ?", path); + } + +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioProperties.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioProperties.java new file mode 100644 index 00000000..7f2c8f7e --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioProperties.java @@ -0,0 +1,56 @@ +package com.dstz.component.uploader.minio; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + *
+ * minio属性配置
+ * 
+ * + * @author lightning + */ +@ConfigurationProperties(prefix = "ab.minio") +public class MinioProperties { + + private String endpoint; + + + private String accessKey; + + + private String secretKey; + + private String bucketName; + + public String getBucketName() { + return bucketName; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public String getEndpoint() { + return endpoint; + } + + public void setEndpoint(String endpoint) { + this.endpoint = endpoint; + } + + public String getAccessKey() { + return accessKey; + } + + public void setAccessKey(String accessKey) { + this.accessKey = accessKey; + } + + public String getSecretKey() { + return secretKey; + } + + public void setSecretKey(String secretKey) { + this.secretKey = secretKey; + } +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioTemplate.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioTemplate.java new file mode 100644 index 00000000..42dddce2 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioTemplate.java @@ -0,0 +1,219 @@ +package com.dstz.component.uploader.minio; + + +import cn.hutool.core.date.DateUtil; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.component.uploader.constant.UploadCodes; +import io.minio.*; +import io.minio.errors.InvalidEndpointException; +import io.minio.errors.InvalidPortException; +import io.minio.messages.DeleteObject; +import io.minio.messages.Item; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.InputStream; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; + +import static com.dstz.component.uploader.constant.UploadCodes.*; + +/** + *
+ * minio模板
+ * 
+ * + * @author lightning + */ +public class MinioTemplate { + + + private MinioClient client; + + private String endpoint, accessKey, secretKey; + + public MinioTemplate(String endpoint, String accessKey, String secretKey) { + this.endpoint = endpoint; + this.accessKey = accessKey; + this.secretKey = secretKey; + if (client == null) { + try { + client = getMinioClient(); + } catch (InvalidPortException e) { + e.getStackTrace(); + } catch (InvalidEndpointException e) { + e.getStackTrace(); + } + } + } + + public MinioClient getMinioClient() throws InvalidPortException, InvalidEndpointException { + return MinioClient.builder().endpoint(endpoint).credentials(accessKey, secretKey).build(); + } + + + /** + * 判断 bucket是否存在 + * + * @param bucketName 桶 + * @return + */ + + public boolean bucketExists(String bucketName) { + + try { + return client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + } catch (Exception e) { + throw new ApiException(UploadCodes.MINIO_BUCKET_EXISTS_ERROR.formatDefaultMessage(e.getMessage()), e); + } + + } + + + /** + * 创建 bucket + * + * @param bucketName 桶 + */ + + public void makeBucket(String bucketName) { + + try { + boolean isExist = client.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (!isExist) { + client.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + throw new ApiException(CREATE_BUCKET_ERROR.formatDefaultMessage(e.toString()), e); + } + + } + + + /** + * 文件上传 + * + * @param bucketName 桶 + * @param objectName 文件名 + * @param stream 文件流 + */ + + public void putObject(String bucketName, String objectName, InputStream stream) { + try { + client.putObject(PutObjectArgs.builder().bucket(bucketName).object(objectName).stream(stream, stream.available(), -1).build()); + } catch (Exception e) { + throw new ApiException(MINIO_ERROR.formatDefaultMessage(e.toString(), e)); + } + + } + + + /** + * 删除文件 + * + * @param bucketName 桶 + * @param objectName 文件名 + * @return + */ + + public void removeObject(String bucketName, String objectName) { + + try { + client.removeObject(RemoveObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } catch (Exception e) { + throw new ApiException(MINIO_DEL_FILE_ERROR.formatDefaultMessage(e.toString()), e); + } + + } + + + /** + * 列出存储桶中的所有对象 + * + * @param bucketName 桶 存储桶名称 + * @return 桶中所有对象 + */ + public Iterable> listObjects(String bucketName) { + try { + boolean flag = bucketExists(bucketName); + if (flag) { + return client.listObjects(ListObjectsArgs.builder().bucket(bucketName).build()); + } + } catch (Exception e) { + throw new ApiException(MINIO_GET_ALL_BUCKET_FILE.formatDefaultMessage(e.toString()), e); + } + return null; + + } + + /** + * 以流的形式获取一个文件对象 + * + * @param bucketName 桶 存储桶名称 + * @param objectName 文件名 存储桶里的对象名称 + * @return 文件流 + */ + + public InputStream getObject(String bucketName, String objectName) { + boolean flag = bucketExists(bucketName); + if (flag) { + ObjectStat statObject = statObject(bucketName, objectName); + if (statObject != null && statObject.length() > 0) { + InputStream stream = null; + try { + //stream = client.getObject(bucketName, objectName); + stream = client.getObject(GetObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } catch (Exception e) { + throw new ApiException(MINIO_GET_FILE_ERROR.formatDefaultMessage(e.toString()), e); + } + return stream; + } + } + return null; + } + + /** + * 获取对象的元数据 + * + * @param bucketName 桶 存储桶名称 + * @param objectName 文件名 存储桶里的对象名称 + * @return 元数据 + */ + public ObjectStat statObject(String bucketName, String objectName) { + boolean flag = bucketExists(bucketName); + if (flag) { + ObjectStat statObject = null; + try { + statObject = client.statObject(StatObjectArgs.builder().bucket(bucketName).object(objectName).build()); + } catch (Exception e) { + throw new ApiException(MINIO_GET_OBJ_FILE_ERROR.formatDefaultMessage(e.toString()), e); + } + return statObject; + } + return null; + } + + /** + * 文件访问路径 + * + * @param bucketName 桶 存储桶名称 + * @param objectName 文件名 存储桶里的对象名称 + * @return 文件url + */ + public String getObjectUrl(String bucketName, String objectName) { + boolean flag = bucketExists(bucketName); + String url = ""; + if (flag) { + try { + url = client.getObjectUrl(bucketName, objectName); + } catch (Exception e) { + throw new ApiException(MINIO_GET_OBJ_URL_ERROR.formatDefaultMessage(e.toString()), e); + } + } + return url; + } + +} \ No newline at end of file diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioUploader.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioUploader.java new file mode 100644 index 00000000..0be24206 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/minio/MinioUploader.java @@ -0,0 +1,82 @@ +package com.dstz.component.uploader.minio; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.component.upload.api.IUploader; +import com.dstz.component.uploader.constant.UploadCodes; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.InputStream; +import java.util.Date; + +import static com.dstz.component.uploader.constant.UploadCodes.*; + +/** + *
+ * minio实现
+ * 
+ * + * @author lightning + */ +public class MinioUploader implements IUploader { + + @Autowired + private MinioTemplate minIoTemplate; + + @Autowired + private MinioProperties minioProperties; + + @Override + public String type() { + return "minio"; + } + + @Override + public String upload(InputStream is, String name, String type) { + String id = IdUtil.randomUUID(); + try { + String bucketName = minioProperties.getBucketName(); + if (StrUtil.isEmpty(bucketName)) { + throw new BusinessException(MINIO_CONF_MISS.formatDefaultMessage("ab.minio.bucketName")); + } + //判断桶是否存在 + if (!minIoTemplate.bucketExists(bucketName)) { + minIoTemplate.makeBucket(bucketName); + } + String pathStr = getPath(name, type); + minIoTemplate.putObject(bucketName, pathStr, is); + return pathStr; + } catch (Exception e) { + throw new BusinessException(UploadCodes.MINIO_ERROR.formatDefaultMessage(e.getMessage()), e); + } + } + + + @Override + public InputStream take(String path) { + try { + return minIoTemplate.getObject(minioProperties.getBucketName(), path); + } catch (Exception e) { + throw new BusinessException(MINIO_GET_FILE_ERROR.formatDefaultMessage(e.getMessage()), e); + } + } + + @Override + public void remove(String path) { + try { + minIoTemplate.removeObject(minioProperties.getBucketName(), path); + } catch (Exception e) { + throw new BusinessException(MINIO_DEL_FILE_ERROR.formatDefaultMessage(e.getMessage()), e); + } + } + + private String getPath(String name, String type) { + return type + StrPool.SLASH + DateUtil.format(new Date(), "yyyyMMdd") + StrPool.SLASH + name; + } + +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryProperties.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryProperties.java new file mode 100644 index 00000000..d9cf5845 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryProperties.java @@ -0,0 +1,20 @@ +package com.dstz.component.uploader.ordinary; + +import org.springframework.boot.context.properties.ConfigurationProperties; +/** + * Ordinary 属性配置 + * + * @author lightning + */ +@ConfigurationProperties(prefix = "uploader.ordinary") +public class OrdinaryProperties { + private String path; + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryUploader.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryUploader.java new file mode 100644 index 00000000..3cce8e74 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/ordinary/OrdinaryUploader.java @@ -0,0 +1,59 @@ +package com.dstz.component.uploader.ordinary; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.io.FileUtil; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.component.upload.api.IUploader; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.File; +import java.io.FileInputStream; +import java.io.InputStream; +import java.util.Date; + +/** + *
+ * 描述:普通的上传器
+ * 上传到服务器的某个文件夹中
+ * 每次上传时会自动放在当前日期yyyyMMdd的目录下
+ * 
+ * + * @author lightning + */ +public class OrdinaryUploader implements IUploader { + + @Override + public String type() { + return "ordinary"; + } + + @Autowired + private OrdinaryProperties ordinaryProperties; + + + @Override + public String upload(InputStream is, String name, String type) { + FileUtil.writeFromStream(is, getPath(name, type)); + return getPath(name, type); + } + + @Override + public InputStream take(String path) { + try { + return new FileInputStream(new File(path)); + } catch (Exception e) { + throw new BusinessException(GlobalApiCodes.INTERNAL_ERROR.formatDefaultMessage(e)); + } + } + + @Override + public void remove(String path) { + FileUtil.del(path); + } + + private String getPath(String name, String type) { + return ordinaryProperties.getPath() + File.separator + type + File.separator + DateUtil.format(new Date(), "yyyyMMdd") + File.separator + name; + } + +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssProperties.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssProperties.java new file mode 100644 index 00000000..143a7b87 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssProperties.java @@ -0,0 +1,45 @@ +package com.dstz.component.uploader.oss; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * oss 属性配置 + * + * @author lightning + */ +@ConfigurationProperties(prefix = "spring.cloud.alicloud.oss") +public class AliOssProperties { + + private String endpoint; + + private String accessKeyId; + + private String accessKeySecret; + + /** + * bucket name + */ + + private String bucketName; + + /** + * base dir + */ + private String baseDir; + + public String getBucketName() { + return bucketName; + } + + public String getBaseDir() { + return baseDir; + } + + public void setBucketName(String bucketName) { + this.bucketName = bucketName; + } + + public void setBaseDir(String baseDir) { + this.baseDir = baseDir; + } +} diff --git a/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssUploader.java b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssUploader.java new file mode 100644 index 00000000..77006167 --- /dev/null +++ b/ab-component/ab-component-upload-engine/src/main/java/com/dstz/component/uploader/oss/AliOssUploader.java @@ -0,0 +1,57 @@ +package com.dstz.component.uploader.oss; + +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import com.aliyun.oss.OSS; +import com.aliyun.oss.model.PutObjectResult; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.component.upload.api.IUploader; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; + +import java.io.InputStream; +import java.util.Date; + +/** + * alibaba oss 上传实现 + * + * @author lightning + */ +public class AliOssUploader implements IUploader { + + private static final Logger logger = LoggerFactory.getLogger(AliOssUploader.class); + + @Autowired + private OSS ossClient; + + @Autowired + private AliOssProperties aliOssProperties; + + @Override + public String type() { + return "oss"; + } + + @Override + public String upload(InputStream inputStream, String s, String type) { + final String key = StrUtil.join("/", new String[]{aliOssProperties.getBaseDir(), type, DateUtil.format(new Date(), "yyyyMMdd"), s}); + logger.debug("aliyun oss upload file {}", key); + PutObjectResult result = ossClient.putObject(aliOssProperties.getBucketName(), key, inputStream); + if (logger.isDebugEnabled()) { + logger.debug("aliyun oss upload response {}", JsonUtils.toJSONString(result)); + } + return key; + } + + @Override + public InputStream take(String s) { + logger.debug("aliyun oss get file content {}", s); + return ossClient.getObject(aliOssProperties.getBucketName(), s).getObjectContent(); + } + + @Override + public void remove(String s) { + ossClient.deleteObject(aliOssProperties.getBucketName(), s); + } +} diff --git a/ab-component/ab-groovy-script-api/pom.xml b/ab-component/ab-groovy-script-api/pom.xml new file mode 100644 index 00000000..f3581374 --- /dev/null +++ b/ab-component/ab-groovy-script-api/pom.xml @@ -0,0 +1,14 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-groovy-script-api + + \ No newline at end of file diff --git a/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/GroovyEngineEvaluateException.java b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/GroovyEngineEvaluateException.java new file mode 100644 index 00000000..8953ffba --- /dev/null +++ b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/GroovyEngineEvaluateException.java @@ -0,0 +1,36 @@ +package com.dstz.groovy.script.api; + +/** + * groovy脚本运行异常 + * + * @author wacxhs + * @since 2022-01-26 + */ +public class GroovyEngineEvaluateException extends RuntimeException { + + private static final long serialVersionUID = 1704863836362954551L; + + /** + * 执行脚本 + */ + private final String script; + + public GroovyEngineEvaluateException(String message, String script) { + super(message); + this.script = script; + } + + public GroovyEngineEvaluateException(String message, Throwable cause, String script) { + super(message, cause); + this.script = script; + } + + /** + * 运行脚本 + * + * @return 脚本 + */ + public String getScript() { + return script; + } +} diff --git a/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IGroovyScriptEngine.java b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IGroovyScriptEngine.java new file mode 100644 index 00000000..7219b3d4 --- /dev/null +++ b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IGroovyScriptEngine.java @@ -0,0 +1,20 @@ +package com.dstz.groovy.script.api; + +import java.util.Map; + +/** + * Groovy脚本引擎 + * + * @author wacxhs + * @since 2022-01-26 + */ +public interface IGroovyScriptEngine { + /** + * 运行脚本 + * + * @param script 脚本 + * @param vars 变量 + * @return 运行结果 + */ + T evaluate(String script, Map vars); +} diff --git a/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IScript.java b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IScript.java new file mode 100644 index 00000000..857109e7 --- /dev/null +++ b/ab-component/ab-groovy-script-api/src/main/java/com/dstz/groovy/script/api/IScript.java @@ -0,0 +1,12 @@ +package com.dstz.groovy.script.api; + +/** + * 脚本接口。
+ *

+ * 实现了该接口的spring bean 将会被注入到脚本执行引擎中 + * + * @author jeff + * @since 2022-01-26 + */ +public interface IScript { +} diff --git a/ab-component/ab-groovy-script-engine/pom.xml b/ab-component/ab-groovy-script-engine/pom.xml new file mode 100644 index 00000000..040e7f3c --- /dev/null +++ b/ab-component/ab-groovy-script-engine/pom.xml @@ -0,0 +1,49 @@ + + + + ab-component + com.dstz + 2.5.0 + + 4.0.0 + + ab-groovy-script-engine + + + + com.dstz + ab-groovy-script-api + ${project.version} + + + org.slf4j + slf4j-api + + + com.google.guava + guava + + + org.codehaus.groovy + groovy + + + cn.hutool + hutool-core + + + org.springframework + spring-context + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + + + \ No newline at end of file diff --git a/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/AbGroovyShell.java b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/AbGroovyShell.java new file mode 100644 index 00000000..9a8296b4 --- /dev/null +++ b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/AbGroovyShell.java @@ -0,0 +1,45 @@ +package com.dstz.groovy.script.engine; + +import cn.hutool.core.util.ReflectUtil; +import groovy.lang.Binding; +import groovy.lang.GroovyShell; +import groovy.lang.Script; +import org.codehaus.groovy.control.CompilationFailedException; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +class AbGroovyShell extends GroovyShell { + + private static final Logger logger = LoggerFactory.getLogger(AbGroovyShell.class); + + private Script script; + + private volatile boolean parsed; + + public AbGroovyShell(Binding binding) { + super(binding); + } + + @Override + public Object evaluate(String scriptText) throws CompilationFailedException { + if (!parsed) { + synchronized (this) { + if (!parsed) { + script = parse(scriptText); + parsed = true; + } + } + } + return script.run(); + } + + /** + * help gc + */ + public void helpGc() { + logger.debug("call help gc"); + script = null; + resetLoadedClasses(); + ReflectUtil.setFieldValue(this, "loader", null); + } +} diff --git a/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyBinding.java b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyBinding.java new file mode 100644 index 00000000..20644d36 --- /dev/null +++ b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyBinding.java @@ -0,0 +1,77 @@ +package com.dstz.groovy.script.engine; + +import groovy.lang.Binding; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Groovy 绑定 + * + * @author ray + */ +public class GroovyBinding extends Binding { + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private static final ThreadLocal> LOCAL_VARS = new ThreadLocal<>(); + + private static final Map PROPERTY_MAP = new HashMap<>(); + + public void setThreadVariables(Map variables) { + LOCAL_VARS.remove(); + LOCAL_VARS.set(variables); + } + + @Override + public Object getVariable(String name) { + Map localVars = LOCAL_VARS.get(); + Object result = null; + if (localVars != null) { + result = localVars.get(name); + } + if (result == null) { + result = PROPERTY_MAP.get(name); + } + if (result == null) { + logger.warn("执行Groovy 语句时,Context 缺少 Variable :{}", name); + } + return result; + } + + @Override + public void setVariable(String name, Object value) { + if (LOCAL_VARS.get() == null) { + Map vars = new LinkedHashMap<>(); + vars.put(name, value); + LOCAL_VARS.set(vars); + } else { + LOCAL_VARS.get().put(name, value); + } + } + + @Override + @SuppressWarnings("rawtypes") + public Map getVariables() { + if (LOCAL_VARS.get() == null) { + return new LinkedHashMap(); + } + return LOCAL_VARS.get(); + } + + public void clearVariables() { + LOCAL_VARS.remove(); + } + + @Override + public Object getProperty(String property) { + return PROPERTY_MAP.get(property); + } + + @Override + public void setProperty(String property, Object newValue) { + PROPERTY_MAP.put(property, newValue); + } +} diff --git a/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyScriptEngine.java b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyScriptEngine.java new file mode 100644 index 00000000..36007033 --- /dev/null +++ b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/GroovyScriptEngine.java @@ -0,0 +1,92 @@ +package com.dstz.groovy.script.engine; + +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.text.escape.Html4Unescape; +import cn.hutool.core.text.replacer.StrReplacer; +import cn.hutool.core.util.ObjectUtil; +import com.dstz.groovy.script.api.GroovyEngineEvaluateException; +import com.dstz.groovy.script.api.IGroovyScriptEngine; +import com.dstz.groovy.script.api.IScript; +import com.dstz.groovy.script.engine.configure.AbGroovyScriptProperties; +import com.google.common.cache.Cache; +import com.google.common.cache.CacheBuilder; +import com.google.common.cache.RemovalListener; +import groovy.lang.GroovyShell; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.context.ApplicationListener; +import org.springframework.context.event.ContextRefreshedEvent; +import org.springframework.util.DigestUtils; + +import java.nio.charset.StandardCharsets; +import java.util.Map; +import java.util.Optional; +import java.util.concurrent.ExecutionException; + +/** + * 脚本引擎用于执行groovy脚本。
+ * 实现了IScript接口的类。 可以在脚本中使用。 + * + * @author jeff + * @since 2022-01-26 + */ +public class GroovyScriptEngine implements IGroovyScriptEngine, ApplicationListener, InitializingBean { + + private final Logger logger = LoggerFactory.getLogger(this.getClass()); + + private final GroovyBinding groovyBinding = new GroovyBinding(); + + private final StrReplacer stringEscape = new Html4Unescape(); + + private Cache groovyShellCache; + + private final AbGroovyScriptProperties abGroovyScriptProperties; + + public GroovyScriptEngine(AbGroovyScriptProperties abGroovyScriptProperties) { + this.abGroovyScriptProperties = abGroovyScriptProperties; + } + + @Override + public void afterPropertiesSet() { + groovyShellCache = CacheBuilder.newBuilder() + .maximumSize(abGroovyScriptProperties.getCompileCache()) + .removalListener((RemovalListener) event -> Optional.ofNullable(event.getValue()).ifPresent(AbGroovyShell::helpGc)) + .build(); + } + + + @SuppressWarnings("unchecked") + @Override + public T evaluate(String script, Map vars) { + if (CharSequenceUtil.isBlank(script)) { + return null; + } + script = stringEscape.replace(script).toString(); + if (logger.isDebugEnabled()) { + logger.debug("执行:{}", script); + logger.debug("variables:{}", ObjectUtil.toString(vars)); + } + try { + groovyBinding.setThreadVariables(vars); + GroovyShell shell = groovyShellCache.get(DigestUtils.md5DigestAsHex(script.getBytes(StandardCharsets.UTF_8)), () -> new AbGroovyShell(groovyBinding)); + T result = (T)shell.evaluate(script); + if (logger.isDebugEnabled()) { + logger.debug("result:{}", result); + } + return result; + } catch (ExecutionException ex) { + throw new GroovyEngineEvaluateException(ex.getMessage(), ex, script); + }finally { + groovyBinding.clearVariables(); + } + } + + @Override + public void onApplicationEvent(ContextRefreshedEvent event) { + if (event.getApplicationContext().getParent() == null) { + // 加载IScript实例 + event.getApplicationContext().getBeansOfType(IScript.class).forEach(groovyBinding::setProperty); + } + } +} diff --git a/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptConfigure.java b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptConfigure.java new file mode 100644 index 00000000..671d880b --- /dev/null +++ b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptConfigure.java @@ -0,0 +1,28 @@ +package com.dstz.groovy.script.engine.configure; + +import com.dstz.groovy.script.engine.GroovyScriptEngine; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * ab groovy script configuration + * + * @author wachxs + * @since 2022-01-25 + */ +@EnableConfigurationProperties(AbGroovyScriptProperties.class) +@Configuration +public class AbGroovyScriptConfigure { + + private final AbGroovyScriptProperties abGroovyScriptProperties; + + public AbGroovyScriptConfigure(AbGroovyScriptProperties abGroovyScriptProperties) { + this.abGroovyScriptProperties = abGroovyScriptProperties; + } + + @Bean + public GroovyScriptEngine groovyScriptEngine() { + return new GroovyScriptEngine(abGroovyScriptProperties); + } +} diff --git a/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptProperties.java b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptProperties.java new file mode 100644 index 00000000..a98dab65 --- /dev/null +++ b/ab-component/ab-groovy-script-engine/src/main/java/com/dstz/groovy/script/engine/configure/AbGroovyScriptProperties.java @@ -0,0 +1,25 @@ +package com.dstz.groovy.script.engine.configure; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * groovy 脚本属性配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.groovy") +public class AbGroovyScriptProperties { + + /** + * 编译缓存 + */ + private Integer compileCache = 20; + + public Integer getCompileCache() { + return compileCache; + } + + public void setCompileCache(Integer compileCache) { + this.compileCache = compileCache; + } +} diff --git a/ab-component/pom.xml b/ab-component/pom.xml new file mode 100644 index 00000000..748da311 --- /dev/null +++ b/ab-component/pom.xml @@ -0,0 +1,42 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + ab-component + pom + + + ab-component-j2cache + ab-component-redis + ab-groovy-script-api + ab-groovy-script-engine + ab-component-upload-api + ab-component-upload-engine + ab-component-mq-api + ab-component-mq-engine + ab-component-pubsub-api + ab-component-pubsub-core + ab-component-msg-api + ab-component-msg-engine + + + + 2.6.1 + + + + + + org.springframework.data + spring-data-redis + ${spring-data-redis} + + + + diff --git a/ab-demo/ab-demo-api/pom.xml b/ab-demo/ab-demo-api/pom.xml new file mode 100644 index 00000000..b85acd17 --- /dev/null +++ b/ab-demo/ab-demo-api/pom.xml @@ -0,0 +1,20 @@ + + + + ab-demo + com.dstz + 2.5.0 + + 4.0.0 + + ab-demo-api + + + + com.dstz + ab-base-api + + + \ No newline at end of file diff --git a/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/DemoApi.java b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/DemoApi.java new file mode 100644 index 00000000..e01494cf --- /dev/null +++ b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/DemoApi.java @@ -0,0 +1,11 @@ +package com.dstz.demo.api; + + +/** + * 对外服务接口的定义,模块与模块间依赖服务接口 + * @author Jeff + * + */ +public interface DemoApi { + +} diff --git a/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/constant/package-info.java b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/constant/package-info.java new file mode 100644 index 00000000..161c9ea2 --- /dev/null +++ b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/constant/package-info.java @@ -0,0 +1,18 @@ +/** + * Project Name:demo + * File Name:package-info.java + * Package Name:com.dstz.demo.api + * Date:2022年2月16日 + * Copyright (c) 2022, agilebpm.cn All Rights Reserved. + * + */ +/** + * ClassName: package-info
+ * Function: 模块枚举常量定义
+ * date: 2018年8月24日18:11:58
+ * + * @author jeff + * @version + * @since JDK 17 + */ +package com.dstz.demo.api.constant; \ No newline at end of file diff --git a/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/dto/package-info.java b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/dto/package-info.java new file mode 100644 index 00000000..91200ba6 --- /dev/null +++ b/ab-demo/ab-demo-api/src/mian/java/com/dstz/demo/api/dto/package-info.java @@ -0,0 +1,18 @@ +/** + * Project Name:demo + * File Name:package-info.java + * Package Name:com.dstz.demo.api + * Date:2022年2月16日 + * Copyright (c) 2022, agilebpm.cn All Rights Reserved. + * + */ +/** + * ClassName: package-info
+ * Function: DTO 传输类型的模型,也可以是demo模块接口、实体的定义
+ * date: 2022年2月16日
+ * + * @author jeff + * @version + * @since JDK 17 + */ +package com.dstz.demo.api.dto; \ No newline at end of file diff --git a/ab-demo/ab-demo-core/pom.xml b/ab-demo/ab-demo-core/pom.xml new file mode 100644 index 00000000..768552cd --- /dev/null +++ b/ab-demo/ab-demo-core/pom.xml @@ -0,0 +1,43 @@ + + + + ab-demo + com.dstz + 2.5.0 + + 4.0.0 + + ab-demo-core + + + + com.dstz + ab-demo-api + ${project.version} + + + com.dstz + ab-base-common + + + com.dstz + ab-base-mapper + + + com.dstz + ab-base-web + + + org.springframework + spring-context + + + + com.dstz + ab-wf-client + ${project.version} + + + \ No newline at end of file diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/api/DemoApiImpl.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/api/DemoApiImpl.java new file mode 100644 index 00000000..56bb01ed --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/api/DemoApiImpl.java @@ -0,0 +1,9 @@ +package com.dstz.demo.api; +/** + * 实现对外部模块的接口, implements DemoApi + * 服务接口类似Controller,不能存在事物,仅做数据转换,校验等。逻辑需要下沉到 Mananger 层 + * @author Jeff + */ +public class DemoApiImpl { + +} diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/entity/BizApplyOrder.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/entity/BizApplyOrder.java new file mode 100644 index 00000000..903e15cd --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/entity/BizApplyOrder.java @@ -0,0 +1,142 @@ +package com.dstz.demo.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 订单信息 + *

+ * + * @author wacxhs + * @since 2022-01-20 + */ +@TableName("biz_apply_order") +public class BizApplyOrder extends AbModel { + + private static final long serialVersionUID = 4701340819462582895L; + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 订单编号 + */ + @TableField("apply_no_") + private String applyNo; + + /** + * 渠道经理 + */ + @TableField("qdjl") + private String qdjl; + + /** + * 渠道经理ID + */ + @TableField("qdjl_id_") + private String qdjlId; + + /** + * 组织ID + */ + @TableField("org_id_") + private String orgId; + + /** + * 组织 + */ + @TableField("org_name_") + private String orgName; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 备注 + */ + @TableField("remark") + private String remark; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getApplyNo() { + return applyNo; + } + + public void setApplyNo(String applyNo) { + this.applyNo = applyNo; + } + + public String getQdjl() { + return qdjl; + } + + public void setQdjl(String qdjl) { + this.qdjl = qdjl; + } + + public String getQdjlId() { + return qdjlId; + } + + public void setQdjlId(String qdjlId) { + this.qdjlId = qdjlId; + } + + public String getOrgId() { + return orgId; + } + + public void setOrgId(String orgId) { + this.orgId = orgId; + } + + public String getOrgName() { + return orgName; + } + + public void setOrgName(String orgName) { + this.orgName = orgName; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/BizApplyOrderManager.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/BizApplyOrderManager.java new file mode 100644 index 00000000..a8b0dc79 --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/BizApplyOrderManager.java @@ -0,0 +1,15 @@ +package com.dstz.demo.core.manager; + +import com.dstz.demo.core.entity.BizApplyOrder; +import com.dstz.base.manager.AbBaseManager; + +/** + *

+ * 订单信息 通用业务类 + *

+ * + * @author wacxhs + * @since 2022-01-20 + */ +public interface BizApplyOrderManager extends AbBaseManager { +} diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/impl/BizApplyOrderManagerImpl.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/impl/BizApplyOrderManagerImpl.java new file mode 100644 index 00000000..e007b5aa --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/manager/impl/BizApplyOrderManagerImpl.java @@ -0,0 +1,17 @@ +package com.dstz.demo.core.manager.impl; + +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.demo.core.entity.BizApplyOrder; +import com.dstz.demo.core.manager.BizApplyOrderManager; +import org.springframework.stereotype.Service; + +/** + * 订单信息 通用服务实现类 + * + * @author wacxhs + * @since 2022-01-20 + */ +@Service("bizApplyOrderManager") +public class BizApplyOrderManagerImpl extends AbBaseManagerImpl implements BizApplyOrderManager { + +} diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/mapper/BizApplyOrderMapper.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/mapper/BizApplyOrderMapper.java new file mode 100644 index 00000000..c222b53f --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/mapper/BizApplyOrderMapper.java @@ -0,0 +1,18 @@ +package com.dstz.demo.core.mapper; + +import com.dstz.demo.core.entity.BizApplyOrder; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 订单信息 Mapper 接口 + *

+ * + * @author wacxhs + * @since 2022-01-20 + */ +@Mapper +public interface BizApplyOrderMapper extends AbBaseMapper { + +} diff --git a/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/rest/BizApplyOrderController.java b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/rest/BizApplyOrderController.java new file mode 100644 index 00000000..5a912fef --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/java/com/dstz/demo/core/rest/BizApplyOrderController.java @@ -0,0 +1,106 @@ +package com.dstz.demo.core.rest; + +import java.util.HashMap; +import java.util.Map; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.utils.IdGeneratorUtils; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.bpm.api.dto.invoke.BpmUrlFormSaveDTO; +import com.dstz.bpm.api.vo.BpmUrlFormResponseDTO; +import com.dstz.demo.core.entity.BizApplyOrder; +import com.dstz.demo.core.manager.BizApplyOrderManager; + +import cn.hutool.core.util.StrUtil; + +@RestController + +@RequestMapping(AbAppRestConstant.DEMO_SERVICE_PREFIX + "/applyOrder") +public class BizApplyOrderController extends AbCrudController { + private static final Logger LOGGER = LoggerFactory.getLogger(AbCrudController.class); + @Autowired + private BizApplyOrderManager orderMananger; + + + /** + * Url 表单处理器实例 + * @param urlFormSaveDto + * @return + * @throws Exception + */ + @PostMapping("formHandler") + public ApiResponse formHandler(@RequestBody BpmUrlFormSaveDTO urlFormSaveDto) throws Exception { + LOGGER.debug("DEMO 表单处理器执行保存动作===》{}", JsonUtils.toJSONString(urlFormSaveDto.getBizData())); + + BizApplyOrder order = JsonUtils.parseObject(JsonUtils.toJSONNode(urlFormSaveDto.getBizData()), BizApplyOrder.class) ; + + BpmUrlFormResponseDTO bpmUrlFormResponseDTO = new BpmUrlFormResponseDTO(); + + // 第一次保存,构建业务主键关联 + if (StrUtil.isEmpty(order.getId())) { + String id = IdGeneratorUtils.nextId(); + // 这里必须返回 业务主键 + bpmUrlFormResponseDTO.setBizId(id); + order.setId(id); + orderMananger.create(order); + + Map hashMap = new HashMap<>(); + hashMap.put("startVariable", order.getQdjl()); + // 启动时候设置一些流程变量,请看 act_ru_variable 表、在整个流程声明周期您都可以使用该流程变量,可以用于分支判断等等 + bpmUrlFormResponseDTO.setVariables(hashMap); + } else { + // 更新情况 + orderMananger.update(order); + // 变量设置可忽略 + Map hashMap = new HashMap<>(); + hashMap.put("doTaskVariable", order.getQdjl() + "-" + urlFormSaveDto.getNodeKey()); + bpmUrlFormResponseDTO.setVariables(hashMap); + } + + return ApiResponse.success(bpmUrlFormResponseDTO); + } + + + + + + + + + + + + + + + + + + @Override + protected String getEntityDesc() { + return "demoController"; + } + + + + + + + + + + + + + +} \ No newline at end of file diff --git a/ab-demo/ab-demo-core/src/main/resources/com/dstz/demo/core/mapper/BizApplyOrderMapper.xml b/ab-demo/ab-demo-core/src/main/resources/com/dstz/demo/core/mapper/BizApplyOrderMapper.xml new file mode 100644 index 00000000..10563faa --- /dev/null +++ b/ab-demo/ab-demo-core/src/main/resources/com/dstz/demo/core/mapper/BizApplyOrderMapper.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/ab-demo/pom.xml b/ab-demo/pom.xml new file mode 100644 index 00000000..3e5e7559 --- /dev/null +++ b/ab-demo/pom.xml @@ -0,0 +1,19 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-demo + pom + + + ab-demo-api + ab-demo-core + + \ No newline at end of file diff --git a/ab-org/ab-org-api/pom.xml b/ab-org/ab-org-api/pom.xml new file mode 100644 index 00000000..75e1a13e --- /dev/null +++ b/ab-org/ab-org-api/pom.xml @@ -0,0 +1,39 @@ + + + + ab-org + com.dstz + 2.5.0 + + 4.0.0 + ab-org-api + + + + com.dstz + ab-base-api + + + com.fasterxml.jackson.core + jackson-annotations + + + + + + org.apache.maven.plugins + maven-source-plugin + + + attach-sources + + jar-no-fork + + + + + + + diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupApi.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupApi.java new file mode 100644 index 00000000..374ee82a --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupApi.java @@ -0,0 +1,88 @@ +package com.dstz.org.api; + +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 组接口 + * + * @author wacxhs + */ +public interface GroupApi { + + /** + * 根据组类型和组ID获取组记录 + * + * @param groupType 组类型 + * @param groupId 组ID集 + * @return 组记录 + */ + default IGroup getByGroupId(String groupType, String groupId) { + Iterator iterator = getByGroupIds(groupType, Collections.singleton(groupId)); + return Objects.nonNull(iterator) && iterator.hasNext() ? iterator.next() : null; + } + + /** + * 根据组类型和组ID获取组记录 + * + * @param groupType 组类型 + * @param groupIds 组ID集 + * @return 组记录 + */ + Iterator getByGroupIds(String groupType, Collection groupIds); + + /** + * 根据组类型和组编码获取组 + * + * @param groupType 组类型 + * @param groupCode 组编码 + * @return 组记录 + */ + default IGroup getByGroupCode(String groupType, String groupCode) { + Iterator iterator = getByGroupCodes(groupType, Collections.singleton(groupCode)); + return Objects.nonNull(iterator) && iterator.hasNext() ? iterator.next() : null; + } + + /** + * 通过组类型和组编码获取组记录 + * + * @param groupType 组类型 + * @param groupCodes 组编码 + * @return 组记录 + */ + Iterator getByGroupCodes(String groupType, Collection groupCodes); + + /** + * 根据用户ID获取所关联组 + * + * @param userId 用户ID + * @return 关联组 + */ + default List getByUserId(String userId) { + return Arrays.stream(GroupType.values()).map(o -> getByGroupTypeAndUserId(o.getType(), userId)).flatMap(Collection::stream).collect(Collectors.toList()); + } + + /** + * 根据组类型和用户ID获取组记录列表 + * + * @param groupType 组类型 + * @param userId 用户编号 + * @return 组记录 + */ + List getByGroupTypeAndUserId(String groupType, String userId); + + /** + * 组记录查询过滤 + * + * @param groupType 组类型 + * @param queryParamDTO 过滤参数 + * @return 组记录 + */ + default List queryFilter(String groupType, QueryParamDTO queryParamDTO) { + return null; + } +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupRelationApi.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupRelationApi.java new file mode 100644 index 00000000..ed19d022 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/GroupRelationApi.java @@ -0,0 +1,73 @@ +package com.dstz.org.api; + +import com.dstz.org.api.model.IGroup; + +import java.util.List; + +/** + * @author Jeff + * @描述 组关系服务,比如获取指定部门岗位,指定部门上级岗位 + * @可不实现 这些组织并非ORG 通用的API规范,可实现可不实现, + * @注意: 若不实现则无法使用以下【流程人员插件】 + * 1、相当岗位插件 + */ +public interface GroupRelationApi { + + /** + * 通过组织ID,角色获取 岗位,如获取某部门的部门负责人 + * + * @param orgIds + * @param roleKeys + * @return + */ + List getPostByGroupAndRoles(String orgIds, String roleKeys); + + /** + * 获取指定组织上级的指定岗位用户,如获取某部门上级的部门负责人 + * + * @param orgIds + * @param roleKeys + * @return + */ + List getPostByGroupParentAndRoles(String orgIds, String roleKeys); + + /** + * 获取指定组织上级中,指定级别的组织岗位,如某部门获取上两级的组织负责人 + * + * @param orgIds + * @param parentOrgSpecicalLevel + * @param roleKeys + * @return + */ + List getPostByGroupSpecicalLevelParentAndRoles(String orgIds, Integer parentOrgSpecicalLevel, + String roleKeys); + + /** + * 获取指定组织上级中,指定组织类型的岗位,如某部门获取上级为分公司的公司总监 + * + * @param orgIds + * @param parentOrgFilterType + * @param roleKeys + * @return + */ + List getPostByGroupSpecicalTypeParentAndRoles(String orgIds, Integer parentOrgFilterType, String roleKeys); + + /** + * 获取下级中指定岗位的人员 + * + * @param orgIds + * @param roleKey + * @return + */ + List getPostByGroupChildAndRoles(String orgIds, String roleKey); + + /** + * 获取下级中指定类型的人员 + * + * @param orgIds + * @param parentOrgFilterType + * @param roleKey + * @return + */ + List getPostByGroupSpecicalTypeChildAndRoles(String orgIds, Integer parentOrgFilterType, String roleKey); +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/OrgApi.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/OrgApi.java new file mode 100644 index 00000000..47c13b48 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/OrgApi.java @@ -0,0 +1,40 @@ +package com.dstz.org.api; + +import com.dstz.org.api.model.IGroup; + +import java.util.List; + +/** + * 组织相关接口 + * + * @author wacxhs + */ +public interface OrgApi { + + /** + * 根据组织ID和级别获取数据 + * + * @param orgId 组织ID + * @param grade 组织级别 + * @return + */ + IGroup getByOrgIdAndGrade(String orgId, String grade); + + + /** + * 根据组织ID获取子集数据 + * + * @param orgId 组织ID + * @return 子集数据(包括传入父及) + */ + List getHierarchyByOrgId(String orgId); + + /** + * 根据组织ID和级别获取层级数据列表 + * + * @param orgId 组织ID + * @param grade 组织级别 + * @return 层级数据列表 + */ + List getHierarchyByOrgIdAndGrade(String orgId, String grade); +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserApi.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserApi.java new file mode 100644 index 00000000..9e4e6c26 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserApi.java @@ -0,0 +1,79 @@ +package com.dstz.org.api; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.org.api.model.IUser; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Objects; + +/** + * 用户接口 + * + * @author wacxhs + */ +public interface UserApi { + + /** + * 根据用户ID获取用户 + * + * @param userId 用户ID + * @return 用户 + */ + default IUser getByUserId(String userId) { + Iterator iterator = getByUserIds(Collections.singleton(userId)); + return Objects.nonNull(iterator) && iterator.hasNext() ? iterator.next() : null; + } + + /** + * 根据用户ID集获取用户记录 + * + * @param userIds 用户记录集 + * @return 用户记录 + */ + Iterator getByUserIds(Collection userIds); + + /** + * 根据用户名获取用户 + * + * @param username 用户名 + * @return 用户 + */ + default IUser getByUsername(String username) { + Iterator iterator = getByUsernames(Collections.singleton(username)); + return Objects.nonNull(iterator) && iterator.hasNext() ? iterator.next() : null; + } + + /** + * 用户名集合获取用户列表 + * + * @param usernames 用户名集 + * @return 用户列表 + */ + Iterator getByUsernames(Collection usernames); + + /** + * 根据组类型和组ID集获取用户记录 + * + * @param groupType 组类型 + * @param groupIds 组ID + * @return 用户记录 + */ + Iterator getByGroupTypeAndGroupIds(String groupType, Collection groupIds); + + /** + * 用户查询过滤,做为自定义对护框用户选择弃数据加载来源 + * + * @param queryParamDTO 查询参数过滤对象 + * @return 用户记录 + */ + default PageListDTO queryFilter(QueryParamDTO queryParamDTO) { + return null; + } + + + + +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserOpenIdApi.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserOpenIdApi.java new file mode 100644 index 00000000..2ca57cc2 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/UserOpenIdApi.java @@ -0,0 +1,24 @@ +package com.dstz.org.api; + +import com.dstz.org.api.model.IUser; + +/** + * 针对用户操作openId接口 + */ +public interface UserOpenIdApi { + + /** + * 根据用户openId获取用户 + * + * @param openId 用户openId + * @return 用户 + */ + IUser getByOpenId(String openId); + + /** + * 设置用户openId + * @param account + * @param openId + */ + void saveOpenIdByAccount(String account,String openId); +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupGradeConstant.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupGradeConstant.java new file mode 100644 index 00000000..d23f8a64 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupGradeConstant.java @@ -0,0 +1,62 @@ +package com.dstz.org.api.enums; + +import java.util.Arrays; + +/** + * 组织级别 + * + * @author wacxhs + */ +public enum GroupGradeConstant { + + /** + * 集团 + */ + GROUP("0", "0-集团"), + /** + * 公司 + */ + COMPANY("1", "1-公司"), + + /** + * 部门 + */ + DEPARTMENT("3", "3-部门"), + + /** + * 班组 + */ + TEAM("5", "5-班组"); + + + /** + * 级别KEY + */ + private final String key; + + /** + * 级别标签 + */ + private final String label; + + GroupGradeConstant(String key, String label) { + this.key = key; + this.label = label; + } + + public static GroupGradeConstant valueOfKey(Integer key) { + return key == null ? null : valueOfKey(key.toString()); + } + + public static GroupGradeConstant valueOfKey(String key) { + return Arrays.stream(values()).filter(item -> item.getKey().equals(key)).findFirst().orElse(null); + } + + public String getKey() { + return key; + } + + public String getLabel() { + return label; + } +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupType.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupType.java new file mode 100644 index 00000000..ce2c38e6 --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/enums/GroupType.java @@ -0,0 +1,75 @@ +package com.dstz.org.api.enums; + +import java.util.Arrays; +import java.util.Optional; + +/** + * 组类型 + * + * @author wacxhs + */ +public enum GroupType { + + /** + * 组织 + */ + ORG("org", "组织"), + + /** + * 角色 + */ + ROLE("role", "角色"), + + /** + * 岗位 + */ + POST("post", "岗位"); + + /** + * 类型 + */ + private final String type; + + /** + * 描述 + */ + private final String desc; + + GroupType(String type, String desc) { + this.type = type; + this.desc = desc; + } + + public String getType() { + return type; + } + + public String getDesc() { + return desc; + } + + /** + * 匹配type,如果type未匹配到返回null + * + * @param type 类型 + * @return 组类型 + */ + public static GroupType fromTypeIfNull(String type) { + Optional groupType = Arrays.stream(values()).filter(item -> item.getType().equalsIgnoreCase(type)).findFirst(); + return groupType.orElse(null); + } + + /** + * 匹配type,如果type抛出异常 + * + * @param type 类型 + * @return 组类型 + */ + public static GroupType fromType(String type) throws IllegalArgumentException { + GroupType groupType = fromTypeIfNull(type); + if (groupType == null) { + throw new IllegalArgumentException(String.format("%s undefined", type)); + } + return groupType; + } +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IGroup.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IGroup.java new file mode 100644 index 00000000..8cbf167c --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IGroup.java @@ -0,0 +1,67 @@ +package com.dstz.org.api.model; + +/** + * 组类型接口 + * + * @author wacxhs + */ +public interface IGroup extends java.io.Serializable { + + /** + * 属性-别名 + */ + String ATTR_CODE = "code"; + + /** + * 获取组ID + * + * @return 组ID + */ + String getGroupId(); + + /** + * 获取组名称 + * + * @return 组名称 + */ + String getGroupName(); + + /** + * 获取组类型 + * + * @return 组类型 + */ + String getGroupType(); + + /** + * 组编码 + * + * @return 组编码 + */ + String getGroupCode(); + + /** + * 获取上级ID + * + * @return 上级ID + */ + default String getParentId() { + return null; + } + + /** + * 获取组织级别 + * @return组织级别 + */ + Integer getGroupLevel(); + + /** + * 获取属性值 + * + * @param attrName 属性名称 + * @param tClass 属性值类型类 + * @param T + * @return 属性值 + */ + T getAttrValue(String attrName, Class tClass); +} diff --git a/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IUser.java b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IUser.java new file mode 100644 index 00000000..a315481b --- /dev/null +++ b/ab-org/ab-org-api/src/main/java/com/dstz/org/api/model/IUser.java @@ -0,0 +1,55 @@ +package com.dstz.org.api.model; + +/** + * 用户接口 + * + * @author wacxhs + */ +public interface IUser extends java.io.Serializable { + + /** + * 属性-邮箱 + */ + String ATTR_EMAIL = "email"; + + /** + * 属性-手机号码 + */ + String ATTR_MOBILE = "mobile"; + + /** + * 过期 + */ + String ATTR_EXPIRE_DATE = "expireDate"; + + /** + * 获取用户ID + * + * @return 用户ID + */ + String getUserId(); + + /** + * 获取用户名 + * + * @return 用户名 + */ + String getUsername(); + + /** + * 获取用户姓名 + * + * @return 用户姓名 + */ + String getFullName(); + + /** + * 获取属性值 + * + * @param attrName 属性名称 + * @param tClass 属性类型 + * @param T + * @return 属性值 + */ + T getAttrValue(String attrName, Class tClass); +} diff --git a/ab-org/ab-org-core/pom.xml b/ab-org/ab-org-core/pom.xml new file mode 100644 index 00000000..2a7f2cc3 --- /dev/null +++ b/ab-org/ab-org-core/pom.xml @@ -0,0 +1,52 @@ + + + + ab-org + com.dstz + 2.5.0 + + 4.0.0 + + ab-org-core + + + + com.dstz + ab-base-common + + + com.dstz + ab-base-mapper + + + com.dstz + ab-base-web + + + org.springframework + spring-context + + + com.dstz + ab-auth-api + ${project.version} + + + com.dstz + ab-auth-spring-security-oauth2 + ${project.version} + + + com.dstz + ab-component-msg-api + ${project.version} + + + com.dstz + ab-groovy-script-api + ${project.version} + + + diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbDelegate.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbDelegate.java new file mode 100644 index 00000000..7e689a92 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbDelegate.java @@ -0,0 +1,28 @@ +package com.dstz.org.api; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.ClassUtil; +import cn.hutool.core.util.ReflectUtil; + +import java.beans.PropertyDescriptor; + +abstract class AbDelegate { + + protected transient final T delegate; + + public AbDelegate(T delegate) { + this.delegate = delegate; + } + + public R getAttrValue(String attrName, Class tClass) { + PropertyDescriptor propertyDescriptor = BeanUtil.getPropertyDescriptor(getClass(), attrName); + if (propertyDescriptor != null && propertyDescriptor.getReadMethod() != null) { + return Convert.convert(tClass, ReflectUtil.invoke(this, propertyDescriptor.getReadMethod())); + } + propertyDescriptor = BeanUtil.getPropertyDescriptor(ClassUtil.getClass(delegate), attrName); + Assert.isTrue(propertyDescriptor != null && propertyDescriptor.getReadMethod() != null, () -> new IllegalArgumentException(String.format("%s属性%s不存在", ClassUtil.getClass(delegate).getName(), attrName))); + return Convert.convert(tClass, ReflectUtil.invoke(delegate, propertyDescriptor.getReadMethod())); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbGroupApiImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbGroupApiImpl.java new file mode 100644 index 00000000..ad2aee03 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbGroupApiImpl.java @@ -0,0 +1,272 @@ +package com.dstz.org.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.lang.Pair; +import cn.hutool.core.lang.Tuple; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.constats.ThreadMapKeyConstant; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.QuerySort; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.GroupManager; +import com.dstz.org.core.manager.OrgPostManager; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.core.manager.RoleManager; +import com.dstz.org.core.mapper.OrgRelationMapper; +import com.dstz.org.enums.RelationTypeConstant; +import com.dstz.org.vo.GroupVO; +import com.dstz.org.vo.OrgPostVO; +import com.google.common.collect.ImmutableMap; +import org.checkerframework.common.value.qual.IntRange; +import org.springframework.stereotype.Service; + +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.IntStream; + +/** + * 组业务接口适配 + * + * @author wacxhs + */ +@Service("groupApi") +public class AbGroupApiImpl implements GroupApi { + + private final RoleManager roleManager; + private final GroupManager groupManager; + private final OrgRelationManager orgRelationManager; + private final OrgPostManager orgPostManager; + private final OrgRelationMapper orgRelationMapper; + + public AbGroupApiImpl(RoleManager roleManager, GroupManager groupManager, OrgRelationManager orgRelationManager, OrgPostManager orgPostManager, OrgRelationMapper orgRelationMapper) { + this.roleManager = roleManager; + this.groupManager = groupManager; + this.orgRelationManager = orgRelationManager; + this.orgPostManager = orgPostManager; + this.orgRelationMapper = orgRelationMapper; + } + + @Override + public Iterator getByGroupIds(String groupType, Collection groupIds) { + Iterator groupIterator = Collections.emptyIterator(); + //角色 + if (StrUtil.equalsIgnoreCase(GroupType.ROLE.getType(), groupType)) { + groupIterator = roleManager.selectByIds(groupIds).stream().map(AbRole::newRole).iterator(); + } + //岗位 + else if (StrUtil.equalsIgnoreCase(GroupType.POST.getType(), groupType)) { + groupIterator = orgPostManager.getByIds(groupIds).stream().map(AbRole::newPost).iterator(); + } + //组织 + else if (StrUtil.equalsIgnoreCase(GroupType.ORG.getType(), groupType)) { + groupIterator = groupManager.selectByIds(groupIds).stream().map(AbOrg::new).iterator(); + } + return groupIterator; + } + + @Override + public List getByGroupTypeAndUserId(String groupType, String userId) { + if (StrUtil.equalsIgnoreCase(GroupType.ROLE.getType(), groupType)) { + return CollUtil.map(roleManager.getByUserId(userId), AbRole::newRole, true); + } else if (StrUtil.equalsIgnoreCase(GroupType.POST.getType(), groupType)) { + return CollUtil.map(orgRelationManager.getUserRelation(userId, RelationTypeConstant.POST_USER.getKey()), AbRole::newPost, true); + } else if (StrUtil.equalsIgnoreCase(GroupType.ORG.getType(), groupType)) { + return CollUtil.map(groupManager.getByUserId(userId), AbOrg::new, false); + } + return new ArrayList<>(); + } + + @Override + public Iterator getByGroupCodes(String groupType, Collection groupCodes) { + Iterator groupIterator = Collections.emptyIterator(); + if (StrUtil.equalsIgnoreCase(GroupType.ROLE.getType(), groupType)) { + groupIterator = roleManager.selectByCodes(groupCodes).stream().map(AbRole::newRole).iterator(); + } else if (StrUtil.equalsIgnoreCase(GroupType.POST.getType(), groupType)) { + groupIterator = orgPostManager.getByIds(groupCodes).stream().map(AbRole::newPost).iterator(); + } else if (StrUtil.equalsIgnoreCase(GroupType.ORG.getType(), groupType)) { + groupIterator = groupManager.selectByCodes(groupCodes).stream().map(AbOrg::new).iterator(); + } + return groupIterator; + } + + @Override + public List queryFilter(String groupType, QueryParamDTO queryParamDTO) { + if (CharSequenceUtil.equalsIgnoreCase(GroupType.ROLE.getType(), groupType)) { + QueryParamDTO newQueryParamDTO = convertGroupQueryParam( + queryParamDTO, + ImmutableMap.of("groupName", "name", "groupCode", "code", "groupId", "id", "groupLevel", "level", "enabled", new Tuple("enabled", "N")) + ); + PageListDTO pageListDTO = roleManager.query(new DefaultAbQueryFilter(newQueryParamDTO)); + List roleRows = ObjectUtil.defaultIfNull(pageListDTO.getRows(), Collections.emptyList()).stream().map(AbRole::newRole).collect(Collectors.toList()); + return new PageListDTO<>(pageListDTO.getPageSize(), pageListDTO.getPage(), pageListDTO.getTotal(), roleRows); + } else if (CharSequenceUtil.equalsIgnoreCase(GroupType.POST.getType(), groupType)) { + DefaultAbQueryFilter queryFilter = convertPostQueryParam(queryParamDTO); + PageListDTO roles = orgRelationMapper.queryPosts(queryFilter); + return new PageListDTO<>(roles.getPageSize(), roles.getPage(), roles.getTotal(), CollUtil.map(roles.getRows(), AbOrgRelation::newPost, true)); + } else if (CharSequenceUtil.equalsIgnoreCase(GroupType.ORG.getType(), groupType)) { + Map.Entry userCountEntry = Optional.ofNullable(queryParamDTO.getQueryParam()).map(Map::entrySet).orElseGet(Collections::emptySet).stream().filter(entry -> StrUtil.startWith(entry.getKey(), "userCount")).findFirst().orElse(null); + QueryParamDTO newQueryParamDTO = convertGroupQueryParam(queryParamDTO, ImmutableMap.of("groupId", "id", "groupName", "name")); + // 组织树含用户数量 + if (Objects.nonNull(userCountEntry)) { + newQueryParamDTO.setQueryParam(MapUtil.filter(queryParamDTO.getQueryParam(), mapEntry -> !mapEntry.getKey().equals(userCountEntry.getKey()))); + return ObjectUtil.defaultIfNull(groupManager.queryGroup(newQueryParamDTO), Collections.emptyList()).stream().map(AbOrg::of).collect(Collectors.toList()); + } else { + PageListDTO pageListDTO = groupManager.query(new DefaultAbQueryFilter(newQueryParamDTO)); + return ObjectUtil.defaultIfNull(pageListDTO.getRows(), Collections.emptyList()).stream().map(AbOrg::new).collect(Collectors.toList()); + } + } + throw new IllegalArgumentException("未定义组类型, " + groupType); + } + + private QueryParamDTO convertGroupQueryParam(QueryParamDTO originQueryParamDTO, Map fieldMapping) { + if (CollUtil.isEmpty(originQueryParamDTO.getQueryParam()) && CharSequenceUtil.isEmpty(originQueryParamDTO.getSortColumn())) { + return originQueryParamDTO; + } + + Map newParamMap = MapUtil.newHashMap(CollUtil.size(originQueryParamDTO.getQueryParam())); + + // 重写查询 字段 + CollUtil.forEach(originQueryParamDTO.getQueryParam(), (CollUtil.KVConsumer) (key, value, index) -> { + int dollarIndex = key.lastIndexOf(StrPool.DOLLAR); + String newKey = key; + if (dollarIndex != CharSequenceUtil.INDEX_NOT_FOUND) { + Object mapField = fieldMapping.get(key.substring(0, dollarIndex)); + if (mapField instanceof String) { + newKey = mapField + key.substring(dollarIndex); + } else if (mapField instanceof Tuple) { + // 映射数据字段类型 + Tuple mapFieldTuple = (Tuple) mapField; + newKey = mapFieldTuple.get(0) + "$" + mapFieldTuple.get(1) + key.substring(dollarIndex + 2); + } + } + newParamMap.put(newKey, value); + }); + + QueryParamDTO newQueryParamDTO = originQueryParamDTO.clone(); + newQueryParamDTO.setQueryParam(newParamMap); + + // 重写排序字段 + if (CharSequenceUtil.isNotEmpty(newQueryParamDTO.getSortColumn())) { + Object mapField = fieldMapping.get(newQueryParamDTO.getSortColumn()); + if (mapField instanceof String) { + newQueryParamDTO.setSortColumn((String) mapField); + } else if (mapField instanceof Tuple) { + Tuple mapFieldTuple = (Tuple) mapField; + newQueryParamDTO.setSortColumn(mapFieldTuple.get(0)); + } + } + return newQueryParamDTO; + } + + private DefaultAbQueryFilter convertPostQueryParam(QueryParamDTO queryParamDTO) { + // 获取岗位名称筛选条件 + Map newQueryParam = MapUtil.newHashMap(CollUtil.size(queryParamDTO.getQueryParam())); + + Pair> nameFilterPair = null; + + // 处理查询条件 + for (Map.Entry entry : ObjectUtil.defaultIfNull(queryParamDTO.getQueryParam(), Collections.emptyMap()).entrySet()) { + final String key = entry.getKey(); + final Object value = entry.getValue(); + + int dollarIndex = key.lastIndexOf('$'); + if (dollarIndex != CharSequenceUtil.INDEX_NOT_FOUND) { + String fieldName = key.substring(0, dollarIndex); + // 岗位名称 + if (CharSequenceUtil.equalsIgnoreCase(fieldName, "name")) { + // 判断值不为空 + if(ObjectUtil.isNotEmpty(value)) { + ConditionType conditionType = ConditionType.formKey(key.substring(dollarIndex + 2)); + StringBuilder sqlBuilder = new StringBuilder("CONCAT_WS('-', ogroup.name_, orole.name_) "); + if (ConditionType.IN.equals(conditionType) || ConditionType.NOT_IN.equals(conditionType)) { + sqlBuilder.append(conditionType.condition()).append(" "); + Map fieldValues = MapUtil.newHashMap(); + sqlBuilder.append("("); + for (ListIterator listIterator = Convert.toList(String.class, value).listIterator(); listIterator.hasNext(); ) { + String itemName = "name_" + listIterator.nextIndex(); + sqlBuilder.append("#{").append(itemName).append("}"); + fieldValues.put(itemName, listIterator.next()); + if (listIterator.hasNext()) { + sqlBuilder.append(","); + } + } + sqlBuilder.append(")"); + nameFilterPair = Pair.of(sqlBuilder.toString(), fieldValues); + } else{ + String valueString = Convert.toStr(value); + if (ConditionType.LIKE.equals(conditionType)) { + sqlBuilder.append(conditionType.condition()).append(" "); + valueString = CharSequenceUtil.addPrefixIfNot(valueString, "%"); + valueString = CharSequenceUtil.addSuffixIfNot(valueString, "%"); + }else if(ConditionType.RIGHT_LIKE.equals(conditionType)){ + sqlBuilder.append(" LIKE "); + valueString = CharSequenceUtil.addPrefixIfNot(valueString, "%"); + } else if (ConditionType.LEFT_LIKE.equals(conditionType)) { + sqlBuilder.append(" LIKE "); + valueString = CharSequenceUtil.addSuffixIfNot(valueString, "%"); + }else{ + sqlBuilder.append(conditionType.condition()).append(" "); + } + sqlBuilder.append("#{name}"); + nameFilterPair = Pair.of(sqlBuilder.toString(), ImmutableMap.of("name", Convert.toStr(valueString))); + } + } + } else if (CharSequenceUtil.equalsIgnoreCase(fieldName, "id")) { + // 岗位ID处理 + List groupIds = new ArrayList<>(); + List roleIds = new ArrayList<>(); + for (String id : Convert.toList(String.class, value)) { + List groupIdUnderlineRoleId = CharSequenceUtil.split(id, StrPool.UNDERLINE); + Assert.isTrue(groupIdUnderlineRoleId.size() == 2, "不合法的岗位ID:{}", id); + groupIds.add(groupIdUnderlineRoleId.get(0)); + roleIds.add(groupIdUnderlineRoleId.get(1)); + } + newQueryParam.put("ogroup.id$VIN", groupIds); + newQueryParam.put("orole.id$VIN", roleIds); + }else if(CharSequenceUtil.equalsIgnoreCase(fieldName, "status")){ + String newKey = fieldName + "$N" + key.substring(dollarIndex + 2); + newQueryParam.put(newKey, value); + }else{ + newQueryParam.put(key, value); + } + }else{ + newQueryParam.put(key, value); + } + } + + QueryParamDTO newQueryParamDTO = queryParamDTO.clone(); + newQueryParamDTO.setQueryParam(newQueryParam); + + DefaultAbQueryFilter queryFilter = new DefaultAbQueryFilter(newQueryParamDTO); + // 岗位名称过滤 + if (nameFilterPair != null) { + queryFilter.addParam("nameFilterSql", nameFilterPair.getKey()); + nameFilterPair.getValue().forEach(queryFilter::addParam); + } + + // 排序字段替换 + if (CharSequenceUtil.isNotEmpty(queryParamDTO.getSortColumn())) { + boolean sortAsc = CharSequenceUtil.isEmpty(queryParamDTO.getSortOrder()) || QuerySort.ASC.equalsIgnoreCase(queryParamDTO.getSortOrder()); + String[] sortColumns = new String[]{"ogroup.name_", "orole.name_"}; + queryFilter.setQuerySortList(sortAsc ? QuerySort.ascs(sortColumns) : QuerySort.descs(sortColumns)); + } + + return queryFilter; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrg.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrg.java new file mode 100644 index 00000000..61dd7c16 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrg.java @@ -0,0 +1,74 @@ +package com.dstz.org.api; + +import com.dstz.base.common.constats.NumberPool; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.entity.Group; +import com.dstz.org.vo.GroupVO; + +/** + * 组 组织 + */ +class AbOrg extends AbDelegate implements IGroup { + + private static final long serialVersionUID = -6454184965728216871L; + + public AbOrg(Group delegate) { + super(delegate); + } + + public static AbOrg of(GroupVO groupVO) { + Group group = new Group(); + group.setId(groupVO.getId()); + group.setName(groupVO.getName()); + group.setParentId(groupVO.getParentId()); + group.setSn(groupVO.getSn()); + group.setCode(groupVO.getCode()); + group.setType(groupVO.getType()); + group.setDesc(groupVO.getDesc()); + group.setPath(groupVO.getPath()); + group.setPathName(groupVO.getPathName()); + return new AbOrg(group); + } + + @Override + public String getGroupId() { + return delegate.getId(); + } + + @Override + public String getGroupName() { + return delegate.getName(); + } + + @Override + public String getGroupType() { + return GroupType.ORG.getType(); + } + + @Override + public String getGroupCode() { + return delegate.getCode(); + } + + @Override + public String getParentId() { + return delegate.getParentId(); + } + + public String getType() { + return delegate.getType(); + } + + public String getPathName() { + return delegate.getPathName(); + } + + @Override + public Integer getGroupLevel() { + if (delegate.getType() == null) { + return NumberPool.INTEGER_ZERO; + } + return Integer.valueOf(delegate.getType()); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrgRelation.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrgRelation.java new file mode 100644 index 00000000..be648e08 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbOrgRelation.java @@ -0,0 +1,141 @@ +package com.dstz.org.api; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.enums.RelationTypeConstant; +import com.dstz.org.vo.OrgPostVO; + +/** + * 关联用户 + * + * @author wacxhs + */ +public class AbOrgRelation implements IGroup { + + /** + * 关联ID + */ + private String id; + + private String name; + + /** + * 关联类型 {@link RelationTypeConstant} + */ + private String type; + + /** + * 组ID + */ + private String groupId; + + /** + * 组名称 + */ + private String groupName; + + + /** + * 角色名 + */ + private String roleName; + + + /** + * 角色ID + */ + private String roleId; + + public static AbOrgRelation newPost(OrgPostVO orgPostVO) { + AbOrgRelation abOrgRelation = new AbOrgRelation(); + abOrgRelation.setId(String.format(StrPool.FORMATSTR, orgPostVO.getGroupId(), orgPostVO.getRoleId())); + abOrgRelation.setName(StrUtil.join(StrPool.DASHED, orgPostVO.getGroupName(), orgPostVO.getRoleName())); + abOrgRelation.setGroupId(orgPostVO.getGroupId()); + abOrgRelation.setGroupName(orgPostVO.getGroupName()); + abOrgRelation.setRoleId(orgPostVO.getRoleId()); + abOrgRelation.setRoleName(orgPostVO.getRoleName()); + abOrgRelation.setType(GroupType.POST.getType()); + return abOrgRelation; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + @Override + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + @Override + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + @Override + public String getGroupType() { + return type; + } + + @Override + public String getGroupCode() { + return null; + } + + @Override + public T getAttrValue(String attrName, Class tClass) { + return null; + } + + @Override + public Integer getGroupLevel() { + return NumberPool.INTEGER_ZERO; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbRole.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbRole.java new file mode 100644 index 00000000..86f00b3f --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbRole.java @@ -0,0 +1,81 @@ +package com.dstz.org.api; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.entity.Role; +import com.dstz.org.vo.OrgRelationUserVO; + +/** + * 组 角色/岗位 + * + * @author wacxhs + */ +class AbRole extends AbDelegate implements IGroup{ + + private static final long serialVersionUID = 6636298359317208345L; + + private final GroupType groupType; + + private AbRole(Role role, GroupType groupType) { + super(role); + this.groupType = groupType; + } + + @Override + public String getGroupId() { + return delegate.getId(); + } + + @Override + public String getGroupName() { + return delegate.getName(); + } + + @Override + public String getGroupType() { + return groupType.getType(); + } + + @Override + public String getGroupCode() { + return delegate.getCode(); + } + + public Integer getEnabled(){ + return delegate.getEnabled(); + } + + public Integer getLevel() { + return delegate.getLevel(); + } + + @Override + public Integer getGroupLevel() { + return delegate.getLevel(); + } + + public static AbRole newRole(Role role) { + return new AbRole(role, GroupType.ROLE); + } + + public static AbRole newPost(Role post) { + return new AbRole(post, GroupType.POST); + } + + public static AbRole newPost(OrgRelationUserVO orgRelationVO) { + Role role = new Role(); + role.setId(String.format(StrPool.FORMATSTR, orgRelationVO.getGroupId(), orgRelationVO.getRoleId())); + role.setName(StrUtil.join(StrPool.DASHED, orgRelationVO.getGroupName(), orgRelationVO.getRoleName())); + return newPost(role); + } + + public static AbRole newPost(Group group, Role role) { + Role post = new Role(); + post.setId(String.format(StrPool.FORMATSTR, group.getId(), role.getId())); + post.setName(StrUtil.join(StrPool.DASHED, group.getName(), role.getName())); + return newPost(post); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUser.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUser.java new file mode 100644 index 00000000..6a6d0154 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUser.java @@ -0,0 +1,67 @@ +package com.dstz.org.api; + +import com.dstz.org.api.model.IUser; +import com.dstz.org.core.entity.OrgUser; + +/** + * ab 用户实现 + * + * @author wacxhs + */ +class AbUser extends AbDelegate implements IUser { + + public AbUser(OrgUser delegate) { + super(delegate); + } + + @Override + public String getUserId() { + return delegate.getId(); + } + + @Override + public String getUsername() { + return delegate.getAccount(); + } + + @Override + public String getFullName() { + return delegate.getFullname(); + } + + public String getEmail() { + return delegate.getEmail(); + } + + public String getMobile() { + return delegate.getMobile(); + } + + public String getAddress() { + return delegate.getAddress(); + } + + public String getPhoto() { + return delegate.getPhoto(); + } + + public String getSex() { + return delegate.getSex(); + } + + public String getWeixin() { + return delegate.getWeixin(); + } + + public Integer getStatus() { + return delegate.getStatus(); + } + + public String getOpenid() { + return delegate.getOpenid(); + } + + public String getSignature() { + return delegate.getSignature(); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserApiImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserApiImpl.java new file mode 100644 index 00000000..4ec5c9e0 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserApiImpl.java @@ -0,0 +1,96 @@ +package com.dstz.org.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.CharSequenceUtil; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.org.api.model.IUser; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.core.manager.OrgUserManager; +import com.dstz.org.enums.RelationTypeConstant; +import com.google.common.collect.ImmutableMap; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 用户接口适配实现 + * + * @author wacxhs + */ +@Component("userApi") +public class AbUserApiImpl implements UserApi { + + @Autowired + private OrgUserManager orgUserManager; + + @Override + public Iterator getByUsernames(Collection usernames) { + if (CollUtil.isEmpty(usernames)) { + return Collections.emptyListIterator(); + } + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(OrgUser.class).in(OrgUser::getAccount, usernames); + return orgUserManager.selectByWrapper(queryWrapper).stream().map(AbUser::new).iterator(); + } + + @Override + public Iterator getByUserIds(Collection userIds) { + if (CollUtil.isEmpty(userIds)) { + return Collections.emptyListIterator(); + } + return orgUserManager.selectByIds(userIds).stream().map(AbUser::new).iterator(); + } + + @Override + public Iterator getByGroupTypeAndGroupIds(String groupType, Collection groupIds) { + RelationTypeConstant relationTypeConstant = RelationTypeConstant.getUserRelationTypeByGroupType(groupType); + Assert.notNull(relationTypeConstant, "%s GroupType not matched", StrUtil.nullToEmpty(groupType)); + return groupIds.stream() + .map(o -> orgUserManager.getUserListByRelation(o, relationTypeConstant.getKey())) + .flatMap(Collection::stream) + .map(AbUser::new) + .iterator(); + } + + @Override + public PageListDTO queryFilter(QueryParamDTO queryParamDTO) { + QueryParamDTO newQueryParamDTO = queryParamDTO; + if (CharSequenceUtil.isNotEmpty(queryParamDTO.getSortColumn()) || MapUtil.isNotEmpty(queryParamDTO.getQueryParam())) { + ImmutableMap fieldMapping = ImmutableMap.of("username", "account", "fullName", "fullname", "userId", "id"); + newQueryParamDTO = queryParamDTO.clone(); + + Map newQueryParam = MapUtil.newHashMap(CollUtil.size(queryParamDTO.getQueryParam())); + CollUtil.forEach(queryParamDTO.getQueryParam(), (k, v, index) -> { + int dollarIndex = k.lastIndexOf('$'); + String fieldName = k; + if(dollarIndex != -1){ + String mappingName = fieldMapping.get(k.substring(0, dollarIndex)); + if(mappingName != null){ + fieldName = mappingName + k.substring(dollarIndex); + } + } + newQueryParam.put(fieldName, v); + }); + + newQueryParamDTO.setQueryParam(newQueryParam); + + + // 替换排序字段 + newQueryParamDTO.setSortColumn(fieldMapping.get(queryParamDTO.getSortColumn())); + } + PageListDTO pageListDTO = orgUserManager.queryUser(newQueryParamDTO); + List userRows = ObjectUtil.defaultIfNull(pageListDTO.getRows(), Collections.emptyList()).stream().map(AbUser::new).collect(Collectors.toList()); + return new PageListDTO<>(pageListDTO.getPageSize(), pageListDTO.getPage(), pageListDTO.getTotal(), userRows); + } + + + +} \ No newline at end of file diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserOpenIdApiImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserOpenIdApiImpl.java new file mode 100644 index 00000000..4ad28b6e --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/AbUserOpenIdApiImpl.java @@ -0,0 +1,36 @@ +package com.dstz.org.api; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.org.api.model.IUser; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.core.manager.OrgUserManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import static com.dstz.org.core.constant.OrgStatusCode.NOT_FIND_USER_BY_OPENID; + +@Component("userOpenIdApi") +public class AbUserOpenIdApiImpl implements UserOpenIdApi { + + @Autowired + private OrgUserManager orgUserManager; + + + @Override + public IUser getByOpenId(String openId) { + OrgUser orgUser = orgUserManager.selectOne(Wrappers.lambdaQuery(OrgUser.class).eq(OrgUser::getOpenid, openId)); + Assert.notNull(orgUser, ()-> new BusinessMessage(NOT_FIND_USER_BY_OPENID)); + return new AbUser(orgUser); + } + + @Override + public void saveOpenIdByAccount(String account, String openId) { + OrgUser orgUser = orgUserManager.selectOne(Wrappers.lambdaQuery(OrgUser.class).eq(OrgUser::getAccount, account)); + Assert.notNull(orgUser, "根据账户 %s 未找到用户信息", StrUtil.nullToEmpty(account)); + orgUser.setOpenid(openId); + orgUserManager.update(orgUser); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/GroupRelationApiImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/GroupRelationApiImpl.java new file mode 100644 index 00000000..12cf3e4b --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/GroupRelationApiImpl.java @@ -0,0 +1,240 @@ +package com.dstz.org.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.NumberUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.org.api.enums.GroupGradeConstant; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.GroupManager; +import com.dstz.org.core.manager.RoleManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + * 相对岗位接口实现 + * + * @author wacxhs + */ +@Component +public class GroupRelationApiImpl implements GroupRelationApi { + + private static final Logger LOG = LoggerFactory.getLogger(GroupRelationApiImpl.class); + + private final GroupManager groupManager; + private final RoleManager roleManager; + + public GroupRelationApiImpl(GroupManager groupManager, RoleManager roleManager) { + this.groupManager = groupManager; + this.roleManager = roleManager; + } + + @Override + public List getPostByGroupAndRoles(String orgIds, String roleKeys) { + LOG.debug("通过组织角色查询岗位,组织IDs:{},角色KEYs:{}", orgIds, roleKeys); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys)) { + return Collections.emptyList(); + } + + List groups = groupManager.selectByIds(StrUtil.split(orgIds, StrPool.COMMA)); + + return getPostsByGroupsAndRoleKeys(groups, roleKeys); + } + + /** + * 通过组 角色构造 岗位 + * 如果角色为空,则返回组织 + * + * @param groups + * @param roleKeys + * @return + */ + private List getPostsByGroupsAndRoleKeys(List groups, String roleKeys) { + + if (CollectionUtil.isEmpty(groups)) { + return Collections.emptyList(); + } + + // 如果角色未指定则直接返回指定的组织即可 + if (StrUtil.isEmpty(roleKeys)) { + return CollUtil.map(groups, AbOrg::new, false); + } + + String[] roleKeyArr = roleKeys.split(StrUtil.COMMA); + + List postList = new ArrayList<>(); + for (String roleKey : roleKeyArr) { + Role role = roleManager.getById(roleKey); + if (role == null) { + continue; + } + + for (Group g : groups) { + postList.add(AbRole.newPost(g, role)); + } + } + + + if (LOG.isDebugEnabled()) { + LOG.debug("通过组织角色查询岗位返回结果{}条为:{}", postList.size(), JsonUtils.toJSONString(postList)); + } + + return postList; + } + + @Override + public List getPostByGroupParentAndRoles(String orgIds, String roleKeys) { + LOG.debug("通过组织上级,角色查询岗位,组织IDs:{},角色KEYs:{}", orgIds, roleKeys); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys)){ + return Collections.emptyList(); + } + + List groups = groupManager.selectByIds(StrUtil.split(orgIds, StrUtil.COMMA)); + + List parentsGroupsIds = groups.stream().filter(group -> !StrPool.NUMBER_ZERO.equals(group.getParentId())) + .map(Group::getParentId).collect(Collectors.toList()); + if (parentsGroupsIds.isEmpty()) { + List groupNames = groups.stream().map(Group::getName).collect(Collectors.toList()); + LOG.warn("相对岗位人员查询失败,父组织不存在 :" + groupNames); + return Collections.emptyList(); + } + // 父groups + List parentGroups = groupManager.selectByIds(parentsGroupsIds); + + return getPostsByGroupsAndRoleKeys(parentGroups, roleKeys); + } + + @Override + public List getPostByGroupSpecicalLevelParentAndRoles(String orgIds, Integer parentOrgSpecicalLevel, String roleKeys) { + LOG.debug("通过指定级别的组织上级,角色查询岗位,组织IDs:{},角色KEYs:{},级别:{}", orgIds, roleKeys, parentOrgSpecicalLevel); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys) || parentOrgSpecicalLevel == null || parentOrgSpecicalLevel < 1) { + return Collections.emptyList(); + } + + List groups = groupManager.selectByIds(StrUtil.split(orgIds, StrPool.COMMA)); + + List upLevelParent = new ArrayList<>(groups.size()); + for (Group group : groups) { + String path = group.getPath(); + if (StrUtil.isEmpty(path)) { + continue; + } + + List parentIds = StrUtil.split(path, StrPool.DOT); + if (parentIds.size() < (parentOrgSpecicalLevel + 1)) { + LOG.debug("组织{}上{}级的组织不存在", group.getName(), parentOrgSpecicalLevel); + continue; + } + + String specicalLevelId = parentIds.get(parentIds.size() - parentOrgSpecicalLevel - 1); + upLevelParent.add(specicalLevelId); + } + + List parentLevelGrouops = groupManager.selectByIds(upLevelParent); + + return getPostsByGroupsAndRoleKeys(parentLevelGrouops, roleKeys); + } + + @Override + public List getPostByGroupSpecicalTypeParentAndRoles(String orgIds, Integer parentOrgFilterType, String roleKeys) { + LOG.debug("通过指定级别的组织上级,角色查询岗位,组织IDs:{},角色KEYs:{},级别:{}", orgIds, roleKeys, parentOrgFilterType); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys) || parentOrgFilterType == null) { + return new ArrayList<>(); + } + + List groups = groupManager.selectByIds(StrUtil.split(orgIds, StrUtil.COMMA)); + + List parentGroups = new ArrayList<>(); + for (Group group : groups) { + + if (parentOrgFilterType.equals(Integer.valueOf(group.getType()))) { + parentGroups.add(group); + continue; + } + + Group parentTypeGroup = getParentByType(group, parentOrgFilterType); + if (parentTypeGroup == null) { + GroupGradeConstant type = GroupGradeConstant.valueOfKey(parentOrgFilterType); + LOG.debug("组织{}上级类型为【{}】的组织不存在", group.getName(), type == null ? "未知" : type.name()); + continue; + } + parentGroups.add(parentTypeGroup); + } + + return getPostsByGroupsAndRoleKeys(parentGroups, roleKeys); + } + + + private Group getParentByType(Group group, Integer type) { + if (group == null || type == null) { + return null; + } + + Group parentGroup = groupManager.getById(group.getParentId()); + if (parentGroup != null && type.equals(Integer.valueOf(parentGroup.getType()))) { + return parentGroup; + } + return getParentByType(parentGroup, type); + } + + @Override + public List getPostByGroupChildAndRoles(String orgIds, String roleKeys) { + LOG.debug("通过组织上级,角色查询岗位,组织IDs:{},角色KEYs:{}", orgIds, roleKeys); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys)) { + return Collections.emptyList(); + } + + List children = new ArrayList<>(); + + String[] groupIds = orgIds.split(StrUtil.COMMA); + for (String id : groupIds) { + children.addAll(groupManager.getChildrenByParentId(id)); + } + + return getPostsByGroupsAndRoleKeys(children, roleKeys); + } + + @Override + public List getPostByGroupSpecicalTypeChildAndRoles(String orgIds, Integer childOrgFilterType, String roleKeys) { + LOG.debug("通过指定级别的组织上级,角色查询岗位,组织IDs:{},角色KEYs:{},级别:{}", orgIds, roleKeys, childOrgFilterType); + if (StrUtil.isEmpty(orgIds) || StrUtil.isEmpty(roleKeys) || childOrgFilterType == null) { + return Collections.emptyList(); + } + + List groups = groupManager.selectByIds(StrUtil.split(orgIds, StrPool.COMMA)); + + List parentGroups = new ArrayList<>(); + for (Group group : groups) { + if (NumberUtil.isNumber(group.getType()) && Integer.valueOf(group.getType()).equals(childOrgFilterType)) { + parentGroups.add(group); + } + + List childTypeGroup = getChildByType(group, childOrgFilterType); + if (CollectionUtil.isEmpty(childTypeGroup)) { + GroupGradeConstant type = GroupGradeConstant.valueOfKey(childOrgFilterType); + LOG.debug("组织{}下级类型为【{}】的组织不存在", group.getName(), type == null ? "未知" : type.name()); + continue; + } + parentGroups.addAll(childTypeGroup); + } + + return getPostsByGroupsAndRoleKeys(parentGroups.stream().collect( + Collectors.collectingAndThen( + Collectors.toCollection(() -> new TreeSet<>(Comparator.comparing(Group::getId))), ArrayList::new)), roleKeys); + } + + private List getChildByType(Group group, Integer type) { + if (group == null || type == null) { + return null; + } + return groupManager.getChildByPathAndType(group.getPath(), type); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/api/OrgApiImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/OrgApiImpl.java new file mode 100644 index 00000000..57b2af8b --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/api/OrgApiImpl.java @@ -0,0 +1,67 @@ +package com.dstz.org.api; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.StrPool; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.mapper.GroupMapper; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +/** + * 组织相关接口实现 + * + * @author wacxhs + */ +@Service("orgApi") +public class OrgApiImpl implements OrgApi { + + private final GroupMapper groupMapper; + + public OrgApiImpl(GroupMapper groupMapper) { + this.groupMapper = groupMapper; + } + + @Override + public IGroup getByOrgIdAndGrade(String orgId, String grade) { + Group group = groupMapper.selectById(orgId); + if (group == null) { + return null; + } + if (StrUtil.equals(grade, group.getType())) { + return new AbOrg(group); + } + // 向上查找层级 + for (Group parent : groupMapper.selectBatchIds(StrUtil.split(group.getPath(), StrPool.DOT))) { + if (StrUtil.equals(grade, parent.getType())) { + return new AbOrg(parent); + } + } + return null; + } + + @Override + public List getHierarchyByOrgId(String orgId) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(Group.class).like(Group::getPath, orgId); + return groupMapper.selectList(queryWrapper).stream().map(AbOrg::new).collect(Collectors.toList()); + } + + @Override + public List getHierarchyByOrgIdAndGrade(String orgId, String grade) { + IGroup group = getByOrgIdAndGrade(orgId, grade); + if (group == null) { + return new ArrayList<>(); + } + List groupList = new ArrayList<>(); + groupList.add(group); + Optional.ofNullable(getHierarchyByOrgId(group.getGroupId())).filter(CollUtil::isNotEmpty).ifPresent(groupList::addAll); + return groupList; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/constant/OrgStatusCode.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/constant/OrgStatusCode.java new file mode 100644 index 00000000..3d49170f --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/constant/OrgStatusCode.java @@ -0,0 +1,49 @@ +package com.dstz.org.core.constant; + +import com.dstz.base.common.codes.IBaseCode; + +public enum OrgStatusCode implements IBaseCode { + + FROM_MODIFICATION_DEMO_PWD("fromModificationDemoPassword", "为了防止恶意破坏演示数据,禁止修改密码!
您的访问信息已经被我们统计!"), + USER_DOES_NOT_EXIST("userDoesNotExist", "用户不存在!"), + OLD_PWD_INPUT_ERROR("oldPwdInputError", "原密码输入错误!"), + EMAIL_DOES_NOT_EXIST("emailDoesNotExistPleaseContactAdminResetPwd", "邮箱不存在,请联系管理员重置密码"), + EMAIL_INPUT_ERROR("emailInputErrorPleaseEnterCorrectEmail", "邮箱输入错误,请输入正确的邮箱"), + NEW_PWD_IS_DIFFERENT_CONFIRM_PWD("newPasswordIsDifferentFromConfirmPassword", "新密码和确认密码不相同"), + VERIFICATION_CODE_IS_EXPIRED(" verificationCodeIsExpired", "验证码已过期,请重新获取"), + PWD_RESET_FUNCTION_IS_NOT_ENABLED("PasswordResetFunctionIsNotEnabled ", "密码重置功能暂未启用,请联系管理员重置密码!"), + ROLE_CODE_IS_EXIST("roleCodeIsExistInTheSystem", "角色编码在系统中已存在!"), + GROUP_CODE_IS_EXIST("groupCodeIsExistInTheSystem", "组织编码在系统中已存在!"), + ACCOUNT_IS_EXIST("accountIsExist", "账号已存在,请修改!"), + INPUT_INFORMATION_IS_EMPTY("inputInformationIsEmpty", "输入错误,请重新设置用户信息"), + DEL_FAILED_PARAM_IS_EMPTY("deleteFailedParamIsEmpty", "删除失败,参数不能为空"), + OPERATION_FAILURE("operationFailure", "操作失败,参数异常"), + IS_SUPER_ADMIN("isSuperAdmin","只有超管才能重置密码,请确认当前操作用户"), + NOT_ALLOW_DELETE_ADMIN("notAllowDeleteAdmin","删除用户失败,不能删除系统管理员"), + NOT_FIND_USER_BY_OPENID("not_find_user_by_openid", "用户尚未绑定第三方登录!"), + + /** + * 关系检查 + */ + ORG_RELATION_REMOVE_CHECK("OrgRelationRemoveCheck", "{}"); + + private final String code; + private final String message; + + OrgStatusCode(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return code; + } + + @Override + public String getMessage() { + return message; + } + + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Group.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Group.java new file mode 100644 index 00000000..2b596ace --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Group.java @@ -0,0 +1,249 @@ +package com.dstz.org.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; +import com.dstz.org.api.enums.GroupGradeConstant; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 组织架构 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@TableName("org_group") +public class Group extends AbModel { + + /** + * 主键 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + protected String id; + + /** + * 名字 + */ + @TableField("name_") + protected String name; + + /** + * 父ID + */ + @TableField("parent_id_") + protected String parentId; + + /** + * 排序 + */ + @TableField("sn_") + protected Integer sn; + + @TableField("code_") + protected String code; + + /** + * 类型:0集团,1公司,3部门 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = GroupGradeConstant.class, matchField = "key", attrMap = {@AbValueMap.AttrMap(originName = "label", targetName = "groupType")}) + @TableField("type_") + protected String type; + + /** + * 描述 + */ + @TableField("desc_") + protected String desc; + + /** + * 组织路径 + */ + @TableField("path_") + protected String path; + + /** + * 组织路径 + */ + @TableField("path_name_") + protected String pathName; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + protected Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + protected String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + protected String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + protected Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + protected String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + protected String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getPathName() { + return pathName; + } + + public void setPathName(String pathName) { + this.pathName = pathName; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgRelation.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgRelation.java new file mode 100644 index 00000000..d7bb7c46 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgRelation.java @@ -0,0 +1,248 @@ +package com.dstz.org.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; +import com.dstz.org.enums.OrgMaster; +import com.dstz.org.enums.OrgStatus; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * + *

+ * + * @author xz + * @since 2022-02-07 + */ +@TableName("org_relation") +public class OrgRelation extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + /** + * 组ID + */ + @TableField("group_id_") + private String groupId; + /** + * 用户ID + */ + @TableField("user_id_") + private String userId; + /** + * 0:默认组织,1:主组织 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgMaster.class, matchField = "master", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "isMasterDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "isMasterCss") + }) + @TableField("is_master_") + private Integer isMaster = NumberPool.INTEGER_ZERO; + /** + * 角色ID + */ + @TableField("role_id_") + private String roleId; + /** + * 状态:1启用,0禁用 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + @TableField("status_") + private Integer status = NumberPool.INTEGER_ONE; + /** + * 类型:groupUser,groupRole,userRole,groupUserRole + */ + @TableField("type_") + private String type; + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + public OrgRelation() { + } + + public OrgRelation(String groupId, String userId, String roleId, String type) { + this.groupId = groupId; + this.userId = userId; + this.roleId = roleId; + this.type = type; + } + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public Integer getIsMaster() { + return isMaster; + } + + public void setIsMaster(Integer isMaster) { + this.isMaster = isMaster; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + @Override + public String toString() { + return "OrgRelation{" + + "id=" + id + + ", groupId=" + groupId + + ", userId=" + userId + + ", isMaster=" + isMaster + + ", roleId=" + roleId + + ", status=" + status + + ", type=" + type + + ", createTime=" + createTime + + ", createBy=" + createBy + + ", createOrgId=" + createOrgId + + ", updateTime=" + updateTime + + ", updater=" + updater + + ", updateBy=" + updateBy + + "}"; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgUser.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgUser.java new file mode 100644 index 00000000..b050722b --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/OrgUser.java @@ -0,0 +1,337 @@ +package com.dstz.org.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; +import com.dstz.org.enums.OrgStatus; + +import java.io.Serializable; +import java.util.Date; + + +/** + *

+ * 用户表 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@TableName("org_user") +public class OrgUser extends AbModel { + + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 姓名 + */ + @TableField("fullname_") + private String fullname; + + /** + * 账号 + */ + @TableField("account_") + private String account; + + /** + * 密码 + */ + @TableField("password_") + private String password; + + /** + * 邮箱 + */ + @TableField("email_") + private String email; + + /** + * 手机号码 + */ + @TableField("mobile_") + private String mobile; + + /** + * 微信号 + */ + @TableField("weixin_") + private String weixin; + + /** + * 地址 + */ + @TableField("address_") + private String address; + + /** + * 头像 + */ + @TableField("photo_") + private String photo; + + /** + * 性别:男,女,未知 + */ + @TableField("sex_") + private String sex; + + /** + * 签名 + */ + @TableField("signature_") + private String signature; + + /** + * 来源 + */ + @TableField("from_") + private String from = StrPool.FROM; + + /** + * 0:禁用,1正常 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + @TableField("status_") + private Integer status; + + /** + * openid + */ + @TableField("openid_") + private String openid; + + /** + * 到期时间 + */ + @TableField("expire_date_") + private Date expireDate; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getWeixin() { + return weixin; + } + + public void setWeixin(String weixin) { + this.weixin = weixin; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getFrom() { + return from; + } + + public void setFrom(String from) { + this.from = from; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getOpenid() { + return openid; + } + + public void setOpenid(String openid) { + this.openid = openid; + } + + public Date getExpireDate() { + return expireDate; + } + + public void setExpireDate(Date expireDate) { + this.expireDate = expireDate; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Role.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Role.java new file mode 100644 index 00000000..b02971b5 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/entity/Role.java @@ -0,0 +1,225 @@ +package com.dstz.org.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; +import com.dstz.org.enums.OrgStatus; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 角色管理 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@TableName("org_role") +public class Role extends AbModel { + + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 角色名称 + */ + @TableField("name_") + private String name; + + /** + * 编码 + */ + @TableField("code_") + private String code; + + /** + * 0:禁用,1:启用 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + @TableField("enabled_") + private Integer enabled = NumberPool.INTEGER_ONE; + + /** + * 角色等级 + */ + @TableField("level_") + private Integer level; + + /** + * 描述 + */ + @TableField("desc_") + private String desc = StrPool.EMPTY; + + /** + * 分类字典编码 + */ + @AbValueMap(type = AbValueMapType.TYPE, fixValue = "jsfl", attrMap = {@AbValueMap.AttrMap(originName = "name", targetName = "typeName")}) + @TableField("type_code_") + private String typeCode; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Integer getEnabled() { + return enabled; + } + + public void setEnabled(Integer enabled) { + this.enabled = enabled; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/GroupManager.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/GroupManager.java new file mode 100644 index 00000000..6f541586 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/GroupManager.java @@ -0,0 +1,92 @@ +package com.dstz.org.core.manager; + +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.Group; +import com.dstz.org.dto.SaveGroupDTO; +import com.dstz.org.vo.GroupTreeVO; +import com.dstz.org.vo.GroupVO; + +import java.util.Collection; +import java.util.List; + +/** + *

+ * 组织架构 通用业务类 + *

+ * + * @author xz + * @since 2022-02-07 + */ +public interface GroupManager extends AbBaseManager { + + + /** + * 组织树接口 + * + * @param queryParamDTO 查询条件DTO + * @return 组织集合 + */ + List queryGroup(QueryParamDTO queryParamDTO); + + /** + * 获取组织信息 + * + * @param id 组织id + * @return 组织 + */ + GroupVO getGroupVo(String id); + + /** + * 保存组织 + * + * @param saveGroupDTO 保存组织DTO + * @return 组织id + */ + String saveGroup(SaveGroupDTO saveGroupDTO); + + /** + * 获取组织树 + * + * @return 树形组织集合 + */ + List getOrgTree(AbQueryFilter abQueryFilter); + + /** + * 通过 path 过滤孩子 + * 组业务接口适配:GroupRelationApiImpl + * + * @param path 路径 + * @param type 类型 + * @return 组织集合 + */ + List getChildByPathAndType(String path, Integer type); + + /** + * 获取下一级孩子 + * 组业务接口适配:GroupRelationApiImpl + * + * @param parentId 父组织id + * @return 组织集合 + */ + List getChildrenByParentId(String parentId); + + /** + * 通过编码获取组织列表 + * 组业务接口适配:AbGroupApiImpl + * + * @param codes 组织编码 + * @return 组织集合 + */ + List selectByCodes(Collection codes); + + /** + * 根据用户ID获取组织列表 + * 组业务接口适配:AbGroupApiImpl + * + * @param userId 用户id + * @return 组织集合 + */ + List getByUserId(String userId); +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgPostManager.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgPostManager.java new file mode 100644 index 00000000..57698ccc --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgPostManager.java @@ -0,0 +1,32 @@ +package com.dstz.org.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.Role; + +import java.util.Collection; +import java.util.List; + +/** + * 组织-岗位通用业务处理 + * + * @author wacxhs + */ +public interface OrgPostManager { + + /** + * 根据主键ID获取岗位列表 + * + * @param ids 组织ID集 + * @return 岗位列表 + */ + List getByIds(Collection ids); + + /** + * 查询岗位集 + * + * @param queryFilter 查询过滤器 + * @return 岗位列表 + */ + PageListDTO queryPosts(AbQueryFilter queryFilter); +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgRelationManager.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgRelationManager.java new file mode 100644 index 00000000..d35e471f --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgRelationManager.java @@ -0,0 +1,116 @@ +package com.dstz.org.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.dto.BatchSaveRelationDTO; +import com.dstz.org.dto.RemoveCheckRelationDTO; +import com.dstz.org.dto.SaveGroupUserRelDTO; +import com.dstz.org.dto.SaveRoleUsersDTO; +import com.dstz.org.vo.OrgRelationUserVO; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + *

+ * 通用业务类 + *

+ * + * @author xz + * @since 2022-02-09 + */ +public interface OrgRelationManager extends AbBaseManager { + + + /** + * 查询组用户 + * + * @param queryFilter 查询过滤器 + * @return 分页结果 + */ + PageListDTO queryGroupUser(AbQueryFilter queryFilter); + + /** + * 更新组织关系是否为主岗位 + * + * @param id 岗位id + */ + void setMaster(String id); + + /** + * 修改状态 + * + * @param id 岗位id + */ + String changeStatus(String id); + + /** + * 获取组织上的岗位 + * + * @param groupId 组织id + * @return 岗位集合 + */ + List getGroupPost(String groupId); + + /** + * 保存 用户与组织的关系 + * + * @param saveGroupUserRelDTO 保存岗位DTO + */ + void saveGroupUserRel(SaveGroupUserRelDTO saveGroupUserRelDTO); + + + /** + * 保存 用户与角色的关系 + * + * @param saveRoleUsersDTO 保存角色DTO + * @return 保存数量 + */ + int saveRoleUsers(SaveRoleUsersDTO saveRoleUsersDTO); + + /** + * 查询用户集合 + * + * @param queryFilter 查询过滤器 + * @return 分页用户集合 + */ + PageListDTO queryRoleUser(AbQueryFilter queryFilter); + + /** + * 删除前校验 + * + * @param removeCheckRelationDTO 删除校验DTO + */ + void beforeRemoveRelCheck(RemoveCheckRelationDTO removeCheckRelationDTO); + + /** + * 批量保存组织角色关系 + * + * @param batchSaveRelationDTO 批量保存DTO + */ + void batchSave(BatchSaveRelationDTO batchSaveRelationDTO); + + /** + * 获取用户的所有关系 + * + * @param userId 用户id + * @param relationType 关系类型 + * @return 关联用户集合 + */ + List getUserRelation(String userId, String relationType); + + /** + * 删除 组织上的岗位 + * + * @param groupId 组织id + */ + void removeGroupPostByGroupId(String groupId); + + void removeByUserId(String userId); + + Collection getUserRelationIds(Collection ids); + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgUserManager.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgUserManager.java new file mode 100644 index 00000000..8101012f --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/OrgUserManager.java @@ -0,0 +1,136 @@ +package com.dstz.org.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.dto.*; +import com.dstz.org.vo.OrgUserInfoVO; +import com.dstz.org.vo.OrgUserListJsonVO; +import com.dstz.org.vo.OrgUserVO; +import com.dstz.org.vo.ResourceUserVO; + +import java.util.List; + +/** + *

+ * 用户表 通用业务类 + *

+ * + * @author xz + * @since 2022-02-07 + */ +public interface OrgUserManager extends AbBaseManager { + + /** + * 用户权限查询 + * + * @param queryFilter 查询过滤器 + * @return 分页用户查询结果 + */ + PageListDTO getUserByResource(AbQueryFilter queryFilter); + + /** + * 仅供内置的自定义对话框-查询用户列表使用 + * + * @param queryParamDTO 查询条件DTO + * @return 分页角色集合 + */ + PageListDTO queryUser(QueryParamDTO queryParamDTO); + + /** + * 获取用户信息 + * + * @param id 用户id + * @return 用户信息 + */ + OrgUserVO getUserVO(String id); + + /** + * 发送验证码 + * + * @param sendCaptchaEmailDTO 发送验证码DTO + */ + void sendCaptchaEmail(SendCaptchaEmailDTO sendCaptchaEmailDTO); + + /** + * 设置密码 + * + * @param setPwdByEmailDTO 设置面DTO + */ + void setPwdByEmail(SetPwdByEmailDTO setPwdByEmailDTO); + + /** + * 根据角色ID获取用户列表 + * + * @param relId 岗位id + * @param type 类型 + * @return 用户集合 + */ + List getUserListByRelation(String relId, String type); + + /** + * 重置密码 + * + * @param id 用户id + */ + void resetUserPassword(String id); + + /** + * 更新密码 + * + * @param updateUserPassWorldDTO 更新密码DTO + */ + void updateUserPassWorld(UpdateUserPassWorldDTO updateUserPassWorldDTO); + + /** + * 修改用户状态 + * + * @param id 用户id + */ + void updateUserStatus(String id); + + /** + * 保存用户 + * + * @param saveOrgUserDTO 保存用户DTO + */ + void saveUserDto(SaveOrgUserDTO saveOrgUserDTO); + + /** + * 保存个人信息 + * @param saveOrgUserDTO-保存个人信息DTO + */ + void saveUserInfo(SaveOrgUserInfoDTO saveOrgUserDTO); + + /** + * 根据主键选择性更新记录 + * + * @param record 更新记录 + * @return 影响行数 + */ + int updateByPrimaryKeySelective(OrgUser record); + + /** + * 个人中心信息 + * + * @return 个人信息VO + */ + OrgUserInfoVO getUserInfo(); + + + /** + * 判断系统中用户是否存在。 + */ + boolean isUserExist(OrgUser user); + + /** + * 分页列表查询 + * + * @param abQueryFilter 查询过滤器 + * @return OrgUserListJsonVO 集合 + */ + PageListDTO queryUserList(AbQueryFilter abQueryFilter); + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/RoleManager.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/RoleManager.java new file mode 100644 index 00000000..1faebed3 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/RoleManager.java @@ -0,0 +1,63 @@ +package com.dstz.org.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.org.core.entity.Role; +import com.dstz.org.dto.SaveRoleDTO; +import com.dstz.org.vo.ResourceRoleVO; + +import java.util.Collection; +import java.util.List; + +/** + *

+ * 角色管理 通用业务类 + *

+ * + * @author xz + * @since 2022-02-07 + */ +public interface RoleManager extends AbBaseManager { + + String saveRoleDto(SaveRoleDTO saveRoleDTO); + + /** + * 判断角色是否存在。 + * + * @param role + * @return + */ + boolean isRoleExist(Role role); + + /** + * 根据code获取角色 + * + * @param code 角色编码 + * @return 角色 + */ + Role getByCode(String code); + + /** + * 用过用户ID 获取角色 + * + * @param userId 用户id + * @return 角色集合 + */ + List getByUserId(String userId); + + /** + * 角色编码集查询角色列表 + * + * @param codes 编码集合 + * @return 角色集合 + */ + List selectByCodes(Collection codes); + + /** + * 根据资源获取角色集合 + * + * @param resourcesId 资源id + * @return 角色集合 + */ + List getRoleListByResource(String resourcesId); + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/GroupManagerImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/GroupManagerImpl.java new file mode 100644 index 00000000..a1ec5ab7 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/GroupManagerImpl.java @@ -0,0 +1,344 @@ +package com.dstz.org.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.baomidou.mybatisplus.extension.plugins.pagination.PageDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.IdGeneratorUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.org.api.enums.GroupGradeConstant; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.core.constant.OrgStatusCode; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.core.manager.GroupManager; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.core.mapper.GroupMapper; +import com.dstz.org.core.mapper.OrgUserMapper; +import com.dstz.org.dto.RemoveCheckRelationDTO; +import com.dstz.org.dto.SaveGroupDTO; +import com.dstz.org.vo.GroupTreeVO; +import com.dstz.org.vo.GroupUserCountVO; +import com.dstz.org.vo.GroupVO; +import com.dstz.org.vo.OrgRelationUserVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * 组织架构 通用服务实现类 + * + * @author xz + * @since 2022-02-07 + */ +@Service("groupManager") +public class GroupManagerImpl extends AbBaseManagerImpl implements GroupManager { + + @Autowired + GroupMapper groupMapper; + @Autowired + OrgUserMapper orgUserMapper; + @Autowired + OrgRelationManager orgRelationManager; + + + /** + * 删除组织接口 + * + * @param ids 实体ID集 + * @return 返回删除数量 + */ + @Override + public int removeByIds(Collection ids) { + Assert.isFalse(CollectionUtil.isEmpty(ids), () -> new BusinessMessage(OrgStatusCode.DEL_FAILED_PARAM_IS_EMPTY)); + + for (Serializable id : ids) { + orgRelationManager.beforeRemoveRelCheck(new RemoveCheckRelationDTO(id.toString(), null)); + Group group = groupMapper.selectById(id); + Assert.notNull(group, () -> new BusinessMessage(OrgStatusCode.OPERATION_FAILURE)); + + if (StrUtil.isNotEmpty(group.getPath())) { + //查询子组织 + List childList = groupMapper.getChildByPath(group.getPath() + StrPool.MOD, null); + if (CollectionUtil.isNotEmpty(childList)) { + // 级联删除子组织 + for (Group child : childList) { + orgRelationManager.beforeRemoveRelCheck(new RemoveCheckRelationDTO(child.getId(), null)); + super.removeById(child.getId()); + } + } + } + } + return super.removeByIds(ids); + } + + /** + * 获取组织详情接口 + * + * @param id 组织id + * @return GroupVO + */ + @Override + public GroupVO getGroupVo(String id) { + GroupVO group = BeanCopierUtils.transformBean(getById(id), GroupVO.class); + Assert.notNull(group, () -> new BusinessMessage(OrgStatusCode.OPERATION_FAILURE)); + + List orgRelationList = orgRelationManager.getGroupPost(group.getId()); + group.setOrgRelationList(orgRelationList); + if (!StrPool.NUMBER_ZERO.equals(group.getParentId())) { + group.setParentName(getById(group.getParentId()).getName()); + } + return group; + } + + /** + * 获取组织树接口 + * + * @return GroupTreeVO + */ + @Override + public List getOrgTree(AbQueryFilter abQueryFilter) { + + List groupList = groupMapper.queryGroup(abQueryFilter); + List groupTreeList = BeanCopierUtils.transformList(groupList, GroupTreeVO.class); + return setRootGroup(groupTreeList); + } + + /** + * 设置 组织树的根目录 + * 解决 数据权限带来的 组织树断层 + * + * @param groupTreeList + * @return + */ + private List setRootGroup(List groupTreeList) { + final IGroup currentGroup = UserContextUtils.getGroup().orElse(null); + GroupTreeVO rootGroup = new GroupTreeVO(); + rootGroup.setName("组织"); + rootGroup.setId(StrPool.NUMBER_ZERO); + + if (CollUtil.isEmpty(groupTreeList)) { + if (UserContextUtils.isSuperAdmin()) { + return ListUtil.toList(rootGroup); + } + if (currentGroup != null) { + rootGroup.setName(currentGroup.getGroupName()); + rootGroup.setId(currentGroup.getGroupId()); + } + return ListUtil.toList(rootGroup); + } + if (UserContextUtils.isSuperAdmin()) { + groupTreeList.add(rootGroup); + } + return BeanConversionUtils.listToTree(groupTreeList); + } + + /** + * 保存组织接口 + * + * @param saveGroupDTO 组织Dto + * @return 组织id + */ + @Override + public String saveGroup(SaveGroupDTO saveGroupDTO) { + Group group = BeanCopierUtils.transformBean(saveGroupDTO, Group.class); + Assert.notNull(group, () -> new BusinessMessage(OrgStatusCode.OPERATION_FAILURE)); + QueryWrapper queryWrapper = new QueryWrapper(); + //新增组织 + if (StrUtil.isEmpty(group.getId())) { + Assert.isFalse( + exists(queryWrapper.eq("code_", group.getCode())), + () -> new BusinessMessage(OrgStatusCode.GROUP_CODE_IS_EXIST)); + String id = IdGeneratorUtils.nextId(); + group.setId(id); + saveGroupDTO.setId(id); + if (StrUtil.isNotEmpty(group.getParentId()) && !StrPool.NUMBER_ZERO.equals(group.getParentId())) { + Group parent = groupMapper.selectById(group.getParentId()); + if (parent != null && StrUtil.isNotEmpty(parent.getPath())) { + group.setPath(parent.getPath().concat(StrPool.DOT).concat(group.getId())); + } + } else { + group.setPath(group.getId()); + } + groupMapper.insert(setEntityPathName(group)); + } else { + groupMapper.updateById(updatePathName(group)); + //删除旧地组织岗位 + orgRelationManager.removeGroupPostByGroupId(group.getId()); + } + // 创建新地组织岗位 + if (CollectionUtil.isNotEmpty(saveGroupDTO.getOrgRelationList())) { + creatOrgRelation(saveGroupDTO); + } + return group.getId(); + } + + /** + * 新增组织时设置pathName + * + * @param group 组织 + * @return Group 组织 + */ + private Group setEntityPathName(Group group) { + if (group.getPath().equals(group.getId())) { + group.setPathName(group.getName()); + } else { + List companyGroup = selectByIds(StrUtil.split(group.getPath(), StrPool.DOT)); + List pathNames = companyGroup.stream().map(Group::getName).collect(Collectors.toList()); + String pathName = CollectionUtil.join(pathNames, StrPool.DASHED); + group.setPathName(pathName); + } + return group; + } + + /** + * 更新组织时维护pathName + * + * @param group 组织 + * @return group 组织 + */ + private Group updatePathName(Group group) { + Group oldGroup = groupMapper.selectById(group.getId()); + String oldGroupName = oldGroup.getName(); + //没有修改name + if (group.getName().equals(oldGroupName) && StrUtil.isNotEmpty(group.getPathName())) { + return group; + } + + // 维护所属下级的path + List groups = groupMapper.getChildByPath(compatiblePathName(group, group.getName()).getPath() + StrPool.MOD, null); + //更新组织的path + for (Group g : groups) groupMapper.updateById(compatiblePathName(g, group.getName())); + + return group; + } + + /** + * 兼容没有path的旧数据 + * + * @param group 组织 + * @param groupName 组织名称 + * @return Group 组织 + */ + private Group compatiblePathName(Group group, String groupName) { + if (StrUtil.isEmpty(group.getPathName())) { + return setEntityPathName(group); + } else { + group.setPathName(group.getPathName().replace(group.getName(), groupName)); + } + return group; + } + + /** + * 创建组织岗位 + * + * @param groupDTO 组织DTO + */ + private void creatOrgRelation(SaveGroupDTO groupDTO) { + List list = BeanCopierUtils.transformList(groupDTO.getOrgRelationList(), OrgRelation.class); + + if (CollectionUtil.isNotEmpty(list)) { + list.forEach(orgRelation -> { + orgRelation.setGroupId(groupDTO.getId()); + orgRelation.setId( + String.format(StrPool.FORMATSTR, groupDTO.getId(), orgRelation.getRoleId())); + }); + orgRelationManager.bulkCreate(list); + } + } + + /** + * 自定义对话框组织 + * + * @return List + */ + public List queryGroup(QueryParamDTO paramDTO) { + List groups = groupMapper.queryGroup(new DefaultAbQueryFilter(paramDTO)); + List groupUserCountVOS = groupMapper.getGroupUserCount(); + for (GroupVO group : groups) { + String groupId = group.getId(); + for (GroupUserCountVO groupCount : groupUserCountVOS) { + String id = groupCount.getId(); + if (groupId != null && groupId.equals(id)) { + if (!NumberPool.INTEGER_ZERO.equals(groupCount.getUserCount())) { + group.setName(group.getName() + StrPool.LPAREN + groupCount.getUserCount() + StrPool.RPAREN); + break; + } + } + } + } + return groups; + } + + /** + * 组业务接口适配:GroupRelationApiImpl + * 通过 path 过滤孩子 + * + * @param path 路径 + * @param type 类型 + * @return 组织集合 + */ + @Override + public List getChildByPathAndType(String path, Integer type) { + if (StrUtil.isEmpty(path)) return Collections.emptyList(); + if (type == null) return groupMapper.getChildByPath(path.concat(StrPool.MOD), null); + return groupMapper.getChildByPath(path.concat(StrPool.MOD), GroupGradeConstant.valueOfKey(type).getKey()); + } + + /** + * 组业务接口适配:GroupRelationApiImpl + * 获取下一级孩子 + * + * @param parentId 父组织id + * @return 组织集合 + */ + @Override + public List getChildrenByParentId(String parentId) { + if (StrUtil.isEmpty(parentId)) return Collections.emptyList(); + return groupMapper.getChildrenByParentId(parentId); + } + + /** + * 根据用户ID获取组织列表 + * 组业务接口适配:AbGroupApiImpl + * + * @param userId 用户id + * @return 组织结合 + */ + @Override + public List getByUserId(String userId) { + Page groupPage = groupMapper.getByUserId(PageDTO.of(0, 200, false), userId); + return groupPage.getRecords(); + } + + /** + * 通过编码获取组织列表 + * 组业务接口适配:AbGroupApiImpl + * + * @param codes 组织编码 + * @return 组织结合 + */ + @Override + public List selectByCodes(Collection codes) { + return groupMapper.selectList(Wrappers.lambdaQuery(Group.class).in(Group::getCode, codes)); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgPostManagerImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgPostManagerImpl.java new file mode 100644 index 00000000..f45b4ef6 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgPostManagerImpl.java @@ -0,0 +1,61 @@ +package com.dstz.org.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.text.CharPool; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.OrgPostManager; +import com.dstz.org.core.mapper.OrgRelationMapper; +import com.dstz.org.vo.OrgPostVO; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashSet; +import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; + +/** + * 通用组织-岗位通用业务处理实现 + * + * @author wacxhs + */ +@Service("orgPostManager") +public class OrgPostManagerImpl implements OrgPostManager { + + private final OrgRelationMapper orgRelationMapper; + + public OrgPostManagerImpl(OrgRelationMapper orgRelationMapper) { + this.orgRelationMapper = orgRelationMapper; + } + + @Override + public List getByIds(Collection ids) { + if (CollUtil.isEmpty(ids)) { + return new ArrayList<>(); + } + final int hashsetSize = (int) (ids.size() / MapUtil.DEFAULT_LOAD_FACTOR) + 1; + Set groupIds = new LinkedHashSet<>(hashsetSize); + Set roleIds = new LinkedHashSet<>(hashsetSize); + + for (String id : ids) { + int dashedIndex = StrUtil.indexOf(id, CharPool.UNDERLINE); + if (dashedIndex == StrUtil.INDEX_NOT_FOUND) { + throw new IllegalArgumentException(String.format("岗位ID格式不正确, id:%s", id)); + } + groupIds.add(id.substring(0, dashedIndex)); + roleIds.add(id.substring(dashedIndex + 1)); + } + return orgRelationMapper.getPostsByRoleIdsAndGroupIds(roleIds, groupIds).stream().map(OrgPostVO::toRole).collect(Collectors.toList()); + } + + @Override + public PageListDTO queryPosts(AbQueryFilter queryFilter) { + PageListDTO orgPostPageList = orgRelationMapper.queryPosts(queryFilter); + return new PageListDTO<>(orgPostPageList.getPageSize(), orgPostPageList.getPage(), orgPostPageList.getTotal(), CollUtil.map(orgPostPageList.getRows(), OrgPostVO::toRole, false)); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgRelationManagerImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgRelationManagerImpl.java new file mode 100644 index 00000000..18a02f8a --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgRelationManagerImpl.java @@ -0,0 +1,335 @@ +package com.dstz.org.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.events.AbUserEvent; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.org.enums.RelationTypeConstant; +import com.dstz.org.core.constant.OrgStatusCode; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.core.mapper.OrgRelationMapper; +import com.dstz.org.dto.BatchSaveRelationDTO; +import com.dstz.org.dto.RemoveCheckRelationDTO; +import com.dstz.org.dto.SaveGroupUserRelDTO; +import com.dstz.org.dto.SaveRoleUsersDTO; +import com.dstz.org.vo.OrgRelationUserVO; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Arrays; +import java.util.Collection; +import java.util.Collections; +import java.util.List; + +/** + * 通用服务实现类 + * + * @author xz + * @since 2022-02-09 + */ +@Service("orgRelationManager") +public class OrgRelationManagerImpl extends AbBaseManagerImpl implements OrgRelationManager { + protected final Logger log = LoggerFactory.getLogger(this.getClass()); + + @Autowired + OrgRelationMapper orgRelationMapper; + + + /** + * 查询组用户 + * + * @param filter 查询参数 + * @return OrgRelationUserVO集合 + */ + @Override + public PageListDTO queryGroupUser(AbQueryFilter filter) { + return orgRelationMapper.queryGroupUser(filter); + } + + /** + * 设置主组织 修改所有子组织 + * + * @param id 关系变id + */ + @Override + public void setMaster(String id) { + OrgRelation orgRelation = orgRelationMapper.selectById(id); + if (orgRelation == null || StrUtil.isEmpty(orgRelation.getUserId())) return; + + //关系类型 + List relationList = Arrays.asList(RelationTypeConstant.GROUP_USER.getKey(), RelationTypeConstant.POST_USER.getKey()); + //查询出用户 与 岗位,组织的所有关系,置为 非主版本 + List userGroupRelations = orgRelationMapper.getRelationsByParam(relationList, orgRelation.getUserId(), null, null); + + //设置 + if (CollectionUtil.isNotEmpty(userGroupRelations)) { + userGroupRelations.forEach(rel -> { + if (NumberPool.INTEGER_ONE.equals(rel.getIsMaster())) { + rel.setIsMaster(NumberPool.INTEGER_ZERO); + update(BeanCopierUtils.transformBean(rel, OrgRelation.class)); + } + }); + } + //切换是否主版本 + orgRelation.setIsMaster(NumberPool.INTEGER_ZERO.equals(orgRelation.getIsMaster()) ? NumberPool.INTEGER_ONE : NumberPool.INTEGER_ZERO); + update(orgRelation); + } + + /** + * 修改岗位状态 + * + * @param id 关系表id + */ + @Override + public String changeStatus(String id) { + OrgRelation orgRelation = orgRelationMapper.selectById(id); + if (orgRelation == null) return StrPool.FALSE; + + //修改状态更新数据 + orgRelation.setStatus(NumberPool.INTEGER_ZERO.equals(orgRelation.getStatus()) ? NumberPool.INTEGER_ONE : NumberPool.INTEGER_ZERO); + update(orgRelation); + + String userId = orgRelation.getUserId(); + if (StrUtil.isEmpty(userId)) return StrPool.FALSE; + // 当前用户修改了自己的信息,需要更新缓存数据 + if (StrUtil.equals(UserContextUtils.getUserId(), userId)) return StrPool.TRUE; + return StrPool.FALSE; + } + + /** + * 获取组织岗位 + * + * @param groupId 组织id + * @return OrgRelationUserVO集合 + */ + @Override + public List getGroupPost(String groupId) { + return orgRelationMapper.getGroupPost(groupId); + } + + /** + * 保存组织岗位 + * + * @param saveGroupUserRelDTO 组织岗位DTO + */ + @Override + public void saveGroupUserRel(SaveGroupUserRelDTO saveGroupUserRelDTO) { + for (String userId : saveGroupUserRelDTO.getUserIds()) { + if (StrUtil.isEmpty(userId)) continue; + //构建OrgRelation + OrgRelation orgRelation = new OrgRelation(saveGroupUserRelDTO.getGroupId(), userId, null, RelationTypeConstant.GROUP_USER.getKey()); + + if (ArrayUtil.isNotEmpty(saveGroupUserRelDTO.getRoleIds())) { + for (String roleId : saveGroupUserRelDTO.getRoleIds()) { + //设置参数 + orgRelation.setRoleId(roleId); + orgRelation.setType(RelationTypeConstant.POST_USER.getKey()); + // 不存在则创建 + insertRel(orgRelation); + } + continue; + } + //不存在时 添加数据 + insertRel(orgRelation); + } + } + + /** + * 保存角色用户 + * + * @param saveRoleUsersDTO 角色用户DTO + * @return 添加数量 + */ + @Override + public int saveRoleUsers(SaveRoleUsersDTO saveRoleUsersDTO) { + final String roleId = saveRoleUsersDTO.getRoleId(); + int i = 0; + for (String userId : saveRoleUsersDTO.getUserIds()) { + OrgRelation orgRelation = new OrgRelation(null, userId, roleId, RelationTypeConstant.USER_ROLE.getKey()); + if (checkRelationIsExist(orgRelation)) { + log.warn("关系重复添加,已跳过 {}", JsonUtils.toJSONString(orgRelation)); + continue; + } + i++; + orgRelationMapper.insert(orgRelation); + } + return i; + } + + /** + * * 删除用户 需要删除关联关系 + * + * @param ids 实体ID集 + * @return 删除用户数量 + */ + @Override + public int removeByIds(Collection ids) { + Collection relIds=getUserRelationIds(ids); + if (CollectionUtil.isEmpty(relIds)){ + return super.removeByIds(ids); + }; + return super.removeByIds(relIds); + } + + @Override + public Collection getUserRelationIds(Collection ids){ + return orgRelationMapper.getUserRelationIds(ids); + } + + + /** + * 查询组用户 + * + * @param queryFilter 查询过滤 + * @return 分页查询结果 + */ + @Override + public PageListDTO queryRoleUser(AbQueryFilter queryFilter) { + return orgRelationMapper.queryRoleUser(queryFilter); + } + + /** + * 删除 角色、删除组织、删除岗位前进行校验 + * 删除角色 校验 岗位、岗位人员、角色人员是否存在 + * 删除组织、 校验岗位、组织人员 + * 删除岗位 校验岗位人员 + * + * @param removeCheckRelationDTO 删除校验DTO + */ + @Override + public void beforeRemoveRelCheck(RemoveCheckRelationDTO removeCheckRelationDTO) { + String groupId = removeCheckRelationDTO.getGroupId(); + String roleId = removeCheckRelationDTO.getRoleId(); + // 通过 关系查询 用户 + AbQueryFilter filter = DefaultAbQueryFilter.build(); + filter.getPage().setSearchCount(true); + // 岗位检查 岗位下人员是否存在 + if (StrUtil.isNotEmpty(groupId)) + filter.addFilter("relation.group_id_", groupId, ConditionType.EQUAL); + + if (StrUtil.isNotEmpty(roleId)) + filter.addFilter("relation.role_id_", roleId, ConditionType.EQUAL); + + if (StrUtil.isNotEmpty(roleId) && StrUtil.isNotEmpty(groupId)) + filter.addFilter("relation.type_", RelationTypeConstant.POST.getKey(), ConditionType.NOT_EQUAL); + + PageListDTO pageListDTO = orgRelationMapper.queryRoleUser(filter); + if (pageListDTO == null || CollUtil.isEmpty(pageListDTO.getRows())) return; + + StringBuilder sb = new StringBuilder("请先移除以下关系:
"); + for (OrgRelationUserVO orgRelationUserVO : pageListDTO) { + getRelationNotes(orgRelationUserVO, sb); + } + if (pageListDTO.getTotal() > 10) sb.append("......
"); + + sb.append(" 共[").append(pageListDTO.getTotal()).append("]条,待移除关系"); + throw new BusinessMessage(OrgStatusCode.ORG_RELATION_REMOVE_CHECK.formatDefaultMessage(sb.toString())); + } + + private void getRelationNotes(OrgRelationUserVO relation, StringBuilder sb) { + // 岗位 + if (RelationTypeConstant.POST.getKey().equals(relation.getType())) { + sb.append("岗位:").append(relation.getPostName()); + } else if (RelationTypeConstant.POST_USER.getKey().equals(relation.getType())) { + sb.append("岗位 [").append(relation.getPostName()).append("] 下用户:").append(relation.getUserFullname()); + } else if (RelationTypeConstant.GROUP_USER.getKey().equals(relation.getType())) { + sb.append("组织 [").append(relation.getGroupName()).append("] 下用户:").append(relation.getUserFullname()); + } else if (RelationTypeConstant.USER_ROLE.getKey().equals(relation.getType())) { + sb.append("角色 [").append(relation.getRoleName()).append("] 下用户:").append(relation.getUserFullname()); + } + sb.append("
"); + } + + /** + * 批量保存组织角色关系 + * + * @param batchSaveRelationDTO 批量保存DTO + */ + @Override + public void batchSave(BatchSaveRelationDTO batchSaveRelationDTO) { + String[] roleIds = batchSaveRelationDTO.getRoleIds().split(StrPool.COMMA); + String[] groupIds = batchSaveRelationDTO.getGroupIds().split(StrPool.COMMA); + Arrays.asList(roleIds).forEach(roleId -> + Arrays.asList(groupIds).forEach(groupId -> { + OrgRelation orgRelation = new OrgRelation(groupId, null, roleId, RelationTypeConstant.POST.getKey()); + insertRel(orgRelation); + }) + ); + } + + + /** + * 根据组织id删除关系 + * + * @param id 组织id + */ + @Override + public void removeGroupPostByGroupId(String id) { + orgRelationMapper.removeGroupPostByGroupId(id); + } + + /** + * 获取用户的所有关系 + * + * @param userId 用户id + * @param relationType 关系类型 + * @return OrgRelationUserVO集合 + */ + @Override + public List getUserRelation(String userId, String relationType) { + List orgRelationUserVOS= orgRelationMapper.getUserRelation(userId, relationType); + if (CollectionUtil.isEmpty(orgRelationUserVOS)){ + return Collections.emptyList(); + } + return orgRelationUserVOS; + } + + /** + * 查询角色是否已存在 + * + * @param orgRelation 角色关系 + * @return boolean + */ + private boolean checkRelationIsExist(OrgRelation orgRelation) { + return orgRelationMapper.getCountByRelation(orgRelation) > NumberPool.INTEGER_ZERO; + } + + /** + * 保存关联关系 + * + * @param orgRelation 角色关系 + */ + private void insertRel(OrgRelation orgRelation) { + // 不存在则创建 + if (! checkRelationIsExist(orgRelation)) { + orgRelationMapper.insert(orgRelation); + } else { + log.warn("关系重复添加,已跳过 {}", JsonUtils.toJSONString(orgRelation)); + } + } + + @Override + public void removeByUserId(String userId) { + if (StrUtil.isNotEmpty(userId)){ + orgRelationMapper.removeByUserId(userId); + } + } +} + diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgUserManagerImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgUserManagerImpl.java new file mode 100644 index 00000000..cbd0b2f5 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/OrgUserManagerImpl.java @@ -0,0 +1,358 @@ +package com.dstz.org.core.manager.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.auth.utils.VerifyCodeUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.encrypt.EncryptUtil; +import com.dstz.base.common.enums.EnvironmentConstants; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.enums.IdentityType; +import com.dstz.base.common.events.AbUserEvent; +import com.dstz.base.common.exceptions.ApiException; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.component.msg.api.MsgApi; +import com.dstz.component.msg.api.dto.MsgDTO; +import com.dstz.org.core.constant.OrgStatusCode; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.core.manager.OrgUserManager; +import com.dstz.org.core.mapper.OrgUserMapper; +import com.dstz.org.dto.*; +import com.dstz.org.enums.RelationTypeConstant; +import com.dstz.org.vo.OrgUserInfoVO; +import com.dstz.org.vo.OrgUserListJsonVO; +import com.dstz.org.vo.OrgUserVO; +import com.dstz.org.vo.ResourceUserVO; +import com.google.common.collect.ImmutableMap; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Collections; +import java.util.Date; +import java.util.List; +import java.util.regex.Pattern; + +/** + * 用户表 通用服务实现类 + * + * @author xz + * @since 2022-02-07 + */ +@Service("userManager") +public class OrgUserManagerImpl extends AbBaseManagerImpl implements OrgUserManager { + + @Autowired + OrgUserMapper orgUserMapper; + @Autowired + OrgRelationManager orgRelationManager; + @Autowired + ICache cache; + @Autowired + private MsgApi msgApi; + + + /** + *判断系统中用户是否存在 + * @param user 用户 + * @return Boolean + */ + @Override + public boolean isUserExist(OrgUser user) { + return orgUserMapper.isUserExist(user) > NumberPool.INTEGER_ZERO; + } + + /** + * 保存用户方法 + * + * @param saveOrgUserDTO 保存用户DTO + */ + @Override + public void saveUserDto(SaveOrgUserDTO saveOrgUserDTO) { + OrgUser orgUser = BeanCopierUtils.transformBean(saveOrgUserDTO, OrgUser.class); + Assert.notNull(orgUser, () -> new BusinessMessage(OrgStatusCode.INPUT_INFORMATION_IS_EMPTY)); + //查询账户是否已存在账户 + Assert.isFalse( + isUserExist(orgUser), + () -> new BusinessMessage(OrgStatusCode.ACCOUNT_IS_EXIST)); + + //新增用户 + if (StrUtil.isEmpty(orgUser.getId())) { + if (StrUtil.isEmpty(orgUser.getPassword())){ + orgUser.setPassword(StrPool.STRING_PWD_ONZ); + } + //密码进行加密 + orgUser.setPassword(EncryptUtil.encryptSha256(orgUser.getPassword())); + orgUserMapper.insert(orgUser); + }else { + orgUserMapper.updateById(orgUser); + //删除旧的关联关系 + orgRelationManager.removeByUserId(orgUser.getId()); + } + + //获取新的关联关系 + List orgRelationList = saveOrgUserDTO.getOrgRelationList(); + if (CollectionUtil.isEmpty(orgRelationList)) return; + + //设置绑定的用户id + orgRelationList.forEach(rel -> rel.setUserId(orgUser.getId())); + //批量新增关联关系 + orgRelationManager.bulkCreate(BeanCopierUtils.transformList(orgRelationList, OrgRelation.class)); + } + + /** + * 获取个人中心信息 + * + * @return 用户信息VO + */ + @Override + public OrgUserInfoVO getUserInfo() { + OrgUserInfoVO orgUserInfoVO = null; + OrgUser user=getById(UserContextUtils.getUserId()); + if(user!= null) { + orgUserInfoVO=BeanCopierUtils.transformBean(getById(UserContextUtils.getUserId()), OrgUserInfoVO.class); + orgUserInfoVO.setOrgRelationList(orgRelationManager.getUserRelation(user.getId(), null)); + } + return orgUserInfoVO; + } + + /** + * 保存个人信息 + * @param saveOrgUserInfoDTO-保存个人信息DTO + */ + @Override + public void saveUserInfo(SaveOrgUserInfoDTO saveOrgUserInfoDTO) { + OrgUser orgUser = BeanCopierUtils.transformBean(saveOrgUserInfoDTO, OrgUser.class); + Assert.notNull(orgUser, () -> new BusinessMessage(OrgStatusCode.INPUT_INFORMATION_IS_EMPTY)); + orgUserMapper.updateById(orgUser); + } + + /** + * 获取用户信息 + * + * @param id 用户id + * @return 用户信息VO + */ + public OrgUserVO getUserVO(String id) { + OrgUserVO orgUserVO = BeanCopierUtils.transformBean(getById(id), OrgUserVO.class); + Assert.notNull(orgUserVO, () -> new BusinessMessage(OrgStatusCode.OPERATION_FAILURE)); + + //设置用户关系信息 + orgUserVO.setOrgRelationList(orgRelationManager.getUserRelation(id, null)); + return orgUserVO; + } + + /** + * 重置密码为1 + * + * @param id 用户id + */ + @Override + public void resetUserPassword(String id) { + OrgUser user = getById(id); + //设置加密后的密码 + user.setPassword("a4ayc/80/OGda4BO/1o/V0etpOqiLx1JwB5S3beHW0s="); + //清空账户到期时间 + user.setExpireDate(null); + + orgUserMapper.updateById(user); + } + + /** + * 修改用户状态 + * + * @param id 用户id + */ + @Override + public void updateUserStatus(String id) { + OrgUser user = getById(id); + //设置状态 + user.setStatus(NumberPool.INTEGER_ONE.equals(user.getStatus()) ? NumberPool.INTEGER_ZERO : NumberPool.INTEGER_ONE); + orgUserMapper.updateById(user); + } + + /** + * * 删除用户 需要删除关联关系 + * + * @param ids 实体ID集 + * @return 删除用户数量 + */ + @Override + public int removeByIds(Collection ids) { + orgRelationManager.removeByIds(ids); + return super.removeByIds(ids); + } + + /** + * 修改个人密码 + * + * @param updateUserPassWorldDTO 更新密码DTO + */ + @Override + public void updateUserPassWorld(UpdateUserPassWorldDTO updateUserPassWorldDTO) { + //获取用户 + OrgUser user; + String currentUserId = UserContextUtils.getUserId(); + if (StrUtil.isEmpty(currentUserId)) { + user = selectOne(Wrappers.lambdaQuery(OrgUser.class).eq(OrgUser::getAccount, updateUserPassWorldDTO.getAccount())); + } else { + user = getById(currentUserId); + } + + //正则检验 + checkPassWorld(updateUserPassWorldDTO.getNewPassword()); + //检验原密码密码 + Assert.isTrue( + EncryptUtil.encryptSha256(updateUserPassWorldDTO.getOldPassword()).equals(user.getPassword()), + () -> new BusinessMessage(OrgStatusCode.OLD_PWD_INPUT_ERROR)); + + user.setPassword(EncryptUtil.encryptSha256(updateUserPassWorldDTO.getNewPassword())); + + //设置到期时间 + user.setExpireDate(DateUtils.addDays(new Date(), PropertyEnum.PWD_LOSE_COUNT.getPropertyValue(int.class))); + orgUserMapper.updateById(user); + } + + /** + * 更新密码的校验 + * + * @param passWord 密码 + */ + private void checkPassWorld(String passWord) { + //演示环境不允许修改 + Assert.isFalse( + SpringUtil.getActiveProfile().equalsIgnoreCase(EnvironmentConstants.DEMO.getKey()), + () -> new ApiException(OrgStatusCode.FROM_MODIFICATION_DEMO_PWD)); + + //正则校验密码 + Assert.isTrue( + Pattern.matches(PropertyEnum.PWD_CHECK_RULE_KEY.getPropertyValue(String.class), passWord), + () -> new BusinessMessage( + GlobalApiCodes.PARAMETER_INVALID.formatDefaultMessage( + PropertyEnum.PWD_CHECK_RULE_TXT.getPropertyValue(String.class)))); + } + + /** + * 发送找回密码的验证码 + * + * @param sendCaptchaEmailDTO 发送验证码DTO + */ + @Override + public void sendCaptchaEmail(SendCaptchaEmailDTO sendCaptchaEmailDTO) { + //根据账户查询用户 + OrgUser user = selectOne(Wrappers.lambdaQuery(OrgUser.class).eq(OrgUser::getAccount, sendCaptchaEmailDTO.getAccount())); + //检验用户和邮箱 + Assert.notNull(user, () -> new BusinessMessage(OrgStatusCode.USER_DOES_NOT_EXIST)); + Assert.notNull(user.getEmail(), () -> new BusinessMessage(OrgStatusCode.EMAIL_DOES_NOT_EXIST)); + Assert.isTrue(sendCaptchaEmailDTO.getEmail().equals(user.getEmail()), () -> new BusinessMessage(OrgStatusCode.EMAIL_INPUT_ERROR)); + + //生成验证码 + final String verifyCode = VerifyCodeUtil.generateVerifyCode(); + List receivers = Collections.singletonList(new SysIdentity(user.getId(), user.getAccount(), IdentityType.USER.getKey())); + List msgTypes = Collections.singletonList("email"); + //缓存中设置验证码的有效期 + cache.put(AbCacheRegionConstant.LOGIN_PWD_REGION, user.getAccount(), verifyCode); + MsgDTO msgDTO = new MsgDTO("AgileBPM 找回密码验证码", "forgotPasswordValidation", receivers, msgTypes, ImmutableMap.of("verifyCode", verifyCode)); + msgApi.sendMsg(msgDTO); + } + + /** + * 通过验证码设置密码 + * + * @param setPwdByEmailDTO 设置密码DTO + */ + @Override + public void setPwdByEmail(SetPwdByEmailDTO setPwdByEmailDTO) { + OrgUser user = selectOne(Wrappers.lambdaQuery(OrgUser.class).eq(OrgUser::getAccount, setPwdByEmailDTO.getAccount())); + Assert.notNull(user, () -> new BusinessMessage(OrgStatusCode.USER_DOES_NOT_EXIST)); + //检验是否开启邮箱找回密码功能 + Assert.isFalse( + PropertyEnum.IS_OPEN_RESET_PWD_BY_EMAIL.getPropertyValue(boolean.class), + () -> new ApiException(OrgStatusCode.PWD_RESET_FUNCTION_IS_NOT_ENABLED)); + + Assert.isTrue( + setPwdByEmailDTO.getNewPassword().equals(setPwdByEmailDTO.getConfirmPassword()), + () -> new BusinessMessage(OrgStatusCode.NEW_PWD_IS_DIFFERENT_CONFIRM_PWD)); + //从缓存获取验证码 + Object captcha = cache.getIfPresent(AbCacheRegionConstant.LOGIN_PWD_REGION, setPwdByEmailDTO.getAccount()); + //验证码校验 + Assert.isFalse( + StrUtil.isEmptyIfStr(captcha) || setPwdByEmailDTO.getCaptcha().equals(captcha), + () -> new BusinessMessage(OrgStatusCode.VERIFICATION_CODE_IS_EXPIRED)); + + //到期时间 + user.setExpireDate(DateUtils.addDays(new Date(), PropertyEnum.PWD_LOSE_COUNT.getPropertyValue(int.class))); + //密码加密 + user.setPassword(EncryptUtil.encryptSha256(setPwdByEmailDTO.getNewPassword())); + orgUserMapper.updateById(user); + } + + /** + * 通过relId和type查找用户 + * + * @param relId 岗位id + * @param type 类型 + * @return OrgUser集合 + */ + @Override + public List getUserListByRelation(String relId, String type) { + if (type.equals(RelationTypeConstant.POST_USER.getKey())) { + String[] postId = relId.split(StrPool.UNDERLINE); + if (postId.length != 2) { + return Collections.emptyList(); + } + return orgUserMapper.getUserListByPost(postId[1], postId[0]); + } + return orgUserMapper.getUserListByRelation(relId, type); + } + + /** + * 权限用户查询 + * + * @param queryFilter 参数过滤 + */ + @Override + public PageListDTO getUserByResource(AbQueryFilter queryFilter) { + return orgUserMapper.getUserByResource(queryFilter); + } + + /** + * 仅供内置的自定义对话框-查询用户列表使用 + * + * @param paramDTO 请求参数 + * @return PageListDTO数据对象 + */ + @Override + public PageListDTO queryUser(QueryParamDTO paramDTO) { + return orgUserMapper.queryUser(new DefaultAbQueryFilter(paramDTO)); + } + + @Override + public int updateByPrimaryKeySelective(OrgUser record) { + return orgUserMapper.updateByPrimaryKeySelective(record); + } + + @Override + public PageListDTO queryUserList(AbQueryFilter abQueryFilter) { + return orgUserMapper.queryUserList(abQueryFilter); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/RoleManagerImpl.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/RoleManagerImpl.java new file mode 100644 index 00000000..1560b5ed --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/manager/impl/RoleManagerImpl.java @@ -0,0 +1,129 @@ +package com.dstz.org.core.manager.impl; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.org.core.constant.OrgStatusCode; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.core.manager.RoleManager; +import com.dstz.org.core.mapper.RoleMapper; +import com.dstz.org.dto.RemoveCheckRelationDTO; +import com.dstz.org.dto.SaveRoleDTO; +import com.dstz.org.vo.ResourceRoleVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * 角色管理 通用服务实现类 + * + * @author xz + * @since 2022-02-07 + */ +@Service("roleManager") +public class RoleManagerImpl extends AbBaseManagerImpl implements RoleManager { + + @Autowired + RoleMapper roleMapper; + @Autowired + OrgRelationManager orgRelationManager; + + + /** + * 保存角色 + * + * @param saveRoleDTO 保存角色DTO + */ + @Override + public String saveRoleDto(SaveRoleDTO saveRoleDTO) { + String desc; + Role role = BeanCopierUtils.transformBean(saveRoleDTO, Role.class); + //校验角色是否已存在 + Assert.isFalse(isRoleExist(role), + () -> new BusinessMessage(OrgStatusCode.ROLE_CODE_IS_EXIST)); + if (StrUtil.isEmpty(role.getId())) { + roleMapper.insert(role); + desc = "添加%s成功"; + } else { + roleMapper.updateById(role); + desc = "更新%s成功"; + } + return desc; + } + + @Override + public boolean isRoleExist(Role role) { + return !Objects.equals(roleMapper.isRoleExist(role), NumberPool.INTEGER_ZERO); + } + + /** + * 批量删除角色 + * + * @param ids 实体ID集 + * @return 删除的数量 + */ + @Override + public int removeByIds(Collection ids) { + for (Serializable id : ids) { + Assert.isFalse(StrUtil.isEmptyIfStr(id), () -> new BusinessMessage(OrgStatusCode.DEL_FAILED_PARAM_IS_EMPTY)); + orgRelationManager.beforeRemoveRelCheck(new RemoveCheckRelationDTO(null, id.toString())); + } + return super.removeByIds(ids); + } + + /** + * 通过角色编码查找角色 + * 相对岗位接口实现 GroupRelationApiImpl + * + * @param code 角色编码 + * @return 角色 + */ + @Override + public Role getByCode(String code) { + return selectOne(Wrappers.lambdaQuery(Role.class).eq(Role::getCode, code)); + } + + /** + * 通过用户ID 获取角色 + * 组业务接口适配 AbGroupApiImpl + * + * @param userId 用户id + * @return 角色集合 + */ + @Override + public List getByUserId(String userId) { + return roleMapper.getByUserId(userId); + } + + /** + * 角色编码集查询角色列表 + * 组业务接口适配 AbGroupApiImpl + * + * @param codes 角色编码 + * @return 角色集合 + */ + @Override + public List selectByCodes(Collection codes) { + return roleMapper.selectList(Wrappers.lambdaQuery(Role.class).in(Role::getCode, codes)); + } + + /** + * 获取资源上的角色列表 + * + * @param resourcesId 资源id + * @return 资源角色集合 + */ + @Override + public List getRoleListByResource(String resourcesId) { + return roleMapper.getRoleListByResource(resourcesId); + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/GroupMapper.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/GroupMapper.java new file mode 100644 index 00000000..f7ad70f7 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/GroupMapper.java @@ -0,0 +1,70 @@ +package com.dstz.org.core.mapper; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.Group; +import com.dstz.org.vo.GroupUserCountVO; +import com.dstz.org.vo.GroupVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 组织架构 Mapper 接口 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@Mapper +public interface GroupMapper extends AbBaseMapper { + + void removeAll(); + + /** + * @param queryFilter 查询过滤器 + * @return 组织集合 + */ + List queryGroup(AbQueryFilter queryFilter); + + /** + * 查询组织用户接口 + * + * @return 组织用户数量VO + */ + List getGroupUserCount(); + + /** + * 通过 path 过滤孩子 + * 组业务接口适配:GroupRelationApiImpl + * + * @param path 路径 + * @param type 类型 + * @return 组织集合 + */ + List getChildByPath(@Param("path") String path, @Param("type") String type); + + /** + * 获取下一级孩子 + * 组业务接口适配:GroupRelationApiImpl + * + * @param parentId 父组织id + * @return 组织集合 + */ + List getChildrenByParentId(String parentId); + + + /** + * 根据用户ID获取组织列表,含岗位,组织,第一个为主组织 + * 组业务接口适配:AbGroupApiImpl + * + * @param page 分页 + * @param userId 用户id + * @return 组织集合 + */ +

> P getByUserId(P page, @Param("userId") String userId); + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgRelationMapper.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgRelationMapper.java new file mode 100644 index 00000000..1c1bc456 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgRelationMapper.java @@ -0,0 +1,116 @@ +package com.dstz.org.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.vo.OrgPostVO; +import com.dstz.org.vo.OrgRelationUserVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.io.Serializable; +import java.util.Collection; +import java.util.List; + +/** + *

+ * Mapper 接口 + *

+ * + * @author xz + * @since 2022-02-09 + */ +@Mapper +public interface OrgRelationMapper extends AbBaseMapper { + + + /** + * 查询 组用户 + * + * @param queueFile 查询过滤器 + * @return 分页组用户 + */ + PageListDTO queryGroupUser(AbQueryFilter queueFile); + + + /** + * 获取用户的 关系 + * + * @param userId 必须 + * @param relationType 非必须 + * @return 用户关系集合 + */ + List getUserRelation(@Param("userId") String userId, @Param("type") String relationType); + + /** + * 获取组织岗位 + * + * @param groupId 组织id + * @return 岗位集合 + */ + List getGroupPost(String groupId); + + + /** + * 通过 参数查询关系列表 + * + * @param relationTypes 关系类型 + * @param userId 用户ID + * @param groupId 组织ID + * @param roleId 角色ID + * @return 用户关系列表 + */ + List getRelationsByParam(@Param("relationTypes") List relationTypes, @Param("userId") String userId, @Param("groupId") String groupId, @Param("roleId") String roleId); + + + /** + * @param relation userId,roleId,groupId,relation 存在则相等判断 + * @param relation 存在则非匹配 + * @return 查询角色是否已存在 + */ + int getCountByRelation(OrgRelation relation); + + /** + * 通过 userId 刪除所有关系 + * + * @param userId 用户id + */ + void removeByUserId(Serializable userId); + + + /** + * 删除 组下的岗位 + * + * @param groupId 组织id + */ + void removeGroupPostByGroupId(String groupId); + + /** + * 查询用户集合 + * + * @param queryFilter 查询过滤 + * @return 用户集合 + */ + PageListDTO queryRoleUser(AbQueryFilter queryFilter); + + /** + * 根据角色ID集和组织ID集获取岗位 + * + * @param roleIds 角色ID集 + * @param groupIds 组织ID集 + * @return 岗位 + */ + List getPostsByRoleIdsAndGroupIds(@Param("roleIds") Collection roleIds, @Param("groupIds") Collection groupIds); + + + Collection getUserRelationIds(@Param("ids") Collection ids); + + /** + * 查询岗位集合 + * + * @param queryFilter 查询参数过滤 + * @return 分页列表 + */ + PageListDTO queryPosts(AbQueryFilter queryFilter); +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgUserMapper.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgUserMapper.java new file mode 100644 index 00000000..8633ab5d --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/OrgUserMapper.java @@ -0,0 +1,87 @@ +package com.dstz.org.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.vo.OrgUserListJsonVO; +import com.dstz.org.vo.ResourceUserVO; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + * 用户表 Mapper 接口 + * + * @author xz + * @since 2022-02-07 + */ +@Mapper +public interface OrgUserMapper extends AbBaseMapper { + + + /** + * 判断用户是否存在。 + */ + Integer isUserExist(OrgUser user); + + /** + * 根据角色ID获取用户列表 + * + * @param relId 角色id + * @param type 关系类型 + * @return 用户集合 + */ + List getUserListByRelation(@Param("relationId") String relId, @Param("relationType") String type); + + /** + * 获取岗位下的所有用户 + * + * @param roleId 角色id + * @param groupId 组织id + * @return 用户集合 + */ + List getUserListByPost(@Param("roleId") String roleId, @Param("groupId") String groupId); + + /** + * 获取指定组织路径下的所有用户 + * + * @param path 路径 + * @return 用户集合 + */ + List getUserListByGroupPath(@Param("path") String path); + + /** + * 根据主键选择性更新记录 + * + * @param record 编码 + * @return 删除数量 + */ + int updateByPrimaryKeySelective(OrgUser record); + + /** + * 用户权限查询接口 + * + * @param queryFilter 查询过滤器 + * @return 权限用户集合 + */ + PageListDTO getUserByResource(AbQueryFilter queryFilter); + + /** + * 用户权限查询接口 + * + * @param queryFilter 查询过滤器 + * @return 分页用户集合 + */ + PageListDTO queryUser(AbQueryFilter queryFilter); + + /** + * queryFilter 分页列表查询 + * + * @param abQueryFilter + * @return + */ + PageListDTO queryUserList(AbQueryFilter abQueryFilter); + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/RoleMapper.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/RoleMapper.java new file mode 100644 index 00000000..1c447f81 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/mapper/RoleMapper.java @@ -0,0 +1,49 @@ +package com.dstz.org.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.org.core.entity.Role; +import com.dstz.org.vo.ResourceRoleVO; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +/** + *

+ * 角色管理 Mapper 接口 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@Mapper +public interface RoleMapper extends AbBaseMapper { + + + /** + * 根据资源id获取角色列表 + * + * @param resourcesId 资源id + * @return 角色集合 + */ + List getRoleListByResource(String resourcesId); + + + + + /** + * 用过用户ID 获取角色 + * + * @param userId 用户id + * @return 角色集合 + */ + List getByUserId(String userId); + + + /** + * 判断角色系统中是否存在。 + * + * @param role 角色 + * @return count + */ + Integer isRoleExist(Role role); +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/core/script/OrgScript.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/script/OrgScript.java new file mode 100644 index 00000000..b325f7a9 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/core/script/OrgScript.java @@ -0,0 +1,355 @@ +package com.dstz.org.core.script; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.enums.IdentityType; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.groovy.script.api.IScript; +import com.dstz.org.api.AbGroupApiImpl; +import com.dstz.org.api.AbUserApiImpl; +import com.dstz.org.api.enums.GroupType; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.GroupManager; +import com.dstz.org.core.manager.RoleManager; +import com.google.common.collect.Sets; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.*; +import java.util.stream.Collectors; + +/** + *
+ * 描述:常用org的脚本
+ * 作者:aschs
+ * 邮箱:aschs@agilebpm.cn
+ * 日期:2019年5月26日
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +@Component +public class OrgScript implements IScript { + protected Logger LOG = LoggerFactory.getLogger(getClass()); + + @Autowired + GroupManager groupMananger; + + @Autowired + RoleManager roleManager; + + @Autowired + AbGroupApiImpl abGroupApi; + + + @Autowired + AbUserApiImpl abUserApi; + + /** + * 检查某用户是否拥有某角色 + * + * @param userId 用户ID + * @param roleCode 角色CODE + * @return + */ + public boolean checkUserHasRole(String userId, String roleCode) { + if (StrUtil.isEmpty(userId)) { + userId = UserContextUtils.getUserId(); + } + + List roleList = abGroupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), userId); + for (IGroup group : roleList) { + if (group.getGroupCode().equals(roleCode)) { + return true; + } + } + + return false; + } + + /** + * 获取角色的Level + * + * @param userId + * @return + */ + public Integer getUserRoleLevel(String userId) { + if (StrUtil.isEmpty(userId)) { + userId = UserContextUtils.getUserId(); + } + Integer level = -1; + List roleList = abGroupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), userId); + for (IGroup group : roleList) { + if (group.getGroupLevel() != null && group.getGroupLevel() > level) { + level = group.getGroupLevel(); + } + } + + return level; + } + + public Integer getOrgLevel(String orgId) { + if (StrUtil.isEmpty(orgId)) { + orgId = UserContextUtils.getGroupId(); + } + + IGroup org = abGroupApi.getByGroupId(GroupType.ORG.getType(), orgId); + if (org == null) + return -1; + + return org.getGroupLevel(); + } + + public List getOrgChildrend() { + String path = UserContextUtils.getGroup().get().getAttrValue("path", String.class); + if (StrUtil.isEmpty(path)) return Collections.emptyList(); + + List groups = groupMananger.getChildByPathAndType(path, null); + List groupIds = groups.stream().map(Group::getId).collect(Collectors.toList()); + return groupIds; + } + + /** + *
+     * 获取某个岗位下的某些角色的人员列表
+     * 
+ * + * @param groupIds 组织id,多个以“,”分隔;为空,则取当前用户所在主组织 + * @param roleCodes 角色编码,多个以“,”分隔 + * @return + */ + public Set getPostUserByGroupAndRole(String groupIds, String roleCodes) { + if (StrUtil.isEmpty(groupIds)) { + if (!UserContextUtils.getGroup().isPresent()) { + throw new BusinessMessage(GlobalApiCodes.PARAMETER_INVALID.formatDefaultMessage("请为当前人员分配组织")); + } + groupIds = UserContextUtils.getGroupId(); + } + List roleIdList = ListUtil.toList(IterUtil.trans(abGroupApi.getByGroupCodes(GroupType.ROLE.getType(), StrUtil.split(roleCodes, StrPool.COMMA)), IGroup::getGroupId)); + List postIds = new ArrayList<>(); + for (String groupId : groupIds.split(StrPool.COMMA)) { + CollUtil.forEach(roleIdList, (roleId, index) -> postIds.add(String.format(StrPool.FORMATSTR, groupId, roleId))); + } + return Sets.newHashSet(IterUtil.trans(abUserApi.getByGroupTypeAndGroupIds(GroupType.POST.getType(), postIds), SysIdentity::new)); + } + + /** + *
+     * 获取某个组织下的某些角色的人员列表
+     * 
+ * + * @param groupIds 组织id,多个以“,”分隔;为空,则取当前用户所在主组织 + * @param roleCodes 角色编码,多个以“,”分隔 + * @return + */ + public Set getSisByGroupAndRole(String groupIds, String roleCodes) { + Set identities = new HashSet<>(); + if (StrUtil.isEmpty(groupIds)) { + if (!UserContextUtils.getGroup().isPresent()) { + throw new BusinessMessage(GlobalApiCodes.PARAMETER_INVALID.formatDefaultMessage("请为当前人员分配组织")); + } + groupIds = UserContextUtils.getGroupId(); + } + for (String gi : groupIds.split(StrPool.COMMA)) { + List users = CollUtil.newArrayList( + abUserApi.getByGroupTypeAndGroupIds(GroupType.ORG.getType(), Collections.singleton(gi))); + users.forEach(user -> { + List roles = abGroupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), user.getUserId()); + roles.forEach(role -> { + if (Arrays.asList(roleCodes.split(StrPool.COMMA)).contains(role.getGroupCode())) { + SysIdentity identity = new SysIdentity(user.getUserId(), user.getFullName(), IdentityType.USER.getKey()); + identities.add(identity); + } + }); + }); + } + + return identities; + } + + /** + * 获取指定组织上级的人员 + * + * @param groupId + * @param level + * @return + */ + public IGroup getSpecificSuperOrg(String groupId, int level) { + if (StrUtil.isEmpty(groupId)) { + groupId = UserContextUtils.getGroupId(); + } + + IGroup group = abGroupApi.getByGroupId(GroupType.ORG.getType(), groupId); + if (group != null && group.getGroupLevel() != null && level == group.getGroupLevel()) { + return group; + } + + return getParentByType(group, level); + } + + private IGroup getParentByType(IGroup group, int type) { + if (group == null) + return null; + + IGroup parentGroup = abGroupApi.getByGroupId(GroupType.ORG.getType(), group.getParentId()); + if (parentGroup != null && parentGroup.getGroupLevel() == type) { + return parentGroup; + } + return getParentByType(parentGroup, type); + } + + public Set getSpecificSuperOrgIdentity(String groupId, int level) { + IGroup group = this.getSpecificSuperOrg(groupId, level); + if (group == null) + return Collections.emptySet(); + + Set identities = new HashSet<>(); + SysIdentity identity = new SysIdentity(group.getGroupId(), group.getGroupName(), IdentityType.ORG.getKey()); + identities.add(identity); + return identities; + } + + /** + * 获取上级组织作为候选人 + * + * @param groupId + * @return + */ + public Set getSuperOrgIdentity(String groupId) { + if (StrUtil.isEmpty(groupId)) { + groupId = UserContextUtils.getGroupId(); + } + Set identities = new HashSet<>(); + IGroup group = abGroupApi.getByGroupId(GroupType.ORG.getType(), groupId); + if (group == null) { + return identities; + } + IGroup parent = abGroupApi.getByGroupId(GroupType.ORG.getType(), group.getParentId()); + if (parent == null) { + return identities; + } + + SysIdentity parentIdentity = new SysIdentity(parent.getGroupId(), parent.getGroupName(), IdentityType.ORG.getKey()); + identities.add(parentIdentity); + return identities; + } + + // 设置通过ID Name 构建候选人 + public Set constructeIdentityUser(String id, String name) { + if (StrUtil.isEmpty(id) || StrUtil.isEmpty(name)) { + LOG.warn("表单取候选人失败,表单字段为空 : constructeIdentityUser id:{},name:{} ", id, name); + return Collections.emptySet(); + } + + Set identities = new HashSet<>(); + SysIdentity identity = new SysIdentity(id, name, IdentityType.USER.getKey()); + identities.add(identity); + return identities; + } + + // 设置通过ID Name type 构建Identity + public Set constructeIdentity(String id, String name, String type) { + Set identities = constructeIdentityUser(id, name); + identities.forEach(identity -> { + identity.setType(type); + }); + return identities; + } + + + public Set getSpecificSuperPostIdentity(String groupId, int level, String roleCodes) { + IGroup group = this.getSpecificSuperOrg(groupId, level); + if (group == null) return Collections.emptySet(); + + List users = CollUtil.newArrayList( + abUserApi.getByGroupTypeAndGroupIds( + GroupType.ORG.getType(), Collections.singleton(group.getGroupId()) + ) + ); + Set identities = new HashSet<>(); + users.forEach(user -> { + List roles = abGroupApi.getByGroupTypeAndUserId(GroupType.ROLE.getType(), user.getUserId()); + roles.forEach(role -> { + if (roleCodes.contains(role.getGroupCode())) { + SysIdentity identity = new SysIdentity(user.getUserId(), user.getFullName(), IdentityType.USER.getKey()); + identities.add(identity); + } + }); + }); + return identities; + } + + + /** + *
+     * 根据角色条件获取角色候选组
+     * eg:
+     * 获取级别大于等于50 小于等于60:
+     * orgScript.getRoleSis("level_ >= 50 and level_ <= 60")
+     * 
+ * + * @param where + * @return + */ + public Set getRoleSis(String where) { + AbQueryFilter filter = DefaultAbQueryFilter.build(); + filter.addParam("defaultWhere", where); + List roles = roleManager.query(filter); + Set roleSis = new HashSet<>(); + roles.forEach(role -> { + SysIdentity identity = new SysIdentity(role.getId(), role.getName(), IdentityType.ROLE.getKey()); + roleSis.add(identity); + }); + + return roleSis; + } + + /** + *
+     * 根据id获取组信息
+     * 
+ * + * @param type 枚举在:SysIdentity + * @param ids + * @return + */ + public Set getSis(String type, String ids) { + Set identities = new HashSet<>(); + if (ids == null) { + return identities; + } + if (IdentityType.USER.getKey().equals(type)) {// 用户 + for (String id : ids.split(StrPool.COMMA)) { + IUser user = abUserApi.getByUserId(id); + if (user != null) { + SysIdentity identity = new SysIdentity(user.getUserId(), user.getFullName(), IdentityType.USER.getKey()); + identities.add(identity); + } + } + } else {// 组织 + for (String id : ids.split(StrPool.COMMA)) { + IGroup group = abGroupApi.getByGroupId(type, id); + if (group != null) { + SysIdentity identity = new SysIdentity(group.getGroupId(), group.getGroupName(), type); + identities.add(identity); + } + } + } + + return identities; + } + + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/BatchSaveRelationDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/BatchSaveRelationDTO.java new file mode 100644 index 00000000..2cb92ae0 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/BatchSaveRelationDTO.java @@ -0,0 +1,34 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + +public class BatchSaveRelationDTO implements java.io.Serializable { + + /** + * 组ID + */ + @NotBlank(message = "组织不能为空") + private String groupIds; + + /** + * 角色ID + */ + @NotBlank(message = "角色不能为空") + private String roleIds; + + public String getGroupIds() { + return groupIds; + } + + public void setGroupIds(String groupIds) { + this.groupIds = groupIds; + } + + public String getRoleIds() { + return roleIds; + } + + public void setRoleIds(String roleIds) { + this.roleIds = roleIds; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/OrgRelationDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/OrgRelationDTO.java new file mode 100644 index 00000000..07540178 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/OrgRelationDTO.java @@ -0,0 +1,206 @@ +package com.dstz.org.dto; + +import com.dstz.base.common.constats.NumberPool; + +import java.util.Date; + +public class OrgRelationDTO implements java.io.Serializable { + + /** + * ID + */ + private String id; + + /** + * 组ID + */ + private String groupId; + + /** + * 角色ID + */ + private String userId; + + /** + * 0:默认组织,1:从组织 + */ + private Integer isMaster = NumberPool.INTEGER_ZERO; + + /** + * 角色ID + */ + private String roleId; + + + /** + * 状态:1启用,0禁用 + */ + private Integer status = NumberPool.INTEGER_ONE; + + /** + * 类型:groupUser,groupRole,userRole,groupUserRole + */ + private String type; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 创建人ID + */ + private String createBy; + + /** + * 所属组织 + */ + private String createOrgId; + + /** + * 更新时间 + */ + private Date updateTime; + + /** + * 更新人 + */ + private String updater; + + /** + * 更新人ID + */ + private String updateBy; + + + /** + * 前端字段 + */ + + private String[] roleIds; + + private String[] userIds; + + public OrgRelationDTO() { + } + + public String[] getRoleIds() { + return roleIds; + } + + public void setRoleIds(String[] roleIds) { + this.roleIds = roleIds; + } + + public String[] getUserIds() { + return userIds; + } + + public void setUserIds(String[] userIds) { + this.userIds = userIds; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public Integer getIsMaster() { + return isMaster; + } + + public void setIsMaster(Integer isMaster) { + this.isMaster = isMaster; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdater() { + return updater; + } + + public void setUpdater(String updater) { + this.updater = updater; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/RemoveCheckRelationDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/RemoveCheckRelationDTO.java new file mode 100644 index 00000000..2681fcd2 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/RemoveCheckRelationDTO.java @@ -0,0 +1,38 @@ +package com.dstz.org.dto; + +public class RemoveCheckRelationDTO implements java.io.Serializable { + + + /** + * 组ID + */ + private String groupId; + /** + * 角色ID + */ + private String roleId; + + public RemoveCheckRelationDTO() { + } + + public RemoveCheckRelationDTO(String groupId, String roleId) { + this.groupId = groupId; + this.roleId = roleId; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupDTO.java new file mode 100644 index 00000000..4ce6179f --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupDTO.java @@ -0,0 +1,125 @@ +package com.dstz.org.dto; + + +import javax.validation.constraints.NotBlank; +import java.util.List; + + +public class SaveGroupDTO implements java.io.Serializable { + + private String id; + + /** + * 名字 + */ + @NotBlank(message = "组织名称不能为空") + private String name; + + /** + * 父ID + */ + private String parentId; + + /** + * 排序 + */ + + private Integer sn; + + @NotBlank(message = "组织编码不能为空") + private String code; + + /** + * 类型:0集团,1公司,3部门 + */ + private String type; + + /** + * 描述 + */ + private String desc; + + /** + * 组织路径 + */ + private String path; + + + /** + * 岗位 + */ + private List orgRelationList; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public List getOrgRelationList() { + return orgRelationList; + } + + public void setOrgRelationList(List orgRelationList) { + this.orgRelationList = orgRelationList; + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupUserRelDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupUserRelDTO.java new file mode 100644 index 00000000..33de1f15 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveGroupUserRelDTO.java @@ -0,0 +1,39 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +public class SaveGroupUserRelDTO implements java.io.Serializable { + + @NotBlank(message = "参数不能为空") + private String groupId; + + private String[] roleIds; + + @NotEmpty(message = "请选择用户!") + private String[] userIds; + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String[] getRoleIds() { + return roleIds; + } + + public void setRoleIds(String[] roleIds) { + this.roleIds = roleIds; + } + + public String[] getUserIds() { + return userIds; + } + + public void setUserIds(String[] userIds) { + this.userIds = userIds; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserDTO.java new file mode 100644 index 00000000..7a8dcbb5 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserDTO.java @@ -0,0 +1,198 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * requestDto 保存用户信息 + */ +public class SaveOrgUserDTO implements java.io.Serializable { + + private String id; + + /** + * 头像 + */ + private String photo; + + /** + * 签名 + */ + private String signature; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空!") + private String fullname; + + /** + * 账号 + */ + @NotBlank(message = "账号不能为空!") + private String account; + + /** + * 密码 + */ + private String password; + + /** + * 邮箱 + */ + private String email; + + /** + * 手机 + */ + private String mobile; + + /** + * 微信 + */ + private String weixin; + + /** + * 性别 + */ + private String sex; + + /** + * 状态 + */ + private Integer status; + + /** + * 地址 + */ + private String address; + + /** + * 用户关系 + */ + private List orgRelationList; + + + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getPassword() { + return password; + } + + public void setPassword(String password) { + this.password = password; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getWeixin() { + return weixin; + } + + public void setWeixin(String weixin) { + this.weixin = weixin; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public List getOrgRelationList() { + return orgRelationList; + } + + public void setOrgRelationList(List orgRelationList) { + this.orgRelationList = orgRelationList; + } + + @Override + public String toString() { + return "SaveOrgUserDTO{" + + "id='" + id + '\'' + + ", photo='" + photo + '\'' + + ", signature='" + signature + '\'' + + ", fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", password='" + password + '\'' + + ", email='" + email + '\'' + + ", mobile='" + mobile + '\'' + + ", weixin='" + weixin + '\'' + + ", sex='" + sex + '\'' + + ", status=" + status + + ", address='" + address + '\'' + + ", orgRelationList=" + orgRelationList + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserInfoDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserInfoDTO.java new file mode 100644 index 00000000..342555da --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveOrgUserInfoDTO.java @@ -0,0 +1,156 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + + +/** + * requestDto 保存用户信息 + */ +public class SaveOrgUserInfoDTO implements java.io.Serializable { + + private String id; + + /** + * 头像 + */ + private String photo; + + /** + * 签名 + */ + private String signature; + + /** + * 姓名 + */ + @NotBlank(message = "姓名不能为空!") + private String fullname; + + /** + * 账号 + */ + @NotBlank(message = "账号不能为空!") + private String account; + + /** + * 邮箱 + */ + private String email; + + /** + * 手机 + */ + private String mobile; + + /** + * 微信 + */ + private String weixin; + + /** + * 性别 + */ + private String sex; + + /** + * 地址 + */ + private String address; + + + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getWeixin() { + return weixin; + } + + public void setWeixin(String weixin) { + this.weixin = weixin; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + @Override + public String toString() { + return "SaveOrgUserDTO{" + + "id='" + id + '\'' + + ", photo='" + photo + '\'' + + ", signature='" + signature + '\'' + + ", fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", email='" + email + '\'' + + ", mobile='" + mobile + '\'' + + ", weixin='" + weixin + '\'' + + ", sex='" + sex + '\'' + + ", address='" + address + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleDTO.java new file mode 100644 index 00000000..272b8484 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleDTO.java @@ -0,0 +1,110 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + +public class SaveRoleDTO implements java.io.Serializable { + + private String id; + + /** + * 角色名称 + */ + @NotBlank(message = "名称不能为空!") + private String name; + + /** + * 编码 + */ + @NotBlank(message = "编码不能为空!") + private String code; + + /** + * 0:禁用,1:启用 + */ + private Integer enabled; + + /** + * 角色等级 + */ + private Integer level; + + /** + * 描述 + */ + private String desc; + + /** + * 分类字典编码 + */ + @NotBlank(message = "分组不能为空!") + private String typeCode; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public Integer getEnabled() { + return enabled; + } + + public void setEnabled(Integer enabled) { + this.enabled = enabled; + } + + public Integer getLevel() { + return level; + } + + public void setLevel(Integer level) { + this.level = level; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + @Override + public String toString() { + return "SaveRoleDTO{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", code='" + code + '\'' + + ", enabled=" + enabled + + ", level=" + level + + ", desc='" + desc + '\'' + + ", typeCode='" + typeCode + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleUsersDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleUsersDTO.java new file mode 100644 index 00000000..e5236efa --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SaveRoleUsersDTO.java @@ -0,0 +1,32 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotEmpty; + +public class SaveRoleUsersDTO implements java.io.Serializable { + + /** + * 角色ID + */ + @NotBlank(message = "参数不能为空!") + private String roleId; + + @NotEmpty(message = "请选择用户!") + private String[] userIds; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String[] getUserIds() { + return userIds; + } + + public void setUserIds(String[] userIds) { + this.userIds = userIds; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SendCaptchaEmailDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SendCaptchaEmailDTO.java new file mode 100644 index 00000000..6790f7da --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SendCaptchaEmailDTO.java @@ -0,0 +1,44 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + +public class SendCaptchaEmailDTO implements java.io.Serializable { + + /** + * 账户 + */ + @NotBlank(message = "账户不能为空!") + private String account; + + + /** + * 邮箱 + */ + @NotBlank(message = "邮箱不能为空!") + private String email; + + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + @Override + public String toString() { + return "SendCaptchaEmailDTO{" + + "account='" + account + '\'' + + ", email='" + email + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SetPwdByEmailDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SetPwdByEmailDTO.java new file mode 100644 index 00000000..87fb39a3 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/SetPwdByEmailDTO.java @@ -0,0 +1,72 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + +public class SetPwdByEmailDTO { + /** + * 账户 + */ + @NotBlank(message = "账户不能为空!") + private String account; + + /** + * 新密码 + */ + @NotBlank(message = "密码不能为空!") + private String newPassword; + + /** + * 验证码 + */ + @NotBlank(message = "验证码不能为空!") + private String captcha; + + /** + * 确认密码 + */ + @NotBlank(message = "确认密码不能为空!") + private String confirmPassword; + + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } + + public String getCaptcha() { + return captcha; + } + + public void setCaptcha(String captcha) { + this.captcha = captcha; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + @Override + public String toString() { + return "SetPwdByEmailDTO{" + + "account='" + account + '\'' + + ", newPassword='" + newPassword + '\'' + + ", captcha='" + captcha + '\'' + + ", confirmPassword='" + confirmPassword + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/UpdateUserPassWorldDTO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/UpdateUserPassWorldDTO.java new file mode 100644 index 00000000..ce73b9bc --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/dto/UpdateUserPassWorldDTO.java @@ -0,0 +1,87 @@ +package com.dstz.org.dto; + +import javax.validation.constraints.NotBlank; + +public class UpdateUserPassWorldDTO implements java.io.Serializable { + + /** + * 姓名 + */ + private String fullname; + + /** + * 账户 + */ + @NotBlank(message = "账户不能为空!") + private String account; + + /** + * 原密码 + */ + @NotBlank(message = "原密码不能为空!") + private String oldPassword; + + /** + * 新密码 + */ + @NotBlank(message = "新密码不能为空!") + private String newPassword; + + /** + * 确认密码 + */ + @NotBlank(message = "确认密码不能为空!") + private String confirmPassword; + + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getOldPassword() { + return oldPassword; + } + + public void setOldPassword(String oldPassword) { + this.oldPassword = oldPassword; + } + + public String getNewPassword() { + return newPassword; + } + + public void setNewPassword(String newPassword) { + this.newPassword = newPassword; + } + + public String getConfirmPassword() { + return confirmPassword; + } + + public void setConfirmPassword(String confirmPassword) { + this.confirmPassword = confirmPassword; + } + + @Override + public String toString() { + return "UpdateUserPassWorldDTO{" + + "fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", oldPassword='" + oldPassword + '\'' + + ", newPassword='" + newPassword + '\'' + + ", confirmPassword='" + confirmPassword + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgMaster.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgMaster.java new file mode 100644 index 00000000..adc07027 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgMaster.java @@ -0,0 +1,35 @@ +package com.dstz.org.enums; + +public enum OrgMaster { + + // 禁用 + DEFAULT (0,"否","danger"), + + // 启用 + MASTER(1,"是","success"); + + + + private final Integer master; + private final String desc; + private String labelCss; + + OrgMaster(Integer master, String desc, String labelCss) { + this.master = master; + this.desc = desc; + this.labelCss = labelCss; + } + + public Integer getMaster() { + return master; + } + + public String getDesc() { + return desc; + } + + public String getLabelCss() { + return labelCss; + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgStatus.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgStatus.java new file mode 100644 index 00000000..3b1f4624 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/OrgStatus.java @@ -0,0 +1,32 @@ +package com.dstz.org.enums; + +public enum OrgStatus { + + // 禁用 + DISABLE(0,"禁用","danger"), + + // 启用 + ENABLE(1,"启用", "success"); + + private final Integer status; + private final String desc; + private String labelCss; + + OrgStatus(Integer status, String desc, String labelCss) { + this.status = status; + this.desc = desc; + this.labelCss = labelCss; + } + + public String getLabelCss() { + return labelCss; + } + + public Integer getStatus() { + return status; + } + + public String getDesc() { + return desc; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/RelationTypeConstant.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/RelationTypeConstant.java new file mode 100644 index 00000000..0bb82d89 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/enums/RelationTypeConstant.java @@ -0,0 +1,72 @@ +package com.dstz.org.enums; + +import com.dstz.org.api.enums.GroupType; + +/** + * 组织级别 + * + * @author jeff + */ +public enum RelationTypeConstant { + + /** + * 用户与组 + */ + GROUP_USER("groupUser", "用户与组"), + + /** + * 岗位 + */ + POST("groupRole", "岗位"), + + /** + * 用户与角色 + */ + USER_ROLE("userRole", "用户与角色"), + + /** + * 岗位用户 + */ + POST_USER("groupUserRole", "岗位用户"); + + private final String key; + private final String label; + + RelationTypeConstant(String key, String label) { + this.key = key; + this.label = label; + } + + public String label() { + return label; + } + + public String getLabel() { + return label; + } + + public String getKey() { + return key; + } + + + /** + * 通过组类型转换成与用户的关系类型 + * + * @param groupType 组类型 + * @return 关联类型 + */ + public static RelationTypeConstant getUserRelationTypeByGroupType(String groupType) { + switch (GroupType.fromType(groupType)) { + case ORG: + return RelationTypeConstant.GROUP_USER; + case POST: + return RelationTypeConstant.POST_USER; + case ROLE: + return RelationTypeConstant.USER_ROLE; + default: + return null; + } + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/GroupController.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/GroupController.java new file mode 100644 index 00000000..ebd95482 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/GroupController.java @@ -0,0 +1,92 @@ +package com.dstz.org.rest.controller; + + +import cn.hutool.core.collection.CollUtil; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.org.core.entity.Group; +import com.dstz.org.core.manager.GroupManager; +import com.dstz.org.dto.SaveGroupDTO; +import com.dstz.org.vo.GroupTreeVO; +import com.dstz.org.vo.GroupVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; +import java.util.Set; + +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + *

+ * 组织架构 前端控制器 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.ORG_SERVICE_PREFIX + "/group") +public class GroupController extends AbCrudController { + + /** + * 允许 fullname,account,roleId 作为入参传入列表页 + */ + final Set accessQueryFilters = CollUtil.newHashSet("fullname", "account", "roleId", "id", "groupId"); + @Autowired + GroupManager groupManager; + + @Override + public Set getAccessQueryFilters() { + return accessQueryFilters; + } + + /** + * 组织架构明细页面 + * + * @param id 组织id + * @return 组织 + */ + @RequestMapping(value = "getGroupVo") + public ApiResponse getGroupVo(@NotBlank(message = "参数不能为空") @RequestParam String id) { + return success(groupManager.getGroupVo(id)); + } + + /** + * 获取组织树 + * + * @return 树形组织集合 + */ + @RequestMapping(value = "getOrgTree") + public ApiResponse> getOrgTree(@RequestBody QueryParamDTO paramDTO) { + DefaultAbQueryFilter abQueryFilter = new DefaultAbQueryFilter(paramDTO); + abQueryFilter.noPage(); + return success(groupManager.getOrgTree(abQueryFilter)); + } + + /** + * 保存组织 + * + * @param saveGroupDTO 保存组织DTO + * @return ApiResponse 响应结果 + */ + @RequestMapping(value = "saveGroup") + public ApiResponse saveGroup(@Valid @RequestBody SaveGroupDTO saveGroupDTO) { + return success(groupManager.saveGroup(saveGroupDTO)); + } + + @Override + protected String getEntityDesc() { + return "组织"; + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgRelationController.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgRelationController.java new file mode 100644 index 00000000..6fe2d977 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgRelationController.java @@ -0,0 +1,185 @@ +package com.dstz.org.rest.controller; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.api.vo.PageListVO; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.events.AbUserEvent; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.org.enums.RelationTypeConstant; +import com.dstz.org.core.entity.OrgRelation; +import com.dstz.org.core.manager.OrgRelationManager; +import com.dstz.org.dto.BatchSaveRelationDTO; +import com.dstz.org.dto.RemoveCheckRelationDTO; +import com.dstz.org.dto.SaveGroupUserRelDTO; +import com.dstz.org.dto.SaveRoleUsersDTO; +import com.dstz.org.vo.OrgRelationUserVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; +import java.util.Set; + +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + *

+ * 前端控制器 + *

+ * + * @author xz + * @since 2022-02-09 + */ +@RestController +@RequestMapping(AbAppRestConstant.ORG_SERVICE_PREFIX + "/orgRelation") +public class OrgRelationController extends AbCrudController { + + /** + * 允许groupId,roleId 作为入参传入列表页 + */ + final Set accessQueryFilters = CollUtil.newHashSet("groupId", "roleId"); + @Autowired + OrgRelationManager orgRelationManager; + + @Override + protected String getEntityDesc() { + return "用户组织关系"; + } + + /** + * 查询 组织用户 + * + * @param queryParamDto 查询参数 + * @return 组织用户集合 + */ + @RequestMapping(value = "queryGroupUser") + public ApiResponse> queryGroupUser(@Valid @RequestBody QueryParamDTO queryParamDto) { + //查询 岗位 和 用户组的关系 + AbQueryFilter filter = new DefaultAbQueryFilter(queryParamDto) + .addFilter("tgroup.id", queryParamDto.getQueryParam().get("groupId"), ConditionType.EQUAL) + .addFilter("relation.type", RelationTypeConstant.POST.getKey(), ConditionType.NOT_EQUAL); + + return success(orgRelationManager.queryGroupUser(filter)); + } + + /** + * 设置主组织 + * + * @param id 岗位id + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "setMaster") + public ApiResponse setMaster(@NotBlank(message = "参数不能为空") @RequestParam(name = "id") String id) { + return success(() -> orgRelationManager.setMaster(id)); + } + + /** + * 修改状态 + * + * @param id 岗位id + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "updateStatus") + public ApiResponse updateStatus(@NotBlank(message = "参数不能为空") @RequestParam(name = "id") String id) { + String str = orgRelationManager.changeStatus(id); + if (StrUtil.equals(StrPool.TRUE,str)){ + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + } + return success(); + } + + /** + * 获取组织岗位 + * + * @param groupId 组织id + * @return 组织角色集合 + */ + @RequestMapping(value = "getGroupPost") + public ApiResponse> getGroupPost(@NotBlank(message = "参数不能为空") @RequestParam(name = "groupId") String groupId) { + List orgRelations = orgRelationManager.getGroupPost(groupId); + return success(orgRelations); + } + + /** + * 保存组织岗位 + * + * @param saveGroupUserRelDTO 保存岗位DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "saveGroupUserRel") + public ApiResponse saveGroupUserRel(@Valid @RequestBody SaveGroupUserRelDTO saveGroupUserRelDTO) { + return success(() -> orgRelationManager.saveGroupUserRel(saveGroupUserRelDTO)); + } + + /** + * 保存角色用户 + * + * @param saveRoleUsersDTO 保存角色DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping("saveRoleUsers") + public ApiResponse saveRoleUsers(@Valid @RequestBody SaveRoleUsersDTO saveRoleUsersDTO) { + String desc = String.format("%d条用户角色添加成功", orgRelationManager.saveRoleUsers(saveRoleUsersDTO)); + return success(desc); + } + + /** + * 查询用户集合 + * + * @param queryParamDto 查询组用户 + * @return 用户集合 + */ + @RequestMapping(value = "roleJson") + public ApiResponse> roleJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + //查询 岗位 和 用户组的关系 + AbQueryFilter filter = new DefaultAbQueryFilter(queryParamDto) + .addFilter("role.id", queryParamDto.getQueryParam().get("roleId"), ConditionType.EQUAL) + .addFilter("relation.type", RelationTypeConstant.POST.getKey(), ConditionType.NOT_EQUAL); + return success(orgRelationManager.queryRoleUser(filter)); + } + + /** + * 删除 角色、删除组织、删除岗位前进行校验 + * 删除角色 校验 岗位、岗位人员、角色人员是否存在 + * 删除组织、 校验岗位、组织人员 + * 删除岗位 校验岗位人员 + * + * @param removeCheckRelationDTO 删除校验DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "removeRelCheck") + public ApiResponse removeRelCheck(@RequestBody RemoveCheckRelationDTO removeCheckRelationDTO) { + return success(() -> orgRelationManager.beforeRemoveRelCheck(removeCheckRelationDTO)); + } + + /** + * 批量设置组织角色关联关系 + * + * @param batchSaveRelationDTO 疲劳保存DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "batchSave") + public ApiResponse batchSave(@Valid @RequestBody BatchSaveRelationDTO batchSaveRelationDTO) { + return success(() -> orgRelationManager.batchSave(batchSaveRelationDTO)); + } + + @Override + public Set getAccessQueryFilters() { + return accessQueryFilters; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgUserController.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgUserController.java new file mode 100644 index 00000000..d39b6417 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/OrgUserController.java @@ -0,0 +1,241 @@ +package com.dstz.org.rest.controller; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.api.vo.PageListVO; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.events.AbUserEvent; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.org.core.constant.OrgStatusCode; +import com.dstz.org.core.entity.OrgUser; +import com.dstz.org.core.manager.OrgUserManager; +import com.dstz.org.dto.*; +import com.dstz.org.vo.OrgUserInfoVO; +import com.dstz.org.vo.OrgUserListJsonVO; +import com.dstz.org.vo.OrgUserVO; +import com.dstz.org.vo.ResourceUserVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import static com.dstz.base.api.vo.ApiResponse.fail; +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + *

+ * 用户表 前端控制器 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.ORG_SERVICE_PREFIX + "/user") +public class OrgUserController extends AbCrudController { + + /** + * 允许 fullname,account, status作为入参传入列表页 + */ + final Set accessQueryFilters = CollUtil.newHashSet("fullname", "account", "sex", "status", "mobile"); + @Autowired + OrgUserManager orgUserManager; + + @Override + public Set getAccessQueryFilters() { + return accessQueryFilters; + } + + /** + * 保存用户信息 + * + * @param user 保存用户DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "saveUserDto") + public ApiResponse saveUserDto(@Valid @RequestBody SaveOrgUserDTO user) { + //保存用户所有信息 + orgUserManager.saveUserDto(user); + // 当前用户修改了自己的信息,需要更新缓存数据 + if (StrUtil.equals(UserContextUtils.getUserId(), user.getId())) { + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + } + return success().withMessage("保存用户成功"); + } + + /** + * 个人中心查询个人信息接口 + * + * @return 用户信息 + */ + @RequestMapping(value = "getUserInfo") + public ApiResponse getUserInfo() { + return success(orgUserManager.getUserInfo()); + } + + @RequestMapping(value = "saveUserInfo") + public ApiResponse saveUserInfo(@Valid @RequestBody SaveOrgUserInfoDTO user) { + //保存用户所有信息 + orgUserManager.saveUserInfo(user); + // 当前用户修改了自己的信息,需要更新缓存数据 + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + return success().withMessage("保存用户成功"); + } + + /** + * 获取用户详情 + * + * @param id 用户id + * @return 用户信息 + */ + @RequestMapping(value = "getUserVO") + public ApiResponse getUserVO(@NotBlank(message = "参数不能为空") @RequestParam(name = "id") String id) { + return success(orgUserManager.getUserVO(id)); + } + + /** + * 重置密码接口 + * + * @param id 用户id + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "resetUserPassword") + public ApiResponse resetUserPassword(@NotBlank(message = "参数不能为空") @RequestParam(name = "id") String id) { + if (!UserContextUtils.isSuperAdmin()) { + return fail(OrgStatusCode.IS_SUPER_ADMIN.getCode(), OrgStatusCode.IS_SUPER_ADMIN.getMessage()); + } + orgUserManager.resetUserPassword(id); + // 当前用户修改了自己的信息,需要更新缓存数据 + if (StrUtil.equals(UserContextUtils.getUserId(), id)) { + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + } + return success().withMessage("密码已经重置为1"); + } + + /** + * 修改用户状态 + * + * @param id 用户id + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "updateUserStatus") + public ApiResponse updateUserStatus(@NotBlank(message = "参数不能为空") @RequestParam(name = "id") String id) { + checkIsDemoEnvironment(); + orgUserManager.updateUserStatus(id); + // 当前用户修改了自己的信息,需要更新缓存数据 + if (StrUtil.equals(UserContextUtils.getUserId(), id)) { + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + } + return success(); + } + + /** + * 用户更新密码 + * + * @param updateUserPassWorldDTO 更新密码DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "updateUserPassWorld") + public ApiResponse updateUserPassWorld(@Valid @RequestBody UpdateUserPassWorldDTO updateUserPassWorldDTO) { + checkIsDemoEnvironment(); + orgUserManager.updateUserPassWorld(updateUserPassWorldDTO); + // 当前用户修改了自己的信息,需要更新缓存数据 + SpringUtil.publishEvent(new AbUserEvent(CollectionUtil.newArrayList(UserContextUtils.getAccount()), AbUserEvent.EventType.UPDATE_USER)); + return success(); + } + + /** + * 获取密码校验正则 + * + * @return 密码检验规则 + */ + @RequestMapping(value = "getPwdCheckRule") + public ApiResponse> getPwdCheckRule() { + return success(Collections.singletonMap(PropertyEnum.PWD_CHECK_RULE_KEY.getPropertyValue(String.class), PropertyEnum.PWD_CHECK_RULE_TXT.getPropertyValue(String.class))); + } + + /** + * 修改密码是否退出登录 + * + * @return Boolean值 + */ + @RequestMapping(value = "updatePwdIsLogOut") + public ApiResponse updatePwdIsLogOut() { + return success(PropertyEnum.CHANGE_PWD_iS_LOG_OUT.getPropertyValue(Boolean.class) || PropertyEnum.CHANGE_PWD_iS_Exit_SYSTEM.getPropertyValue(Boolean.class)); + } + + + public void removeCheck(List ids) { + List accounts = StrUtil.split(PropertyEnum.ADMIN_ACCOUNTS.getPropertyValue(String.class), CharUtil.COMMA); + for (String userId : ids) { + OrgUserVO userVO = orgUserManager.getUserVO(userId); + if (accounts.contains(userVO.getAccount())) { + throw new BusinessMessage(OrgStatusCode.NOT_ALLOW_DELETE_ADMIN); + } + } + } + + + /** + * 用户权限查询接口 + * + * @param queryParamDto 查询参数DTO + * @return 用户权限查询结果 + */ + @RequestMapping(value = "getUserByResource") + public ApiResponse> getUserByResource(@Valid @RequestBody QueryParamDTO queryParamDto) { + AbQueryFilter queryFilter = new DefaultAbQueryFilter(queryParamDto); + PageListDTO sysResourceList = orgUserManager.getUserByResource(queryFilter); + return success(sysResourceList); + } + + /** + * 过邮箱找回密码方法:发送验证码 + * + * @param sendCaptchaEmailDTO 发送验证码DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "sendCaptchaEmail") + public ApiResponse sendCaptchaEmail(@Valid @RequestBody SendCaptchaEmailDTO sendCaptchaEmailDTO) { + return success(() -> orgUserManager.sendCaptchaEmail(sendCaptchaEmailDTO)); + } + + /** + * 通过邮箱找回密码方法:重置密码 + * + * @param setPwdByEmailDTO 设置密码DTO + * @return ApiResponse 响应对象 + */ + @RequestMapping(value = "setPwdByEmail") + public ApiResponse setPwdByEmail(@Valid @RequestBody SetPwdByEmailDTO setPwdByEmailDTO) { + return success(() -> orgUserManager.setPwdByEmail(setPwdByEmailDTO)); + } + + @Override + @RequestMapping("listJson") + public ApiResponse listJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + PageListDTO pageList = orgUserManager.queryUserList(new DefaultAbQueryFilter(queryParamDto, getAccessQueryFilters())); + return ApiResponse.success(pageList); + } + + @Override + protected String getEntityDesc() { + return "用户"; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/RoleController.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/RoleController.java new file mode 100644 index 00000000..d577da95 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/rest/controller/RoleController.java @@ -0,0 +1,70 @@ +package com.dstz.org.rest.controller; + + +import cn.hutool.core.collection.CollUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.org.core.entity.Role; +import com.dstz.org.core.manager.RoleManager; +import com.dstz.org.dto.SaveRoleDTO; +import com.dstz.org.vo.ResourceRoleVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; +import java.util.Set; + +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + *

+ * 角色管理 前端控制器 + *

+ * + * @author xz + * @since 2022-02-07 + */ +@RestController +@RequestMapping(AbAppRestConstant.ORG_SERVICE_PREFIX + "/role") +public class RoleController extends AbCrudController { + + + /** + * 允许 code,enabled,name,typeCode 作为入参传入列表页 + */ + final Set accessQueryFilters = CollUtil.newHashSet("code", "enabled", "name", "typeCode"); + @Autowired + RoleManager roleManager; + + @Override + protected String getEntityDesc() { + return "角色"; + } + + @RequestMapping(value = "saveRoleDto") + public ApiResponse saveRoleDto(@Valid @RequestBody SaveRoleDTO saveRoleDTO) { + return success(String.format( roleManager.saveRoleDto(saveRoleDTO), getEntityDesc())); + } + + /** + * 根据资源获取角色集合 + * + * @param resourcesId 资源id + * @return 角色集合 + */ + @RequestMapping("getRoleListByResource") + public ApiResponse> getRoleListByResource(@RequestParam @NotBlank(message = "参数不能为空") String resourcesId) { + return success(roleManager.getRoleListByResource(resourcesId)); + } + + @Override + public Set getAccessQueryFilters() { + return accessQueryFilters; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupTreeVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupTreeVO.java new file mode 100644 index 00000000..1ca844db --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupTreeVO.java @@ -0,0 +1,126 @@ +package com.dstz.org.vo; + + +import com.dstz.base.api.model.Tree; + +import java.util.List; + +public class GroupTreeVO implements Tree, java.io.Serializable { + + private String id; + /** + * 名字 + */ + private String name; + /** + * 父ID + */ + private String parentId; + /** + * 排序 + */ + private Integer sn; + private String code; + /** + * 组织路径 + */ + private String path; + /** + * 组织路径 + */ + private String pathName; + /** + * 上级组织名称 + */ + private String parentName; + /** + * 组织下的子组织 + */ + private List children; + + public GroupTreeVO(String id, String name) { + this.id = id; + this.name = name; + } + + public GroupTreeVO() { + } + + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getPathName() { + return pathName; + } + + public void setPathName(String pathName) { + this.pathName = pathName; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + @Override + public List getChildren() { + return children; + } + + @Override + public void setChildren(List list) { + this.children = list; + } + + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupUserCountVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupUserCountVO.java new file mode 100644 index 00000000..18bedbf3 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupUserCountVO.java @@ -0,0 +1,27 @@ +package com.dstz.org.vo; + +public class GroupUserCountVO { + + private String id; + + /** + * 组织用户数量 + */ + private Integer userCount; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Integer getUserCount() { + return userCount; + } + + public void setUserCount(Integer userCount) { + this.userCount = userCount; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupVO.java new file mode 100644 index 00000000..4c642232 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/GroupVO.java @@ -0,0 +1,149 @@ +package com.dstz.org.vo; + + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.api.enums.GroupGradeConstant; + +import java.util.List; + +public class GroupVO implements java.io.Serializable { + + private String id; + + /** + * 名字 + */ + private String name; + + /** + * 父ID + */ + private String parentId; + + /** + * 排序 + */ + private Integer sn; + + private String code; + + /** + * 类型:0集团,1公司,3部门 + */ + private String type; + + /** + * 描述 + */ + private String desc; + + /** + * 组织路径 + */ + private String path; + + /** + * 组织路径 + */ + private String pathName; + + /** + * 上级组织名称 + */ + private String parentName; + + /** + * 岗位集合 + */ + private List orgRelationList; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getPathName() { + return pathName; + } + + public void setPathName(String pathName) { + this.pathName = pathName; + } + + public String getParentName() { + return parentName; + } + + public void setParentName(String parentName) { + this.parentName = parentName; + } + + public List getOrgRelationList() { + return orgRelationList; + } + + public void setOrgRelationList(List orgRelationList) { + this.orgRelationList = orgRelationList; + } + +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgPostVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgPostVO.java new file mode 100644 index 00000000..259203f0 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgPostVO.java @@ -0,0 +1,93 @@ +package com.dstz.org.vo; + +import cn.hutool.core.text.StrPool; +import cn.hutool.core.util.StrUtil; +import com.dstz.org.core.entity.Role; + +/** + * 组织岗位VO + * + * @author wacxhs + */ +public class OrgPostVO { + + private String roleId; + + private String roleCode; + + private String roleName; + + private String groupId; + + private String groupCode; + + private String groupName; + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + public String getRoleCode() { + return roleCode; + } + + public void setRoleCode(String roleCode) { + this.roleCode = roleCode; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getGroupCode() { + return groupCode; + } + + public void setGroupCode(String groupCode) { + this.groupCode = groupCode; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + @Override + public String toString() { + return "OrgPostVO{" + + "roleId='" + roleId + '\'' + + ", roleCode='" + roleCode + '\'' + + ", roleName='" + roleName + '\'' + + ", groupId='" + groupId + '\'' + + ", groupCode='" + groupCode + '\'' + + ", groupName='" + groupName + '\'' + + '}'; + } + + public Role toRole() { + Role role = new Role(); + role.setId(StrUtil.join(StrPool.UNDERLINE, getGroupId(), getRoleId())); + role.setCode(StrUtil.join(StrPool.UNDERLINE, getGroupCode(), getRoleCode())); + role.setName(StrUtil.join(StrPool.DASHED, getGroupName(), getRoleName())); + return role; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgRelationUserVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgRelationUserVO.java new file mode 100644 index 00000000..a54b955e --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgRelationUserVO.java @@ -0,0 +1,243 @@ +package com.dstz.org.vo; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.enums.OrgMaster; +import com.dstz.org.enums.OrgStatus; +import com.dstz.org.enums.RelationTypeConstant; + +import java.util.Date; + +/** + * 关联用户 + * + * @author wacxhs + */ +public class OrgRelationUserVO implements java.io.Serializable { + + /** + * 关联ID + */ + private String id; + + /** + * 关联类型 {@link RelationTypeConstant} + */ + private String type; + + /** + * 组ID + */ + private String groupId; + + /** + * 组名称 + */ + private String groupName; + + /** + * 0:默认组织,1:从组织 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgMaster.class, matchField = "master", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "isMasterDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "isMasterCss") + }) + private Integer isMaster; + + /** + * 1:启用,0:禁用 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + private Integer status; + + /** + * 用户ID + */ + private String userId; + + /** + * 用户账户 + */ + private String userAccount; + + /** + * 用户姓名 + */ + private String userFullname; + + + /** + * 角色名 + */ + private String roleName; + + /** + * 角色编码 + */ + private String roleCode; + + /** + * 角色ID + */ + private String roleId; + + /** + * 创建时间 + */ + private Date createTime; + + /** + * 获取岗位名称 + * + * @return 岗位名称 + */ + public String getPostName() { + if (StrUtil.isNotEmpty(groupName) && StrUtil.isNotEmpty(getRoleName())) { + return String.format("%s-%s", getGroupName(), getRoleName()); + } else { + return StrUtil.EMPTY; + } + } + + /** + * 获取岗位ID + * + * @return 岗位ID + */ + public String getPostId() { + if (StrUtil.isNotEmpty(getGroupId()) && StrUtil.isNotEmpty(getRoleId())) { + return String.format(StrPool.FORMATSTR, getGroupId(), getRoleId()); + } else { + return StrUtil.EMPTY; + } + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getGroupId() { + return groupId; + } + + public void setGroupId(String groupId) { + this.groupId = groupId; + } + + public String getGroupName() { + return groupName; + } + + public void setGroupName(String groupName) { + this.groupName = groupName; + } + + public Integer getIsMaster() { + return isMaster; + } + + public void setIsMaster(Integer isMaster) { + this.isMaster = isMaster; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserAccount() { + return userAccount; + } + + public void setUserAccount(String userAccount) { + this.userAccount = userAccount; + } + + public String getUserFullname() { + return userFullname; + } + + public void setUserFullname(String userFullname) { + this.userFullname = userFullname; + } + + public String getRoleName() { + return roleName; + } + + public void setRoleName(String roleName) { + this.roleName = roleName; + } + + public String getRoleCode() { + return roleCode; + } + + public void setRoleCode(String roleCode) { + this.roleCode = roleCode; + } + + public String getRoleId() { + return roleId; + } + + public void setRoleId(String roleId) { + this.roleId = roleId; + } + + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String toString() { + return "OrgRelationUserVO{" + + "id='" + id + '\'' + + ", type='" + type + '\'' + + ", groupId='" + groupId + '\'' + + ", groupName='" + groupName + '\'' + + ", isMaster=" + isMaster + + ", status=" + status + + ", userId='" + userId + '\'' + + ", userAccount='" + userAccount + '\'' + + ", userFullname='" + userFullname + '\'' + + ", roleName='" + roleName + '\'' + + ", roleCode='" + roleCode + '\'' + + ", roleId='" + roleId + '\'' + + ", createTime=" + createTime + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserInfoVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserInfoVO.java new file mode 100644 index 00000000..a7ddc862 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserInfoVO.java @@ -0,0 +1,167 @@ +package com.dstz.org.vo; + +import java.util.List; + +public class OrgUserInfoVO implements java.io.Serializable { + + /** + * id + */ + private String id; + + /** + * 姓名 + */ + private String fullname; + + /** + * 账户 + */ + private String account; + + /** + * 邮箱 + */ + private String email; + + /** + * 手机号 + */ + private String mobile; + + /** + * 微信 + */ + private String weixin; + + /** + * 性别 + */ + private String sex; + + /** + * 地址 + */ + private String address; + + /** + * 头像 + */ + private String photo; + + /** + * 签名 + */ + private String signature; + + /** + * 用户关系 + */ + private List orgRelationList; + + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getWeixin() { + return weixin; + } + + public void setWeixin(String weixin) { + this.weixin = weixin; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getPhoto() { + return photo; + } + + public void setPhoto(String photo) { + this.photo = photo; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getSignature() { + return signature; + } + + public void setSignature(String signature) { + this.signature = signature; + } + + public List getOrgRelationList() { + return orgRelationList; + } + + public void setOrgRelationList(List orgRelationList) { + this.orgRelationList = orgRelationList; + } + + @Override + public String toString() { + return "OrgUserInfoVO{" + + "id='" + id + '\'' + + ", fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", email='" + email + '\'' + + ", mobile='" + mobile + '\'' + + ", weixin='" + weixin + '\'' + + ", sex='" + sex + '\'' + + ", address='" + address + '\'' + + ", photo='" + photo + '\'' + + ", signature='" + signature + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserListJsonVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserListJsonVO.java new file mode 100644 index 00000000..678eed7e --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserListJsonVO.java @@ -0,0 +1,117 @@ +package com.dstz.org.vo; + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.enums.OrgStatus; + +import java.util.Date; + + +public class OrgUserListJsonVO implements java.io.Serializable { + + private String id; + + /** + * 姓名 + */ + private String fullname; + + /** + * 账号 + */ + private String account; + + /** + * 性别:男,女,未知 + */ + private String sex; + + /** + * 邮箱 + */ + private String email; + + /** + * 手机号码 + */ + private String mobile; + + /** + * 0:禁用,1正常 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + private Integer status; + + /** + * 创建时间 + */ + private Date createTime; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserVO.java new file mode 100644 index 00000000..46125317 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/OrgUserVO.java @@ -0,0 +1,161 @@ +package com.dstz.org.vo; + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.enums.OrgStatus; + +import java.util.List; + +public class OrgUserVO implements java.io.Serializable { + + /** + * id + */ + private String id; + + /** + * 姓名 + */ + private String fullname; + + /** + * 账户 + */ + private String account; + + /** + * 邮箱 + */ + private String email; + + /** + * 手机号 + */ + private String mobile; + + /** + * 微信 + */ + private String weixin; + + /** + * 性别 + */ + private String sex; + + /** + * 状态 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + private Integer status; + + /** + * 地址 + */ + private String address; + + /** + * 用户关系 + */ + private List orgRelationList; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getEmail() { + return email; + } + + public void setEmail(String email) { + this.email = email; + } + + public String getMobile() { + return mobile; + } + + public void setMobile(String mobile) { + this.mobile = mobile; + } + + public String getWeixin() { + return weixin; + } + + public void setWeixin(String weixin) { + this.weixin = weixin; + } + + public String getAddress() { + return address; + } + + public void setAddress(String address) { + this.address = address; + } + + public String getSex() { + return sex; + } + + public void setSex(String sex) { + this.sex = sex; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + public List getOrgRelationList() { + return orgRelationList; + } + + public void setOrgRelationList(List orgRelationList) { + this.orgRelationList = orgRelationList; + } + + @Override + public String toString() { + return "OrgUserVO{" + + "id='" + id + '\'' + + ", fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", email='" + email + '\'' + + ", mobile='" + mobile + '\'' + + ", weixin='" + weixin + '\'' + + ", sex='" + sex + '\'' + + ", status=" + status + + ", orgRelationList=" + orgRelationList + + ", address='" + address + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceRoleVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceRoleVO.java new file mode 100644 index 00000000..d173cef9 --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceRoleVO.java @@ -0,0 +1,49 @@ +package com.dstz.org.vo; + +public class ResourceRoleVO { + + private String id; + + /** + * 角色名称 + */ + private String name; + + /** + * 编码 + */ + private String code; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + @Override + public String toString() { + return "ResourceRoleVO{" + + "id='" + id + '\'' + + ", name='" + name + '\'' + + ", code='" + code + '\'' + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceUserVO.java b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceUserVO.java new file mode 100644 index 00000000..9182dc8e --- /dev/null +++ b/ab-org/ab-org-core/src/main/java/com/dstz/org/vo/ResourceUserVO.java @@ -0,0 +1,75 @@ +package com.dstz.org.vo; + +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.org.enums.OrgStatus; + +public class ResourceUserVO implements java.io.Serializable { + + /** + * id + */ + private String id; + + /** + * 姓名 + */ + private String fullname; + + /** + * 账户 + */ + private String account; + + /** + * 状态 + */ + @AbValueMap(type = AbValueMapType.ENUM, fixClass = OrgStatus.class, matchField = "status", + attrMap = {@AbValueMap.AttrMap(originName = "desc", targetName = "statusDesc"), + @AbValueMap.AttrMap(originName = "labelCss", targetName = "statusCss") + }) + private Integer status; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getFullname() { + return fullname; + } + + public void setFullname(String fullname) { + this.fullname = fullname; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public Integer getStatus() { + return status; + } + + public void setStatus(Integer status) { + this.status = status; + } + + @Override + public String toString() { + return "ResourceUserVO{" + + "id='" + id + '\'' + + ", fullname='" + fullname + '\'' + + ", account='" + account + '\'' + + ", status=" + status + + '}'; + } +} diff --git a/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/GroupMapper.xml b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/GroupMapper.xml new file mode 100644 index 00000000..c4024e24 --- /dev/null +++ b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/GroupMapper.xml @@ -0,0 +1,106 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE + FROM org_group + + + + + + + + + + + + + + + diff --git a/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgRelationMapper.xml b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgRelationMapper.xml new file mode 100644 index 00000000..ce9c771b --- /dev/null +++ b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgRelationMapper.xml @@ -0,0 +1,277 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM org_relation + WHERE + user_id_=#{userId} + + + + DELETE FROM org_relation + WHERE + group_id_=#{groupId} and type_ = 'groupRole' + + + + + + + + + + + + + + + + + + + + + + DELETE FROM org_relation + WHERE type_= #{type} + + + + + + + + diff --git a/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgUserMapper.xml b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgUserMapper.xml new file mode 100644 index 00000000..80f3aa62 --- /dev/null +++ b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/OrgUserMapper.xml @@ -0,0 +1,215 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + update org_user + + + fullname_ = #{fullname,jdbcType=VARCHAR}, + + + account_ = #{account,jdbcType=VARCHAR}, + + + password_ = #{password,jdbcType=VARCHAR}, + + + email_ = #{email,jdbcType=VARCHAR}, + + + mobile_ = #{mobile,jdbcType=VARCHAR}, + + + weixin_ = #{weixin,jdbcType=VARCHAR}, + + + address_ = #{address,jdbcType=VARCHAR}, + + + photo_ = #{photo,jdbcType=VARCHAR}, + + + sex_ = #{sex,jdbcType=VARCHAR}, + + + signature_ = #{signature,jdbcType=VARCHAR}, + + + from_ = #{from,jdbcType=VARCHAR}, + + + status_ = #{status,jdbcType=INTEGER}, + + + openid_ = #{openid,jdbcType=VARCHAR}, + + + expire_data_=#{expireData,jdbcType=TIMESTAMP}, + + + create_time_ = #{createTime,jdbcType=TIMESTAMP}, + + + create_by_ = #{createBy,jdbcType=VARCHAR}, + + + create_org_id_ = #{createOrgId,jdbcType=VARCHAR}, + + + update_time_ = #{updateTime,jdbcType=TIMESTAMP}, + + + updater_ = #{updater,jdbcType=VARCHAR}, + + + update_by_ = #{updateBy,jdbcType=VARCHAR}, + + + where id_ = #{id,jdbcType=VARCHAR} + + + + + + + diff --git a/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/RoleMapper.xml b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/RoleMapper.xml new file mode 100644 index 00000000..d6888b32 --- /dev/null +++ b/ab-org/ab-org-core/src/main/resources/com/dstz/org/core/mapper/RoleMapper.xml @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-org/pom.xml b/ab-org/pom.xml new file mode 100644 index 00000000..fdbb5f64 --- /dev/null +++ b/ab-org/pom.xml @@ -0,0 +1,19 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-org + pom + + + ab-org-api + ab-org-core + + diff --git a/ab-spring-boot/ab-spring-boot-app/pom.xml b/ab-spring-boot/ab-spring-boot-app/pom.xml new file mode 100644 index 00000000..1ad1e5a3 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-app/pom.xml @@ -0,0 +1,148 @@ + + + + ab-spring-boot + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot-app + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-validation + + + org.springframework.boot + spring-boot-actuator + + + com.baomidou + mybatis-plus-boot-starter + + + mysql + mysql-connector-java + + + com.dstz + ab-demo-core + ${project.version} + + + com.dstz + ab-groovy-script-engine + ${project.version} + + + com.dstz + ab-spring-boot-starter + ${project.version} + + + com.dstz + ab-spring-boot-starter-security + ${project.version} + + + com.dstz + ab-spring-boot-wf-starter + ${project.version} + + + com.dstz + ab-spring-boot-biz-starter + ${project.version} + + + com.dstz + ab-sys-core + ${project.version} + + + com.dstz + ab-component-upload-engine + ${project.version} + + + com.dstz + ab-component-mq-engine + ${project.version} + + + com.dstz + ab-cms-core + ${project.version} + + + com.dstz + ab-component-msg-engine + ${project.version} + + + com.dstz + ab-org-core + ${project.version} + + + com.dstz + ab-auth-core + ${project.version} + + + com.dstz + ab-component-pubsub-core + ${project.version} + + + + ${project.artifactId} + + + src/main/resources + false + + **/*.yml + banner.txt + **/*.html + **/*.lic + + + + + + org.apache.maven.plugins + maven-install-plugin + + true + + + + org.apache.maven.plugins + maven-deploy-plugin + + true + + + + org.springframework.boot + spring-boot-maven-plugin + + + + repackage + + + + + + + diff --git a/ab-spring-boot/ab-spring-boot-app/src/main/java/com/dstz/AbSpringBootApp.java b/ab-spring-boot/ab-spring-boot-app/src/main/java/com/dstz/AbSpringBootApp.java new file mode 100644 index 00000000..62f1d8a7 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-app/src/main/java/com/dstz/AbSpringBootApp.java @@ -0,0 +1,30 @@ +package com.dstz; + + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; +import org.springframework.scheduling.annotation.EnableAsync; +import org.springframework.web.bind.annotation.CrossOrigin; + +import cn.hutool.extra.spring.EnableSpringUtil; + +/** + *
+ * 应用程序主入口
+ * 作者:jeff
+ * 邮箱:jeff@agilebpm.cn
+ * 日期:2020年1月17日
+ * 版权: 深圳市大世同舟信息科技有限公司
+ * 
+ */ +@EnableSpringUtil +@SpringBootApplication +@EnableAsync +@CrossOrigin +public class AbSpringBootApp extends SpringBootServletInitializer { + + public static void main(String[] args) { + SpringApplication.run(AbSpringBootApp.class, args); + } +} diff --git a/ab-spring-boot/ab-spring-boot-app/src/main/resources/application.yml b/ab-spring-boot/ab-spring-boot-app/src/main/resources/application.yml new file mode 100644 index 00000000..e268208a --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-app/src/main/resources/application.yml @@ -0,0 +1,169 @@ +spring: + profiles: + active: dev + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://192.168.201.100:3306/a5_gitee?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=Asia/Shanghai + username: root + password: root + session: + store-type: none + # 会话时长(单位:秒) + timeout: 1800 + #session存储类型 none , redis,分布式下请使用Redis + redis: + namespace: ab:session + redis: + host: localhost + port: 6379 + database: 5 + password: + jackson: + date-format: yyyy-MM-dd HH:mm:ss + time-zone: GMT+8 + activiti: + database-schema-update: none + servlet: + multipart: + max-request-size: "64MB" + max-file-size: "64MB" +logging: + level: + root: INFO + com: + dstz: DEBUG + baomidou: DEBUG + org: + springframework: + jdbc: + core: DEBUG + data: + redis: ERROR + mongodb: + driver: INFO + io: + lettuce: ERROR +server: + servlet: + encoding: + charset: UTF-8 +ab: + cache: + # 缓存类型 memory:内存 + type: memory + # 缓存区域,定义各个缓存区域过期时间 + region: + # 字典 + DICT: PT12H + # 系统属性 + SYSPROPETIES: PT12H + # 流程定义 + BPM_PROCESS_DEF: PT12H + # 流程定义 + BPM_ACT_PROCESS_DEF: PT12H + # 资源相关 + SYS_RESOURCE: PT12H + # 登录相关 + LOGIN_TOKEN: PT1H + #操作日志相关 + AUDIT_LOG_META: PT12H + #消息相关 AccessToken的过期KEY 1.5h过期,提前自我过期重新获取 + MSG_REGION: PT60M + #第三方登录 + THIRD_TOKEN: PT115M + #密码验证 + PWD_TOKEN: PT1H + #业务对象 + BIZ_OBJECT: PT12H + #数据源 + SYS_DATA_SOURCE: PT12H + #oauth + OAUTH_REGION: PT12H + #自定义列表 + BIZ_CHART: PT1000H + #自定义对话框 + BIZ_CUST_DIALOG: PT12H + # 数据权限 + DATA_PRIVILEGE: PT12H + # 系统应用 + SYS_APPLICATION: PT30M + # 系统会话属性 + SYS_SESSION_ATTRIBUTE: PT15M + #鉴权 + security: + ## 忽略鉴权的URL + auth-ingores: + - /login.* + - /error + - /logout + - /index.html + - /favicon.ico + - /login/dstz + - login/* + - /tenant/resource/* + - /ab-org/auth/login* + - /ab-org/auth/login/* + - /ab-org/auth/getToken + - /ab-org/auth/refreshToken + - /ab-bpm/sys/licenceInfo + - /ab-bpm/sys/sysFile/view/* + - /ab-org/auth/quickLogin* + - /ab-bpm/sys/sysConfiguration/getConfByCode/qywx + - /ab-bpm/sys/sysFile/download* + - /ab-bpm/sys/sysFile/update + + ## 忽略防盗链的URL + csrf-ingores: + - 127.0.0.1 + - localhost + + # 最大会话数 -1:表示会话不受任何限制 0:限制除登陆用户最后一次登陆会话,其它会话都失效 大于0:限制用户下最大会话数 + maximum-sessions: -1 + + oauth: + ## token存储方案 InMemory:本地内存 ,redis + token-store-type: InMemory + ##令牌校验秘钥 + login-token-signing-key: ab22 + + simple-mq: + ## mq 队列配置:redis :使用Redis做队列;synchronous: 同步,不使用队列;jms: activemq作为队列 discard: 丢弃模式,调用JmsProducer不做任何处理 + ## http://doc.a5.tongzhouyun.com/guide/system-setting/mqMessage.html + message-queue-type: synchronous + # Rest 接口调用转发时中继的 Token 配置 + rest-client-info-transform: + headers: + - name: Authorization + # 开启定时任务 + schedule: + enable: false + +# j2cache 缓存配置 +j2cache: + level2: + # 二级缓存开启 + cache-open: false + broadcast: + # 开启广播通知 + open: ${j2cache.level2.cache-open} +xxljob: + # 调度中心部署跟地址 [选填]:如调度中心集群部署存在多个地址则用逗号分隔。执行器将会使用该地址进行"执行器心跳注册"和"任务结果回调";为空则关闭自动注册; + admin-addresses: http://192.168.1.5:8080/xxl-job-admin + # 执行器通讯TOKEN [选填]:非空时启用; + # access-token: + # 执行器AppName [选填]:执行器心跳注册分组依据;为空则关闭自动注册 + app-name: agile-bpm + # 执行器注册 [选填]:优先使用该配置作为注册地址,为空时使用内嵌服务 ”IP:PORT“ 作为注册地址。从而更灵活的支持容器类型执行器动态IP和动态映射端口问题。 + # executor-address: + # 执行器IP [选填]:默认为空表示自动获取IP,多网卡时可手动设置指定IP,该IP不会绑定Host仅作为通讯实用;地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"; + # executor-ip: + # 执行器端口号 [选填]:小于等于0则自动获取;默认端口为9999,单机部署多个执行器时,注意要配置不同执行器端口; + # executor-port: + # 执行器运行日志文件存储磁盘路径 [选填] :需要对该路径拥有读写权限;为空则使用默认路径; + executor-log-path: ${java.io.tmpdir}logs/xxl-job/jobhandler + # 执行器日志文件保存天数 [选填] : 过期日志自动清理, 限制值大于等于3时生效; 否则, 如-1, 关闭自动清理功能; +# executor-log-retention-days: 7 + +## 上传配置: db 存数据库 ;ordinary 存本地磁盘,需要配置 uploader.ordinary.path存储目录。minio 需要ab:minio相关配置 具体请看 IUploader 实现类 +uploader: + default: db diff --git a/ab-spring-boot/ab-spring-boot-app/src/main/resources/banner.txt b/ab-spring-boot/ab-spring-boot-app/src/main/resources/banner.txt new file mode 100644 index 00000000..fd2c6e81 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-app/src/main/resources/banner.txt @@ -0,0 +1,11 @@ + .----------------. .----------------. .----------------. .----------------. .----------------. .----------------. .----------------. .----------------. +| .--------------. || .--------------. || .--------------. || .--------------. || .--------------. | | .--------------. || .--------------. || .--------------. | +| | __ | || | ______ | || | _____ | || | _____ | || | _________ | | | | ______ | || | ______ | || | ____ ____ | | +| | / \ | || | .' ___ | | || | |_ _| | || | |_ _| | || | |_ ___ | | | | | |_ _ \ | || | |_ __ \ | || ||_ \ / _|| | +| | / /\ \ | || | / .' \_| | || | | | | || | | | | || | | |_ \_| | | | | | |_) | | || | | |__) | | || | | \/ | | | +| | / ____ \ | || | | | ____ | || | | | | || | | | _ | || | | _| _ | | | | | __'. | || | | ___/ | || | | |\ /| | | | +| | _/ / \ \_ | || | \ `.___] _| | || | _| |_ | || | _| |__/ | | || | _| |___/ | | | | | _| |__) | | || | _| |_ | || | _| |_\/_| |_ | | +| ||____| |____|| || | `._____.' | || | |_____| | || | |________| | || | |_________| | | | | |_______/ | || | |_____| | || ||_____||_____|| | +| | | || | | || | | || | | || | | | | | | || | | || | | | +| '--------------' || '--------------' || '--------------' || '--------------' || '--------------' | | '--------------' || '--------------' || '--------------' | + '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' '----------------' \ No newline at end of file diff --git a/ab-spring-boot/ab-spring-boot-app/src/main/resources/static/ULR表单示例 b/ab-spring-boot/ab-spring-boot-app/src/main/resources/static/ULR表单示例 new file mode 100644 index 00000000..e69de29b diff --git a/ab-spring-boot/ab-spring-boot-app/src/main/resources/static/urlFormDemo.html b/ab-spring-boot/ab-spring-boot-app/src/main/resources/static/urlFormDemo.html new file mode 100644 index 00000000..11fb6774 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-app/src/main/resources/static/urlFormDemo.html @@ -0,0 +1,84 @@ + + + + + + + + + + URL 表单 demo + + +
+ + + + + + + + + + + +
+ + + \ No newline at end of file diff --git a/ab-spring-boot/ab-spring-boot-starter-security/pom.xml b/ab-spring-boot/ab-spring-boot-starter-security/pom.xml new file mode 100644 index 00000000..f340b698 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/pom.xml @@ -0,0 +1,40 @@ + + + + ab-spring-boot + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot-starter-security + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-configuration-processor + + + com.dstz + ab-auth-spring-security-oauth2 + ${project.version} + + + org.springframework.session + spring-session-data-redis + + + + org.glassfish.jaxb + jaxb-runtime + 2.3.0-b170127.1453 + + + + \ No newline at end of file diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/ldap/AbLdapProperties.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/ldap/AbLdapProperties.java new file mode 100644 index 00000000..4236dfef --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/ldap/AbLdapProperties.java @@ -0,0 +1,91 @@ +package com.dstz.springboot.autoconfigure.ldap; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * ab ldap 属性 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.ldap") +public class AbLdapProperties { + + /** + * Ldap服务地址 + */ + private String server; + + /** + * Ldap管理域 + */ + private String managerDN; + + /** + * Ldap管理者密码 + */ + private String managerPassword; + + /** + * Ldap鉴权方式 + */ + private String authenticationType; + + /** + * Set the base search path for the query + */ + private String userSearchBase = StringUtils.EMPTY; + + /** + * 用户查询过滤,{0} 为传入账户名 + */ + private String userSearchFilter = "uid={0}"; + + public String getServer() { + return server; + } + + public void setServer(String server) { + this.server = server; + } + + public String getManagerDN() { + return managerDN; + } + + public void setManagerDN(String managerDN) { + this.managerDN = managerDN; + } + + public String getManagerPassword() { + return managerPassword; + } + + public void setManagerPassword(String managerPassword) { + this.managerPassword = managerPassword; + } + + public String getAuthenticationType() { + return authenticationType; + } + + public void setAuthenticationType(String authenticationType) { + this.authenticationType = authenticationType; + } + + public String getUserSearchBase() { + return userSearchBase; + } + + public void setUserSearchBase(String userSearchBase) { + this.userSearchBase = userSearchBase; + } + + public String getUserSearchFilter() { + return userSearchFilter; + } + + public void setUserSearchFilter(String userSearchFilter) { + this.userSearchFilter = userSearchFilter; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AbSecurityProperties.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AbSecurityProperties.java new file mode 100644 index 00000000..58d0b3f7 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AbSecurityProperties.java @@ -0,0 +1,89 @@ +package com.dstz.springboot.autoconfigure.oauth2; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +/** + * 鉴权配置 + * @author lightning + */ +@ConfigurationProperties(prefix = "ab.security") +public class AbSecurityProperties { + + /** + * 逗号分隔 + */ + + /**忽略xss 的地址 **/ + private String xssIngores = ""; + /**忽略跨域访问 的地址 **/ + private String [] csrfIngores = new String[]{"127.0.0.1"}; + /**忽略鉴权 的地址 **/ + private String [] authIngores = new String[]{"/login.*"}; + + /** + * 最大会话数 + *
    + *
  • -1:表示会话不受任何限制
  • + *
  • 0:限制除登陆用户最后一次登陆会话,其它会话都失效
  • + *
  • >0:限制用户下最大会话数
  • + *
+ */ + private Integer maximumSessions = -1; + + /** + * 启用跨域拦截 + */ + private Boolean enableCors = Boolean.TRUE; + + /** + * 启用跨站拦截 + */ + private Boolean enableCsrf = Boolean.TRUE; + + public String getXssIngores() { + return xssIngores; + } + public void setXssIngores(String xssIngores) { + this.xssIngores = xssIngores; + } + + public String[] getCsrfIngores() { + return csrfIngores; + } + + public void setCsrfIngores(String[] csrfIngores) { + this.csrfIngores = csrfIngores; + } + + public String[] getAuthIngores() { + return authIngores; + } + + public void setAuthIngores(String[] authIngores) { + this.authIngores = authIngores; + } + + public Integer getMaximumSessions() { + return maximumSessions; + } + + public void setMaximumSessions(Integer maximumSessions) { + this.maximumSessions = maximumSessions; + } + + public Boolean getEnableCors() { + return enableCors; + } + + public void setEnableCors(Boolean enableCors) { + this.enableCors = enableCors; + } + + public Boolean getEnableCsrf() { + return enableCsrf; + } + + public void setEnableCsrf(Boolean enableCsrf) { + this.enableCsrf = enableCsrf; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AuthorizationConfig.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AuthorizationConfig.java new file mode 100644 index 00000000..f89095e1 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/AuthorizationConfig.java @@ -0,0 +1,170 @@ + +package com.dstz.springboot.autoconfigure.oauth2; + +import com.dstz.auth.authentication.AbClientDetailsServiceImpl; +import com.dstz.auth.authentication.AbDaoAuthenticationProvider; +import com.dstz.auth.constant.AuthConstant; +import com.dstz.auth.exception.Auth2CustomizeExceptionTranslator; +import com.dstz.auth.login.CustomPwdEncoder; +import com.dstz.auth.login.UserDetailsServiceImpl; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpMethod; +import org.springframework.security.authentication.AuthenticationManager; +import org.springframework.security.authentication.DefaultAuthenticationEventPublisher; +import org.springframework.security.authentication.ProviderManager; +import org.springframework.security.core.userdetails.UserDetailsService; +import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer; +import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.ClientDetailsService; +import org.springframework.security.oauth2.provider.code.AuthorizationCodeServices; +import org.springframework.security.oauth2.provider.code.JdbcAuthorizationCodeServices; +import org.springframework.security.oauth2.provider.error.OAuth2AuthenticationEntryPoint; +import org.springframework.security.oauth2.provider.token.AuthorizationServerTokenServices; +import org.springframework.security.oauth2.provider.token.DefaultTokenServices; +import org.springframework.security.oauth2.provider.token.TokenEnhancerChain; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; + +import javax.sql.DataSource; +import java.util.Collections; + +import static com.dstz.auth.authentication.api.constant.AuthApiConstant.*; + + +/** + * 颁发令牌配置 + * + * @author lightning + */ +@Configuration +@EnableAuthorizationServer +public class AuthorizationConfig extends AuthorizationServerConfigurerAdapter { + + @Autowired + private DataSource dataSource; + + @Autowired + private ClientDetailsService clientDetailsService; + + + @Autowired + private ApplicationContext applicationContext; + + + @Autowired + private JwtAccessTokenConverter accessTokenConverter; + + + + @Autowired + private TokenStore tokenStore; + + CustomPwdEncoder customPwdEncoder = new CustomPwdEncoder(); + + @Bean + public AbDaoAuthenticationProvider abDaoAuthenticationProvider() { + AbDaoAuthenticationProvider abDaoAuthenticationProvider = new AbDaoAuthenticationProvider(); + abDaoAuthenticationProvider.setHideUserNotFoundExceptions(false); + abDaoAuthenticationProvider.setUserDetailsService(userDetailsService()); + abDaoAuthenticationProvider.setPasswordEncoder(customPwdEncoder); + return abDaoAuthenticationProvider; + } + + @Bean("userDetailsService") + public UserDetailsService userDetailsService() { + return new UserDetailsServiceImpl(); + } + + @Bean("authenticationManager") + public AuthenticationManager authenticationManagerBean() { + ProviderManager providerManager = new ProviderManager(Collections.singletonList(abDaoAuthenticationProvider())); + providerManager.setAuthenticationEventPublisher(new DefaultAuthenticationEventPublisher(applicationContext)); + return providerManager; + } + + + /** + * 配置令牌端点安全约束 + * + * @param security + */ + @Override + public void configure(AuthorizationServerSecurityConfigurer security) { + security.tokenKeyAccess(TOKEN_SERVER_SECURITY_CONFIGURER) + .checkTokenAccess(TOKEN_SERVER_SECURITY_CONFIGURER) + .allowFormAuthenticationForClients(); + } + + + /** + * 配置客户端详情服务 + * + * @param clients + * @throws Exception + */ + @Override + public void configure(ClientDetailsServiceConfigurer clients) throws Exception { + AbClientDetailsServiceImpl clientDetailsService = new AbClientDetailsServiceImpl(dataSource); + clientDetailsService.setSelectClientDetailsSql(AuthConstant.DEFAULT_SELECT_STATEMENT); + clientDetailsService.setFindClientDetailsSql(AuthConstant.DEFAULT_FIND_STATEMENT); + clients.withClientDetails(clientDetailsService); + } + + + /** + * 配置令牌访问端点,令牌服务 + * + * @param endpoints + */ + @Override + public void configure(AuthorizationServerEndpointsConfigurer endpoints) { + + endpoints + //定制授权同意页面 + .pathMapping(TOKEN_SERVER_AUTH_DEFAULTPATH, TOKEN_SERVER_AUTH_CUSTOMPATH) + //认证管理器 + .authenticationManager(authenticationManagerBean()) + //自定义异常处理 + //.exceptionTranslator(new Auth2CustomizeExceptionTranslator()) + //密码模式的用户信息管理 + .userDetailsService(userDetailsService()) + //授权码服务 + .authorizationCodeServices(authorizationCodeServices()) + //令牌管理服务 + .tokenServices(tokenService()) + .allowedTokenEndpointRequestMethods(HttpMethod.POST, HttpMethod.GET); + } + + + @Bean + public AuthorizationCodeServices authorizationCodeServices() { + return new JdbcAuthorizationCodeServices(dataSource); + } + + @Bean + public AuthorizationServerTokenServices tokenService() { + DefaultTokenServices service = new DefaultTokenServices(); + //客户端详情服务 + service.setClientDetailsService(clientDetailsService); + // 是否支持令牌刷新 + service.setSupportRefreshToken(true); + // 令牌存储策略 + service.setTokenStore(tokenStore); + //令牌增强 + /* TokenEnhancerChain tokenEnhancerChain = new TokenEnhancerChain(); + tokenEnhancerChain.setTokenEnhancers(Arrays.asList(accessTokenConverter)); + service.setTokenEnhancer(tokenEnhancerChain);*/ + // 令牌默认有效期2小时 + service.setAccessTokenValiditySeconds(ACCESSTOKEN_VALIDITY_SECONDS); + // 刷新令牌默认有效期3 天 + service.setRefreshTokenValiditySeconds(REFRESHTOKEN_VALIDITY_SECONDS); + return service; + } +} + diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/OauthTokenAspect.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/OauthTokenAspect.java new file mode 100644 index 00000000..7c8cbda8 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/OauthTokenAspect.java @@ -0,0 +1,53 @@ +package com.dstz.springboot.autoconfigure.oauth2; + +import com.dstz.auth.authentication.api.constant.AuthStatusCode; +import com.dstz.auth.constant.AuthConstant; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.exceptions.ApiException; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.common.OAuth2AccessToken; +import org.springframework.stereotype.Component; + +import java.security.Principal; +import java.util.Map; + +/** + * 自定义切面,拦截oauth2接口处理返回参数 + * + * @author lightning + */ +@Aspect +@Component +public class OauthTokenAspect { + @Around("execution(* org.springframework.security.oauth2.provider.endpoint.TokenEndpoint.postAccessToken(..))") + public Object handleControllerMethod(ProceedingJoinPoint joinPoint) throws Throwable { + + Object[] args = joinPoint.getArgs(); + Principal principal = (Principal) args[0]; + if (!(principal instanceof Authentication)) { + throw new ApiException(AuthStatusCode.NO_CLIENT_AUTHENTICATION); + } + Map parameters = (Map) args[1]; + String grantType = parameters.get(AuthConstant.GRANT_TYPE); + + + Object proceed = joinPoint.proceed(); + //不处理 authorization_code 模式 + if (AuthConstant.AUTHORIZATION_CODE.equals(grantType)) { + return proceed; + } else { + ResponseEntity responseEntity = (ResponseEntity) proceed; + OAuth2AccessToken body = responseEntity.getBody(); + return ResponseEntity + .status(HttpStatus.OK) + .body(ApiResponse.success(body)); + } + + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/ResourceServerConfig.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/ResourceServerConfig.java new file mode 100644 index 00000000..1f1b1625 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/ResourceServerConfig.java @@ -0,0 +1,200 @@ +package com.dstz.springboot.autoconfigure.oauth2; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.util.ArrayUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.auth.authentication.AbResourceRoleAuthProvider; +import com.dstz.auth.authentication.AbResourceRoleRelationProvider; +import com.dstz.auth.exception.AuthExceptionEntryPoint; +import com.dstz.auth.exception.CustomizeAccessDeniedHandler; +import com.dstz.auth.filter.*; +import com.dstz.auth.forbidden.DefaultAccessDeniedHandler; +import com.dstz.auth.login.logout.DefualtLogoutSuccessHandler; +import com.dstz.base.common.constats.StrPool; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.security.access.expression.SecurityExpressionHandler; +import org.springframework.security.authentication.AuthenticationTrustResolver; +import org.springframework.security.authentication.AuthenticationTrustResolverImpl; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.builders.WebSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityCustomizer; +import org.springframework.security.core.Authentication; +import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; +import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; +import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer; +import org.springframework.security.oauth2.provider.expression.OAuth2WebSecurityExpressionHandler; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.web.FilterInvocation; +import org.springframework.security.web.access.expression.DefaultWebSecurityExpressionHandler; +import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource; +import org.springframework.security.web.access.intercept.FilterSecurityInterceptor; +import org.springframework.security.web.context.request.async.WebAsyncManagerIntegrationFilter; +import org.springframework.security.web.csrf.CsrfFilter; +import org.springframework.security.web.session.HttpSessionEventPublisher; +import org.springframework.web.servlet.i18n.CookieLocaleResolver; + +import java.util.Arrays; +import java.util.Locale; +import java.util.Objects; + +/** + * 资源验证配置 + * + * @author lightning + */ +@Configuration +@EnableResourceServer +@EnableConfigurationProperties({AbSecurityProperties.class}) +public class ResourceServerConfig extends ResourceServerConfigurerAdapter implements WebSecurityCustomizer { + + @Autowired + private TokenStore tokenStore; + + @Autowired + private AbSecurityProperties abSecurityProperties; + + @Bean + public HttpSessionEventPublisher httpSessionEventPublisher() { + return new HttpSessionEventPublisher(); + } + + @Override + public void configure(ResourceServerSecurityConfigurer resources) { + // 实现匿名鉴权 + OAuth2WebSecurityExpressionHandler oAuth2WebSecurityExpressionHandler = new OAuth2WebSecurityExpressionHandler(); + oAuth2WebSecurityExpressionHandler.setTrustResolver(authenticationTrustResolver()); + resources.expressionHandler(oAuth2WebSecurityExpressionHandler); + + resources.tokenStore(tokenStore).stateless(true); + resources.authenticationEntryPoint(new AuthExceptionEntryPoint()) + .accessDeniedHandler(new CustomizeAccessDeniedHandler()); + } + + @Override + public void configure(HttpSecurity http) throws Exception { + if (Boolean.TRUE.equals(abSecurityProperties.getEnableCsrf())) { + //防止外链连入到本系统。 + http.addFilterAt(csrfFilter(), CsrfFilter.class); + } + //token为空过滤 + http.addFilterBefore(authorizationTokenCheckFilter(), WebAsyncManagerIntegrationFilter.class); + //Cors过滤器 + SecurityInterceptor securityInterceptor = abSecurityInterceptor(); + http.addFilterBefore(securityInterceptor, FilterSecurityInterceptor.class); + //退出登录 + http.logout().logoutSuccessHandler(new DefualtLogoutSuccessHandler()).invalidateHttpSession(true).clearAuthentication(true); + //无权限处理 + http.exceptionHandling().accessDeniedHandler(accessDeniedHandler()); + http.headers().frameOptions().disable(); + http.authorizeRequests() //校验请求 + //忽略鉴权 + .antMatchers(ArrayUtil.removeBlank(abSecurityProperties.getAuthIngores())).permitAll() + .and().csrf().disable(); + } + + @Override + public void customize(WebSecurity web) { + SecurityExpressionHandler expressionHandler = web.getExpressionHandler(); + if(expressionHandler instanceof DefaultWebSecurityExpressionHandler){ + ((DefaultWebSecurityExpressionHandler)expressionHandler).setTrustResolver(authenticationTrustResolver()); + } + } + + public AuthorizationTokenCheckFilter authorizationTokenCheckFilter() { + AuthorizationTokenCheckFilter filter = new AuthorizationTokenCheckFilter(); + filter.setIgnoreUrls(ArrayUtil.removeBlank(abSecurityProperties.getAuthIngores())); + return filter; + } + + + /** + * 允许跨域的请求列表 + * + * @return 实例 + */ + public RefererCsrfFilter csrfFilter() { + RefererCsrfFilter filter = new RefererCsrfFilter(); + filter.setIngores(CollUtil.removeBlank(Arrays.asList(abSecurityProperties.getCsrfIngores()))); + return filter; + } + + /** + * 无权限处理器 返回resultMsg + **/ + public DefaultAccessDeniedHandler accessDeniedHandler() { + return new DefaultAccessDeniedHandler(); + } + + /** + * 鉴权拦截器 + * + * @return + */ + private SecurityInterceptor abSecurityInterceptor() { + SecurityInterceptor intercept = new SecurityInterceptor(); + intercept.setAccessDecisionManager(abResourceRoleAuthProvider()); + intercept.setSecurityMetadataSource(securityMetadataSource()); + return intercept; + } + + @Bean + public AbResourceRoleAuthProvider abResourceRoleAuthProvider() { + return new AbResourceRoleAuthProvider(); + } + + /** + * 获取 URL 对应的角色 + * + * @return + */ + @Bean + protected FilterInvocationSecurityMetadataSource securityMetadataSource() { + AbResourceRoleRelationProvider securityMetadataSource = new AbResourceRoleRelationProvider(); + securityMetadataSource.setIgnoreUrls(ArrayUtil.removeBlank(abSecurityProperties.getAuthIngores())); + return securityMetadataSource; + } + + @Bean("localeResolver") + public CookieLocaleResolver cookieLocaleResolver() { + CookieLocaleResolver cookieLocaleResolver = new CookieLocaleResolver(); + cookieLocaleResolver.setDefaultLocale(Locale.CHINA); + return cookieLocaleResolver; + } + + /** + * 允许HTML 等标签的提交的请求列表 + * + * @return 实例 + */ + public XssFilter xssFilter() { + XssFilter xssFilter = new XssFilter(); + xssFilter.setIgnoreUrls(StrUtil.splitToArray(abSecurityProperties.getXssIngores(), StrPool.COMMA)); + return xssFilter; + } + + /** + * 跨域过滤器 + * + * @return 跨域过滤器 + */ + @ConditionalOnProperty(prefix = "ab.security", name = {"enableCors", "enable-cors"}, havingValue = "true", matchIfMissing = true) + @Bean + public CorsFilter corsFilter() { + return new CorsFilter(); + } + + @Bean + public AuthenticationTrustResolver authenticationTrustResolver() { + return new AuthenticationTrustResolverImpl() { + @Override + public boolean isAnonymous(Authentication authentication) { + // 兼容匿名带有Token访问 + return Objects.nonNull(authentication); + } + }; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/TokenConfig.java b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/TokenConfig.java new file mode 100644 index 00000000..f21e2cc4 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter-security/src/main/java/com/dstz/springboot/autoconfigure/oauth2/TokenConfig.java @@ -0,0 +1,54 @@ + +package com.dstz.springboot.autoconfigure.oauth2; + +import cn.hutool.extra.spring.SpringUtil; +import com.dstz.auth.authentication.AbAuthenticationTokenGenerator; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.security.oauth2.provider.token.TokenStore; +import org.springframework.security.oauth2.provider.token.store.InMemoryTokenStore; +import org.springframework.security.oauth2.provider.token.store.JwtAccessTokenConverter; +import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore; + +import javax.sql.DataSource; + +@Configuration +public class TokenConfig { + + + @Autowired + private DataSource dataSource; + + /** + * 默认注入内存存储 + * @return + */ + @Bean + @ConditionalOnProperty(name = "ab.oauth.token-store-type",havingValue = "InMemory", matchIfMissing = true) + public TokenStore tokenStoreInMemory() { + InMemoryTokenStore store = new InMemoryTokenStore(); + store.setAuthenticationKeyGenerator(new AbAuthenticationTokenGenerator()); + return store; + } + + @Bean + @ConditionalOnProperty(name = "ab.oauth.token-store-type",havingValue = "redis") + public TokenStore tokenStoreInRedis() { + RedisTokenStore store = new RedisTokenStore(SpringUtil.getBean(RedisConnectionFactory.class)); + store.setPrefix("ab:token:"); + store.setAuthenticationKeyGenerator(new AbAuthenticationTokenGenerator()); + return store; + } + + @Bean + public JwtAccessTokenConverter accessTokenConverter() { + JwtAccessTokenConverter converter = new JwtAccessTokenConverter(); + //对称秘钥,资源服务器使用该秘钥来验证 + converter.setSigningKey(SpringUtil.getProperty("ab.oauth.login-token-signing-key")); + return converter; + } +} + diff --git a/ab-spring-boot/ab-spring-boot-starter/pom.xml b/ab-spring-boot/ab-spring-boot-starter/pom.xml new file mode 100644 index 00000000..09740cfc --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/pom.xml @@ -0,0 +1,62 @@ + + + + ab-spring-boot + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot-starter + + + + org.springframework.boot + spring-boot-starter + + + org.springframework.boot + spring-boot-autoconfigure + + + org.springframework.boot + spring-boot-configuration-processor + + + com.baomidou + mybatis-plus-boot-starter + provided + + + com.dstz + ab-base-common + + + com.dstz + ab-base-mapper + provided + + + com.dstz + ab-component-redis + ${project.version} + + + com.dstz + ab-component-j2cache + ${project.version} + provided + + + com.dstz + ab-base-web + provided + + + com.fasterxml.jackson.datatype + jackson-datatype-jsr310 + + + diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConditional.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConditional.java new file mode 100644 index 00000000..80bce707 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConditional.java @@ -0,0 +1,28 @@ +package com.dstz.springboot.autoconfigure.cache; + +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.constats.StrPool; +import com.dstz.springboot.autoconfigure.cache.enums.AbCacheTypeEnum; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.core.type.ClassMetadata; +import org.springframework.util.StringUtils; + +class AbCacheConditional extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + String sourceClass = ""; + if (metadata instanceof ClassMetadata) { + sourceClass = ((ClassMetadata) metadata).getClassName(); + } + String value = context.getEnvironment().getProperty("ab.cache.type"); + if (!StringUtils.hasLength(value)) { + return AbMemoryCacheConfiguration.class.getName().equals(sourceClass) ? ConditionOutcome.match() : ConditionOutcome.noMatch(value + " cache type"); + } + AbCacheTypeEnum abCacheType = AbCacheTypeEnum.valueOf(StrUtil.replace(value, StrPool.DASHED, StrPool.UNDERLINE).toUpperCase()); + return abCacheType.getConfigClass().getName().equals(sourceClass) ? ConditionOutcome.match() : ConditionOutcome.noMatch(value + " cache type"); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConfiguration.java new file mode 100644 index 00000000..03013079 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheConfiguration.java @@ -0,0 +1,43 @@ +package com.dstz.springboot.autoconfigure.cache; + +import com.dstz.base.common.cache.AbSpringCacheManager; +import com.dstz.base.common.cache.ICache; +import com.dstz.springboot.autoconfigure.cache.enums.AbCacheTypeEnum; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.cache.CacheManager; +import org.springframework.cache.annotation.EnableCaching; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Import; +import org.springframework.context.annotation.ImportSelector; +import org.springframework.core.type.AnnotationMetadata; + +/** + * 缓存自动配置 + * + * @author wacxhs + */ +@EnableConfigurationProperties(AbCacheProperties.class) +@Import(AbCacheConfiguration.AbCacheConfigurationSelector.class) +@EnableCaching +@Configuration +public class AbCacheConfiguration { + + + public static class AbCacheConfigurationSelector implements ImportSelector { + @Override + public String[] selectImports(AnnotationMetadata importingClassMetadata) { + AbCacheTypeEnum[] types = AbCacheTypeEnum.values(); + String[] imports = new String[types.length]; + for (int i = 0; i < types.length; i++) { + imports[i] = types[i].getConfigClass().getName(); + } + return imports; + } + } + + @Bean + public CacheManager cacheManager(ICache cache) { + return new AbSpringCacheManager(cache); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheProperties.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheProperties.java new file mode 100644 index 00000000..fe070300 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbCacheProperties.java @@ -0,0 +1,67 @@ +package com.dstz.springboot.autoconfigure.cache; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.cache.CacheRegion; +import com.dstz.springboot.autoconfigure.cache.enums.AbCacheTypeEnum; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.time.Duration; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; + +/** + * ab 缓存属性配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.cache") +public class AbCacheProperties { + + /** + * 缓存类型 + */ + private AbCacheTypeEnum type = AbCacheTypeEnum.MEMORY; + + /** + * 缓存区域,region=缓存时间[,缓存大小] + */ + private Map region; + + public AbCacheTypeEnum getType() { + return type; + } + + public void setType(AbCacheTypeEnum type) { + this.type = type; + } + + public Map getRegion() { + return region; + } + + public void setRegion(Map region) { + this.region = region; + } + + public List getCacheRegionList() { + List cacheRegionList = new ArrayList<>(); + if (MapUtil.isNotEmpty(region)) { + for (Map.Entry entry : region.entrySet()) { + Duration expire; + Long size = null; + int delimiterIndex = entry.getValue().indexOf(CharUtil.COMMA); + if (delimiterIndex == StrUtil.INDEX_NOT_FOUND) { + expire = Duration.parse(entry.getValue().trim()); + } else { + expire = Duration.parse(entry.getValue().substring(0, delimiterIndex).trim()); + size = Long.parseLong(entry.getValue().substring(delimiterIndex + 1).trim()); + } + cacheRegionList.add(new CacheRegion(entry.getKey(), expire, size)); + } + } + return cacheRegionList; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbMemoryCacheConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbMemoryCacheConfiguration.java new file mode 100644 index 00000000..1e40f166 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbMemoryCacheConfiguration.java @@ -0,0 +1,26 @@ +package com.dstz.springboot.autoconfigure.cache; + +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.cache.MemoryCache; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; + +/** + * 内存缓存配置 + * + * @author wacxhs + */ +@Conditional(AbCacheConditional.class) +public class AbMemoryCacheConfiguration { + + private final AbCacheProperties abCacheProperties; + + public AbMemoryCacheConfiguration(AbCacheProperties abCacheProperties) { + this.abCacheProperties = abCacheProperties; + } + + @Bean + public ICache memoryCache() { + return new MemoryCache(abCacheProperties.getCacheRegionList()); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbRedisCacheConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbRedisCacheConfiguration.java new file mode 100644 index 00000000..96f57a76 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/AbRedisCacheConfiguration.java @@ -0,0 +1,29 @@ +package com.dstz.springboot.autoconfigure.cache; + +import com.dstz.base.common.cache.ICache; +import com.dstz.component.redis.cache.AbRedisCache; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.context.annotation.Configuration; +import org.springframework.data.redis.connection.RedisConnectionFactory; + +/** + * ab redis cache + * + * @author wacxhs + */ +@Conditional(AbCacheConditional.class) +@Configuration +public class AbRedisCacheConfiguration { + + private final AbCacheProperties abCacheProperties; + + public AbRedisCacheConfiguration(AbCacheProperties abCacheProperties) { + this.abCacheProperties = abCacheProperties; + } + + @Bean + public ICache redisCache(RedisConnectionFactory redisConnectionFactory) { + return new AbRedisCache(abCacheProperties.getCacheRegionList(), redisConnectionFactory); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheConfiguration.java new file mode 100644 index 00000000..66840a67 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheConfiguration.java @@ -0,0 +1,114 @@ +package com.dstz.springboot.autoconfigure.cache; + +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.cache.CacheRegion; +import com.dstz.base.common.constats.StrPool; +import com.dstz.component.j2cache.AbJ2Cache; +import com.dstz.component.j2cache.J2CacheChannelFactoryBean; +import com.dstz.component.j2cache.constant.J2CacheBroadcastPropertyKeyConstant; +import com.dstz.component.j2cache.constant.J2CacheL2PropertyKeyConstant; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.task.TaskExecutorBuilder; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; +import org.springframework.data.redis.connection.RedisConnectionFactory; +import org.springframework.data.redis.listener.RedisMessageListenerContainer; +import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; + +import java.time.Duration; +import java.util.List; +import java.util.Objects; +import java.util.Properties; + +/** + * j2cache 配置 + * + * @author wacxhs + */ +@Conditional(AbCacheConditional.class) +@EnableConfigurationProperties(J2CacheProperties.class) +public class J2CacheConfiguration { + + private final AbCacheProperties abCacheProperties; + + private final J2CacheProperties j2CacheProperties; + + public J2CacheConfiguration(AbCacheProperties abCacheProperties, J2CacheProperties j2CacheProperties) { + this.abCacheProperties = abCacheProperties; + this.j2CacheProperties = j2CacheProperties; + } + + @Bean + public AbJ2Cache abJ2Cache() { + return new AbJ2Cache(); + } + + @Bean + public J2CacheChannelFactoryBean j2CacheChannelFactoryBean() { + // L1 缓存属性配置 + Properties l1Properties = new Properties(); + if (j2CacheProperties.getLevel1().getProperties() != null) { + l1Properties.putAll(j2CacheProperties.getLevel1().getProperties()); + } + + // L2 缓存属性配置 + Properties l2Properties = new Properties(); + if (j2CacheProperties.getLevel2().getProperties() != null) { + l2Properties.putAll(j2CacheProperties.getLevel2().getProperties()); + } + + // 构建缓存格式 + List cacheRegionList = abCacheProperties.getCacheRegionList(); + for (CacheRegion cacheRegion : cacheRegionList) { + String regionName = StrUtil.concat(true, "region.", cacheRegion.getRegion()); + String regionExpire = StrUtil.concat(true, Long.toString(ObjectUtil.defaultIfNull(cacheRegion.getSize(), Long.MAX_VALUE)), StrPool.COMMA, Long.toString(cacheRegion.getExpiration().getSeconds()), "s"); + l1Properties.put(regionName, regionExpire); + l2Properties.put(regionName, regionExpire); + } + // 缓存区域放入配置文件,供其他实现读取 + l1Properties.put(CacheRegion.class, cacheRegionList); + l2Properties.put(CacheRegion.class, cacheRegionList); + l2Properties.setProperty(J2CacheL2PropertyKeyConstant.CACHE_OPEN, Objects.toString(Boolean.TRUE.equals(j2CacheProperties.getLevel2().getCacheOpen()))); + + // 广播属性 + Properties broadcastProperties = new Properties(); + if (j2CacheProperties.getBroadcast().getProperties() != null) { + broadcastProperties.putAll(j2CacheProperties.getBroadcast().getProperties()); + } + // 是否开启 + broadcastProperties.put(J2CacheBroadcastPropertyKeyConstant.OPEN, Boolean.toString(Boolean.TRUE.equals(j2CacheProperties.getBroadcast().getOpen()))); + broadcastProperties.put(J2CacheBroadcastPropertyKeyConstant.CHANNEL, j2CacheProperties.getBroadcast().getChannel()); + + J2CacheChannelFactoryBean factoryBean = new J2CacheChannelFactoryBean(); + factoryBean.setSerialization(j2CacheProperties.getSerializationClass() == null ? j2CacheProperties.getSerialization().getSerializer() : j2CacheProperties.getSerializationClass().getName()); + factoryBean.setBroadcastProviderClass(j2CacheProperties.getBroadcast().getProviderClass() == null ? j2CacheProperties.getBroadcast().getProvider().getProvider() : j2CacheProperties.getBroadcast().getProviderClass().getName()); + factoryBean.setCacheL1ProviderClass(j2CacheProperties.getLevel1().getProviderClass() == null ? j2CacheProperties.getLevel1().getProvider().getProvider() : j2CacheProperties.getLevel1().getProviderClass().getName()); + factoryBean.setCacheL2ProviderClass(j2CacheProperties.getLevel2().getProviderClass() == null ? j2CacheProperties.getLevel2().getProvider().getProvider() : j2CacheProperties.getLevel2().getProviderClass().getName()); + factoryBean.setDefaultCacheNullObject(Boolean.TRUE.equals(j2CacheProperties.getDefaultCacheNullObject())); + factoryBean.setCacheL1Properties(l1Properties); + factoryBean.setCacheL2Properties(l2Properties); + factoryBean.setBroadcastProperties(broadcastProperties); + return factoryBean; + } + + @Bean + @ConditionalOnMissingBean + public RedisMessageListenerContainer redisMessageListenerContainer(RedisConnectionFactory redisConnectionFactory) { + ThreadPoolTaskExecutor taskExecutor = new TaskExecutorBuilder() + .allowCoreThreadTimeOut(true) + .awaitTermination(true) + .corePoolSize(2) + .keepAlive(Duration.ofSeconds(60)) + .threadNamePrefix(RedisMessageListenerContainer.DEFAULT_THREAD_NAME_PREFIX) + .maxPoolSize(Math.max(Math.round(Runtime.getRuntime().availableProcessors() * (float) 0.2), 2)).build(); + taskExecutor.initialize(); + + RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); + redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory); + redisMessageListenerContainer.setTaskExecutor(taskExecutor); + return redisMessageListenerContainer; + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheProperties.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheProperties.java new file mode 100644 index 00000000..a1b3b78d --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/J2CacheProperties.java @@ -0,0 +1,264 @@ +package com.dstz.springboot.autoconfigure.cache; + + +import com.dstz.springboot.autoconfigure.cache.enums.J2CacheBroadcastEnum; +import com.dstz.springboot.autoconfigure.cache.enums.J2CacheLevel1ProviderEnum; +import com.dstz.springboot.autoconfigure.cache.enums.J2CacheLevel2ProviderEnum; +import com.dstz.springboot.autoconfigure.cache.enums.J2CacheSerializationEnum; +import net.oschina.j2cache.CacheProvider; +import net.oschina.j2cache.cluster.ClusterPolicy; +import net.oschina.j2cache.util.Serializer; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Map; + +/** + * J2Cache 属性配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "j2cache") +public class J2CacheProperties { + + /** + * 序列化 + */ + private J2CacheSerializationEnum serialization = J2CacheSerializationEnum.JSON; + + /** + * 自定义序列化实现类 + */ + private Class serializationClass; + + /** + * 是否缓存空对象 + */ + private Boolean defaultCacheNullObject = Boolean.FALSE; + + /** + * 一级缓存 + */ + private final Level1CacheProperties level1 = new Level1CacheProperties(); + + /** + * 二级缓存 + */ + private final Level2CacheProperties level2 = new Level2CacheProperties(); + + /** + * 广播 + */ + private final BroadcastProperties broadcast = new BroadcastProperties(); + + public J2CacheSerializationEnum getSerialization() { + return serialization; + } + + public void setSerialization(J2CacheSerializationEnum serialization) { + this.serialization = serialization; + } + + public Class getSerializationClass() { + return serializationClass; + } + + public void setSerializationClass(Class serializationClass) { + this.serializationClass = serializationClass; + } + + public Boolean getDefaultCacheNullObject() { + return defaultCacheNullObject; + } + + public void setDefaultCacheNullObject(Boolean defaultCacheNullObject) { + this.defaultCacheNullObject = defaultCacheNullObject; + } + + public Level1CacheProperties getLevel1() { + return level1; + } + + public Level2CacheProperties getLevel2() { + return level2; + } + + public BroadcastProperties getBroadcast() { + return broadcast; + } + + /** + * 二级缓存 + */ + public static class Level1CacheProperties { + + /** + * 二级缓存 + */ + private J2CacheLevel1ProviderEnum provider = J2CacheLevel1ProviderEnum.CAFFEINE; + + /** + * 二级缓存实现提供类 + */ + private Class providerClass; + + /** + * 属性 + */ + private Map properties; + + public J2CacheLevel1ProviderEnum getProvider() { + return provider; + } + + public void setProvider(J2CacheLevel1ProviderEnum provider) { + this.provider = provider; + } + + public Class getProviderClass() { + return providerClass; + } + + public void setProviderClass(Class providerClass) { + this.providerClass = providerClass; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + /** + * 二级缓存 + */ + public static class Level2CacheProperties { + + /** + * 开启二级缓存 + */ + private Boolean cacheOpen = Boolean.FALSE; + + /** + * 二级缓存 + */ + private J2CacheLevel2ProviderEnum provider = J2CacheLevel2ProviderEnum.REDIS; + + /** + * 二级缓存实现提供类 + */ + private Class providerClass; + + /** + * 属性 + */ + private Map properties; + + public Boolean getCacheOpen() { + return cacheOpen; + } + + public void setCacheOpen(Boolean cacheOpen) { + this.cacheOpen = cacheOpen; + } + + public J2CacheLevel2ProviderEnum getProvider() { + return provider; + } + + public void setProvider(J2CacheLevel2ProviderEnum provider) { + this.provider = provider; + } + + public Class getProviderClass() { + return providerClass; + } + + public void setProviderClass(Class providerClass) { + this.providerClass = providerClass; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + /** + * 广播属性 + */ + public static class BroadcastProperties { + + /** + * 开启广播 + */ + private Boolean open = Boolean.FALSE; + + /** + * 广播实现 + */ + private J2CacheBroadcastEnum provider = J2CacheBroadcastEnum.REDIS; + + /** + * 广播实现类 + */ + private Class providerClass; + + /** + * 广播通道名称 + */ + private String channel = "j2cache"; + + /** + * 属性 + */ + private Map properties; + + public Boolean getOpen() { + return open; + } + + public void setOpen(Boolean open) { + this.open = open; + } + + public J2CacheBroadcastEnum getProvider() { + return provider; + } + + public void setProvider(J2CacheBroadcastEnum provider) { + this.provider = provider; + } + + public Class getProviderClass() { + return providerClass; + } + + public void setProviderClass(Class providerClass) { + this.providerClass = providerClass; + } + + public String getChannel() { + return channel; + } + + public void setChannel(String channel) { + this.channel = channel; + } + + public Map getProperties() { + return properties; + } + + public void setProperties(Map properties) { + this.properties = properties; + } + } + + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/AbCacheTypeEnum.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/AbCacheTypeEnum.java new file mode 100644 index 00000000..85b44fa2 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/AbCacheTypeEnum.java @@ -0,0 +1,39 @@ +package com.dstz.springboot.autoconfigure.cache.enums; + +import com.dstz.springboot.autoconfigure.cache.AbMemoryCacheConfiguration; +import com.dstz.springboot.autoconfigure.cache.AbRedisCacheConfiguration; +import com.dstz.springboot.autoconfigure.cache.J2CacheConfiguration; + +/** + * + * 缓存类型 + * + * @author wacxhs + */ +public enum AbCacheTypeEnum { + + /** + * 内存缓存 + */ + MEMORY(AbMemoryCacheConfiguration.class), + + /** + * REDIS 缓存 + */ + REDIS(AbRedisCacheConfiguration.class), + + /** + * J2CACHE 缓存 + */ + J2CACHE(J2CacheConfiguration.class); + + private final Class configClass; + + AbCacheTypeEnum(Class configClass) { + this.configClass = configClass; + } + + public Class getConfigClass() { + return configClass; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheBroadcastEnum.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheBroadcastEnum.java new file mode 100644 index 00000000..b2f7eb62 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheBroadcastEnum.java @@ -0,0 +1,36 @@ +package com.dstz.springboot.autoconfigure.cache.enums; + +import com.dstz.component.j2cache.redis.RedisPubSubClusterPolicy; + +/** + * j2cache 广播 + * + * @author wacxhs + */ +public enum J2CacheBroadcastEnum { + + /** + * redis 发布与订阅 + */ + REDIS(RedisPubSubClusterPolicy.class.getName()), + + /** + * 空通知 + */ + NONE("none"), + + /** + * 组播 + */ + JGROUPS("jgroups"); + + private final String provider; + + J2CacheBroadcastEnum(String provider) { + this.provider = provider; + } + + public String getProvider() { + return provider; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel1ProviderEnum.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel1ProviderEnum.java new file mode 100644 index 00000000..2ade5056 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel1ProviderEnum.java @@ -0,0 +1,40 @@ +package com.dstz.springboot.autoconfigure.cache.enums; + + +/** + * j2cache 一级缓存提供类 + * + * @author wacxhs + */ +public enum J2CacheLevel1ProviderEnum { + + /** + * caffeine 缓存 + */ + CAFFEINE("caffeine"), + + /** + * 无 缓存 + */ + NONE("none"), + + /** + * EhCache 2.x 缓存 + */ + EHCACHE("ehcache"), + + /** + * EhCache 3.x 缓存 + */ + EHCACHE3("ehcache3"); + + private final String provider; + + J2CacheLevel1ProviderEnum(String providerClass) { + this.provider = providerClass; + } + + public String getProvider() { + return provider; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel2ProviderEnum.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel2ProviderEnum.java new file mode 100644 index 00000000..4efd5a27 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheLevel2ProviderEnum.java @@ -0,0 +1,32 @@ +package com.dstz.springboot.autoconfigure.cache.enums; + + +import com.dstz.component.j2cache.redis.SpringRedisCacheProvider; + +/** + * j2cache 一级缓存提供类 + * + * @author wacxhs + */ +public enum J2CacheLevel2ProviderEnum { + + /** + * 无 缓存 + */ + NONE("none"), + + /** + * REDIS + */ + REDIS(SpringRedisCacheProvider.class.getName()); + + private final String provider; + + J2CacheLevel2ProviderEnum(String providerClass) { + this.provider = providerClass; + } + + public String getProvider() { + return provider; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheSerializationEnum.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheSerializationEnum.java new file mode 100644 index 00000000..0ac1b2aa --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/cache/enums/J2CacheSerializationEnum.java @@ -0,0 +1,37 @@ +package com.dstz.springboot.autoconfigure.cache.enums; + +import com.dstz.component.j2cache.serialization.JacksonJsonSerializer; + +/** + * J2Cache 序列化枚举 + * + * @author wacxhs + */ +public enum J2CacheSerializationEnum { + + /** + * JAVA 内置序列化 + */ + JAVA("java"), + + /** + * JSON 序列化 + */ + JSON(JacksonJsonSerializer.class.getName()), + + /** + * FST 序列化 + */ + FST("fst"); + + + private final String serializer; + + J2CacheSerializationEnum(String serializer) { + this.serializer = serializer; + } + + public String getSerializer() { + return serializer; + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientAutoConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientAutoConfiguration.java new file mode 100644 index 00000000..501e7784 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientAutoConfiguration.java @@ -0,0 +1,67 @@ +package com.dstz.springboot.autoconfigure.client; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.lang.Pair; +import cn.hutool.core.util.ObjectUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.common.client.ConfigRestClientInfoTransform; +import com.dstz.base.common.client.RestClientInfoTransform; +import com.dstz.base.common.client.RestClientInfoTransformComposite; +import com.google.common.collect.Lists; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.boot.web.client.RestTemplateBuilder; +import org.springframework.boot.web.client.RestTemplateCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.client.ClientHttpRequestInterceptor; +import org.springframework.web.client.RestTemplate; + +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * ab rest template auto configuration + * + * @author wacxhs + */ +@Configuration(proxyBeanMethods = false) +@EnableConfigurationProperties(AbRestClientInfoTransformProperties.class) +public class AbRestClientAutoConfiguration { + + @Bean + public RestTemplateCustomizer abRestTemplateCustomizer() { + ClientHttpRequestInterceptor clientHttpRequestInterceptor = (request, body, execution) -> { + RestClientInfoTransformComposite transformComposite = RestClientInfoTransformComposite.getInstance(); + transformComposite.getHeaders().forEach((k, v) -> request.getHeaders().add(k, v)); + String cookiesString = transformComposite.getCookiesString(); + if (StrUtil.isNotEmpty(cookiesString)) { + request.getHeaders().add(HttpHeaders.COOKIE, cookiesString); + } + return execution.execute(request, body); + }; + return restTemplate -> restTemplate.getInterceptors().add(clientHttpRequestInterceptor); + } + + @Bean + public RestClientInfoTransform configRestClientInfoTransform(AbRestClientInfoTransformProperties restClientInfoTransformProperties) { + return new ConfigRestClientInfoTransform( + CollUtil.map(restClientInfoTransformProperties.getCookies(), cookie -> Pair.of(cookie.getName(), cookie.getValue()), false), + CollUtil.map(restClientInfoTransformProperties.getHeaders(), header -> Pair.of(header.getName(), header.getValue()), false) + ); + } + + /** + * ab http rest template + * + * @param restTemplateBuilder rest template builder + * @return rest template + */ + @Bean + public RestTemplate abHttpRestTemplate(RestTemplateBuilder restTemplateBuilder) { + return restTemplateBuilder.build(); + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientInfoTransformProperties.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientInfoTransformProperties.java new file mode 100644 index 00000000..da5e11e1 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/client/AbRestClientInfoTransformProperties.java @@ -0,0 +1,68 @@ +package com.dstz.springboot.autoconfigure.client; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.List; + +/** + * 客户端信息传输配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.rest-client-info-transform") +public class AbRestClientInfoTransformProperties { + + /** + * Cookie + */ + private List cookies; + + /** + * header + */ + private List headers; + + public List getCookies() { + return cookies; + } + + public void setCookies(List cookies) { + this.cookies = cookies; + } + + public List getHeaders() { + return headers; + } + + public void setHeaders(List headers) { + this.headers = headers; + } + + public static final class NameValue { + /** + * 属性名称 + */ + private String name; + /** + * 属性值,为空则从当前请求中取 + */ + private String value; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorAutoConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorAutoConfiguration.java new file mode 100644 index 00000000..8adf300d --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorAutoConfiguration.java @@ -0,0 +1,46 @@ +package com.dstz.springboot.autoconfigure.idgen; + +import cn.hutool.core.lang.Snowflake; +import cn.hutool.core.util.IdUtil; +import com.dstz.base.common.idgen.IdGenerator; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * ID 生成器自动配置 + * + * @author wacxhs + */ +@EnableConfigurationProperties(IdGeneratorProperties.class) +@Configuration +public class IdGeneratorAutoConfiguration { + + private final IdGeneratorProperties idGeneratorProperties; + + public IdGeneratorAutoConfiguration(IdGeneratorProperties idGeneratorProperties) { + this.idGeneratorProperties = idGeneratorProperties; + } + + /** + * 雪花算法生成器 + * + * @return 雪花算法生成器 + */ + @ConditionalOnProperty(prefix = "ab.id-generator", name = "type", havingValue = "snowflake", matchIfMissing = true) + @Bean + public IdGenerator snowflakeGenerator() { + IdGeneratorProperties.Snowflake snowflakeProperties = idGeneratorProperties.getSnowflake(); + if (snowflakeProperties.getDataCenterId() == null) { + snowflakeProperties.setDataCenterId(IdUtil.getDataCenterId(31)); + } + if (snowflakeProperties.getWorkerId() == null) { + snowflakeProperties.setWorkerId(IdUtil.getWorkerId(snowflakeProperties.getDataCenterId(), 31)); + } + Snowflake snowflake = new Snowflake(snowflakeProperties.getEpochDate(), snowflakeProperties.getWorkerId(), snowflakeProperties.getDataCenterId(), snowflakeProperties.getUseSystemClock(), snowflakeProperties.getTimeOffset()); + return snowflake::nextIdStr; + } + + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorProperties.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorProperties.java new file mode 100644 index 00000000..3dda3bd9 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorProperties.java @@ -0,0 +1,110 @@ +package com.dstz.springboot.autoconfigure.idgen; + + +import cn.hutool.core.date.SystemClock; +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Date; + +/** + * ID生成器配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.id-generator") +public class IdGeneratorProperties { + + /** + * 生成器类型 + */ + private IdGeneratorType type = IdGeneratorType.SNOWFLAKE; + + /** + * 雪花算法配置 + */ + private Snowflake snowflake = new Snowflake(); + + public IdGeneratorType getType() { + return type; + } + + public void setType(IdGeneratorType type) { + this.type = type; + } + + public Snowflake getSnowflake() { + return snowflake; + } + + public void setSnowflake(Snowflake snowflake) { + this.snowflake = snowflake; + } + + public static class Snowflake { + + /** + * 初始化时间起点(null表示默认起始日期),后期修改会导致id重复,如果要修改连workerId dataCenterId,慎用 + */ + private Date epochDate; + + /** + * 数据中心ID + */ + private Long dataCenterId; + + /** + * 工作机器节点ID + */ + private Long workerId; + + /** + * 是否使用{@link SystemClock} 获取当前时间戳 + */ + private Boolean isUseSystemClock = Boolean.FALSE; + + /** + * 默认回拨时间,2S + */ + private Long timeOffset = cn.hutool.core.lang.Snowflake.DEFAULT_TIME_OFFSET; + + public Date getEpochDate() { + return epochDate; + } + + public void setEpochDate(Date epochDate) { + this.epochDate = epochDate; + } + + public Long getDataCenterId() { + return dataCenterId; + } + + public void setDataCenterId(Long dataCenterId) { + this.dataCenterId = dataCenterId; + } + + public Long getWorkerId() { + return workerId; + } + + public void setWorkerId(Long workerId) { + this.workerId = workerId; + } + + public Boolean getUseSystemClock() { + return isUseSystemClock; + } + + public void setUseSystemClock(Boolean useSystemClock) { + isUseSystemClock = useSystemClock; + } + + public Long getTimeOffset() { + return timeOffset; + } + + public void setTimeOffset(Long timeOffset) { + this.timeOffset = timeOffset; + } + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorType.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorType.java new file mode 100644 index 00000000..4a6d9247 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/idgen/IdGeneratorType.java @@ -0,0 +1,15 @@ +package com.dstz.springboot.autoconfigure.idgen; + +/** + * ID 生成器类型 + * + * @author wacxhs + */ +public enum IdGeneratorType { + + /** + * 雪花算法 + */ + SNOWFLAKE + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/jdbc/AbMybatisAutoConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/jdbc/AbMybatisAutoConfiguration.java new file mode 100644 index 00000000..4db327ec --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/jdbc/AbMybatisAutoConfiguration.java @@ -0,0 +1,50 @@ +package com.dstz.springboot.autoconfigure.jdbc; + + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.autoconfigure.MybatisPlusAutoConfiguration; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.OptimisticLockerInnerInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import com.dstz.base.common.exceptions.OptimisticLockingFieldValueException; +import com.dstz.base.enums.AbDbType; +import com.dstz.base.interceptor.AbQueryFilterInterceptor; +import com.dstz.base.interceptor.MybatisPlusInterceptorCustomizer; +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.springframework.beans.factory.ObjectProvider; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * ab mybatis configuration + * + * @author wacxhs + */ +@ConditionalOnClass({MybatisPlusAutoConfiguration.class, SqlSessionFactory.class, SqlSessionFactoryBean.class}) +@Configuration +public class AbMybatisAutoConfiguration { + + @ConditionalOnMissingBean + @Bean + public MybatisPlusInterceptor mybatisPlusInterceptor(ObjectProvider customizerObjectProvider) { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.getDbType(AbDbType.currentDbType().getDb()))); + + // 乐观锁配置 + OptimisticLockerInnerInterceptor optimisticLockerInnerInterceptor = new OptimisticLockerInnerInterceptor(); + // 更新时校验乐观锁字段值 + optimisticLockerInnerInterceptor.setException(new OptimisticLockingFieldValueException("Optimistic field value is null")); + interceptor.addInnerInterceptor(optimisticLockerInnerInterceptor); + customizerObjectProvider.forEach(mybatisPlusInterceptorCustomizer -> mybatisPlusInterceptorCustomizer.customize(interceptor)); + return interceptor; + } + + @ConditionalOnMissingBean + @Bean + public AbQueryFilterInterceptor abQueryFilterInterceptor() { + return new AbQueryFilterInterceptor(); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/transaction/AbTransactionConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/transaction/AbTransactionConfiguration.java new file mode 100644 index 00000000..ae80a26e --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/transaction/AbTransactionConfiguration.java @@ -0,0 +1,74 @@ +package com.dstz.springboot.autoconfigure.transaction; + + +import org.springframework.aop.Advisor; +import org.springframework.aop.aspectj.AspectJExpressionPointcut; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.annotation.Role; +import org.springframework.transaction.TransactionDefinition; +import org.springframework.transaction.TransactionManager; +import org.springframework.transaction.annotation.AbstractTransactionManagementConfiguration; +import org.springframework.transaction.annotation.AnnotationTransactionAttributeSource; +import org.springframework.transaction.config.TransactionManagementConfigUtils; +import org.springframework.transaction.interceptor.*; + +/** + * ab 事务配置 + * + * @author wacxhs + */ +@Configuration +public class AbTransactionConfiguration extends AbstractTransactionManagementConfiguration { + + @Bean(name = "abTransactionInterceptor") + public TransactionInterceptor abTransactionInterceptor() { + DefaultTransactionAttribute requiredTransactionAttribute = new DefaultTransactionAttribute(); + requiredTransactionAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + + DefaultTransactionAttribute requiredReadonlyTransactionAttribute = new DefaultTransactionAttribute(); + requiredReadonlyTransactionAttribute.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED); + requiredReadonlyTransactionAttribute.setReadOnly(true); + + NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource(); + source.addTransactionalMethod("*", requiredTransactionAttribute); + source.addTransactionalMethod("get*", requiredReadonlyTransactionAttribute); + source.addTransactionalMethod("query*", requiredReadonlyTransactionAttribute); + source.addTransactionalMethod("find*", requiredReadonlyTransactionAttribute); + source.addTransactionalMethod("is*", requiredReadonlyTransactionAttribute); + source.addTransactionalMethod("select*", requiredReadonlyTransactionAttribute); + + TransactionInterceptor transactionInterceptor = new TransactionInterceptor(); + transactionInterceptor.setTransactionAttributeSources(annotationTransactionAttributeSource(), source); + transactionInterceptor.setTransactionManager(txManager); + return transactionInterceptor; + } + + + @Bean(name = "txAdviceAdvisor") + public Advisor txAdviceAdvisor() { + AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut(); + pointcut.setExpression("execution(* com.dstz..manager.*.*(..))||execution(* com.dstz.*.manager.*.*(..))"); + return new DefaultPointcutAdvisor(pointcut, abTransactionInterceptor()); + } + + @Bean(name = TransactionManagementConfigUtils.TRANSACTION_ADVISOR_BEAN_NAME) + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public BeanFactoryTransactionAttributeSourceAdvisor transactionAdvisor() { + BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor(); + advisor.setTransactionAttributeSource(annotationTransactionAttributeSource()); + advisor.setAdvice(abTransactionInterceptor()); + if (this.enableTx != null) { + advisor.setOrder(this.enableTx.getNumber("order")); + } + return advisor; + } + + @Bean + @Role(BeanDefinition.ROLE_INFRASTRUCTURE) + public TransactionAttributeSource annotationTransactionAttributeSource() { + return new AnnotationTransactionAttributeSource(); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/AbJackson2ObjectConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/AbJackson2ObjectConfiguration.java new file mode 100644 index 00000000..cad6b425 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/AbJackson2ObjectConfiguration.java @@ -0,0 +1,64 @@ +package com.dstz.springboot.autoconfigure.web; + +import cn.hutool.core.util.ObjectUtil; +import com.dstz.base.common.jackson.OnlyFilterProvider; +import com.dstz.base.common.utils.CastUtils; +import com.dstz.base.common.valuemap.BeanPropertyAnnotationValueMapFilter; +import com.dstz.base.common.valuemap.JacksonContainerSerializerDelegate; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.databind.BeanDescription; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationConfig; +import com.fasterxml.jackson.databind.ser.BeanSerializerModifier; +import com.fasterxml.jackson.databind.type.CollectionType; +import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer; +import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; +import org.apache.commons.lang3.ObjectUtils; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.boot.autoconfigure.jackson.JacksonProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.time.LocalDateTime; +import java.time.format.DateTimeFormatter; +import java.util.Locale; + +/** + * jackson 定制化配置 + * + * @author wacxhs + */ +@Configuration +public class AbJackson2ObjectConfiguration { + + + @Bean + public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer(JacksonProperties jacksonProperties) { + return jacksonObjectMapperBuilder -> { + // JDK 8 日期转换处理 + final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern(jacksonProperties.getDateFormat(), ObjectUtil.defaultIfNull(jacksonProperties.getLocale(), Locale.getDefault())); + jacksonObjectMapperBuilder.serializerByType(LocalDateTime.class, new LocalDateTimeSerializer(dateTimeFormatter)); + jacksonObjectMapperBuilder.deserializerByType(LocalDateTime.class, new LocalDateTimeDeserializer(dateTimeFormatter)); + + jacksonObjectMapperBuilder.serializationInclusion(JsonInclude.Include.NON_NULL); + jacksonObjectMapperBuilder.filters(new OnlyFilterProvider(new BeanPropertyAnnotationValueMapFilter())); + jacksonObjectMapperBuilder.postConfigurer(this::postObjectMapper); + }; + } + + private void postObjectMapper(ObjectMapper objectMapper) { + objectMapper.setSerializerFactory(objectMapper.getSerializerFactory().withSerializerModifier(new BeanSerializerModifier() { + @Override + public JsonSerializer modifySerializer(SerializationConfig config, BeanDescription beanDesc, JsonSerializer serializer) { + return serializer.withFilterId(ObjectUtils.NULL); + } + + @Override + public JsonSerializer modifyCollectionSerializer(SerializationConfig config, CollectionType valueType, BeanDescription beanDesc, JsonSerializer serializer) { + return new JacksonContainerSerializerDelegate(CastUtils.cast(serializer)); + } + })); + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/aspect/AbControllerAspectConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/aspect/AbControllerAspectConfiguration.java new file mode 100644 index 00000000..76a89a98 --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/aspect/AbControllerAspectConfiguration.java @@ -0,0 +1,40 @@ +package com.dstz.springboot.autoconfigure.web.aspect; + +import com.dstz.base.web.controller.aspect.AbControllerAspect; +import org.springframework.aop.Advisor; +import org.springframework.aop.Pointcut; +import org.springframework.aop.support.DefaultPointcutAdvisor; +import org.springframework.aop.support.StaticMethodMatcherPointcut; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.AnnotatedElementUtils; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; + +/** + * 控制器切面配置 + * + * @author wacxhs + */ +@ConditionalOnClass({HttpServletRequest.class, AbControllerAspect.class}) +@Configuration +public class AbControllerAspectConfiguration { + + @Bean + public Advisor abControllerAdvisor() { + AbControllerAspect abControllerAspect = new AbControllerAspect(); + Pointcut pointcut = new StaticMethodMatcherPointcut() { + @Override + public boolean matches(Method method, Class targetClass) { + return (targetClass.isAnnotationPresent(RestController.class) || method.isAnnotationPresent(ResponseBody.class)) && AnnotatedElementUtils.findMergedAnnotation(method, RequestMapping.class) != null; + } + }; + return new DefaultPointcutAdvisor(pointcut, abControllerAspect); + } + +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogConfiguration.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogConfiguration.java new file mode 100644 index 00000000..d808c78f --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogConfiguration.java @@ -0,0 +1,33 @@ +package com.dstz.springboot.autoconfigure.web.requestlog; + +import cn.hutool.core.util.ObjectUtil; +import com.dstz.base.common.requestlog.AbRequestLog; +import com.dstz.base.common.requestlog.AbRequestLogOutput; +import org.springframework.boot.autoconfigure.condition.ConditionalOnClass; +import org.springframework.boot.context.properties.EnableConfigurationProperties; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.util.Collections; + +/** + * 请求日志配置 + * + * @author wacxhs + */ +@ConditionalOnClass(AbRequestLog.class) +@EnableConfigurationProperties(AbRequestLogProperties.class) +@Configuration +public class AbRequestLogConfiguration { + + private final AbRequestLogProperties requestLogProperties; + + public AbRequestLogConfiguration(AbRequestLogProperties requestLogProperties) { + this.requestLogProperties = requestLogProperties; + } + + @Bean + public AbRequestLogOutput abRequestLogOutput() { + return new AbRequestLogOutput(requestLogProperties.getIncludeHeaderNames(), ObjectUtil.defaultIfNull(requestLogProperties.getSensitiveFields(), Collections.emptySet()), requestLogProperties.getExcludeUrls(), requestLogProperties.isEnableOutput()); + } +} diff --git a/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogProperties.java b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogProperties.java new file mode 100644 index 00000000..53a8863a --- /dev/null +++ b/ab-spring-boot/ab-spring-boot-starter/src/main/java/com/dstz/springboot/autoconfigure/web/requestlog/AbRequestLogProperties.java @@ -0,0 +1,66 @@ +package com.dstz.springboot.autoconfigure.web.requestlog; + +import org.springframework.boot.context.properties.ConfigurationProperties; + +import java.util.Set; + +/** + * 请求日志属性配置 + * + * @author wacxhs + */ +@ConfigurationProperties(prefix = "ab.request-log") +public class AbRequestLogProperties { + + /** + * 包含请求头名称,不配置则包含所有 + */ + private Set includeHeaderNames; + + /** + * 脱敏字段 + */ + private Set sensitiveFields; + + /** + * 排除地址 + */ + private String[] excludeUrls; + + /** + * 启用输出 + */ + private boolean enableOutput = true; + + public Set getIncludeHeaderNames() { + return includeHeaderNames; + } + + public void setIncludeHeaderNames(Set includeHeaderNames) { + this.includeHeaderNames = includeHeaderNames; + } + + public Set getSensitiveFields() { + return sensitiveFields; + } + + public void setSensitiveFields(Set sensitiveFields) { + this.sensitiveFields = sensitiveFields; + } + + public String[] getExcludeUrls() { + return excludeUrls; + } + + public void setExcludeUrls(String[] excludeUrls) { + this.excludeUrls = excludeUrls; + } + + public boolean isEnableOutput() { + return enableOutput; + } + + public void setEnableOutput(boolean enableOutput) { + this.enableOutput = enableOutput; + } +} diff --git a/ab-spring-boot/pom.xml b/ab-spring-boot/pom.xml new file mode 100644 index 00000000..2082b546 --- /dev/null +++ b/ab-spring-boot/pom.xml @@ -0,0 +1,23 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-spring-boot + pom + + + ab-spring-boot-starter + ab-spring-boot-starter-security + ab-spring-boot-app + + + + + + \ No newline at end of file diff --git a/ab-sys/ab-sys-api/pom.xml b/ab-sys/ab-sys-api/pom.xml new file mode 100644 index 00000000..8f696487 --- /dev/null +++ b/ab-sys/ab-sys-api/pom.xml @@ -0,0 +1,23 @@ + + + + ab-sys + com.dstz + 2.5.0 + + 4.0.0 + + ab-sys-api + + + com.dstz + ab-base-common + + + org.springframework + spring-jdbc + + + diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysAuthorizationApi.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysAuthorizationApi.java new file mode 100644 index 00000000..48116f1e --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysAuthorizationApi.java @@ -0,0 +1,39 @@ +package com.dstz.sys.api; + +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.api.dto.SysAuthorizationDTO; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +public interface SysAuthorizationApi { + + Set getUserRights(String userId); + + /** + * 获取授权sql + * + * @param userId 用户id + * @param targetKey 默认为Id_ 关联authorization的 targetId在数据库中的字段名字, + * @return + */ + Map getUserRightsSql(RightsObjectConstants rightsObject, String userId, String targetKey); + + + /** + * 通过业务获取授权集合 + * + * @param rightsObject 业务类型 + * @param rightsCode 业务ID + * @return + */ + List getAuthorizationByRights(RightsObjectConstants rightsObject, String rightsCode); + + /** + * 通过业务类型和业务ID删除授权 + * @param document + * @param id + */ + void deleteAuthorizationByRights(RightsObjectConstants document, String id); +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysConnectRecordApi.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysConnectRecordApi.java new file mode 100644 index 00000000..69283523 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysConnectRecordApi.java @@ -0,0 +1,57 @@ +package com.dstz.sys.api; + +import com.dstz.sys.api.dto.SysConnectRecordDTO; +import com.dstz.sys.api.vo.SysConnectRecordVO; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysConnectRecordApi + * @description: 公共业务关联记录 接口 + * @date 2022/3/815:43 + */ +@Validated +public interface SysConnectRecordApi { + + /** + * 通过 targetId 获取关联源 + * + * @param targetId + * @param type + * @return + */ + List getByTargetId(String targetId, String type); + + /** + * 批量保存 + * + * @param records + */ + void save(@Valid List records); + + /** + * 批量保存 + * + * @param records + */ + void save(@Valid SysConnectRecordDTO records); + + /** + * 通过sourceId 删除 + * + * @param type + * @param id + */ + void removeBySourceId(String id, String type); + + /** + * 检查是否被关联 + * + * @param targetId + * @param type + */ + void checkIsRelatedWithBusinessMessage(String targetId, String type); +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysDataDictApi.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysDataDictApi.java new file mode 100644 index 00000000..9eb32b54 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysDataDictApi.java @@ -0,0 +1,22 @@ +package com.dstz.sys.api; + +import com.dstz.sys.api.dto.DataDictDTO; + +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysDataDictApi + * @description: 字典 + * @date 2022/2/2317:13 + */ +public interface SysDataDictApi { + /** + * 获取字典列表 + * + * @param dictKey + * @param hasRoot + * @return + */ + List getDictNodeList(String dictKey, Boolean hasRoot); +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysFileApi.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysFileApi.java new file mode 100644 index 00000000..d617fbcd --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/SysFileApi.java @@ -0,0 +1,56 @@ +package com.dstz.sys.api; + +import com.dstz.sys.api.dto.SysFileDTO; + +import java.io.InputStream; + +/** + * @author jinxia.hou + * @Name SysFileApi + * @description:文件附件服务 + * @date 2022/2/249:35 + */ +public interface SysFileApi { + + /** + *
+     * 上传附件
+     * 
+ * + * @param is + * @param fileName + * @return + */ + SysFileDTO upload(InputStream is, String fileName); + + /** + *
+     * 下载附件
+     * 返回流
+     * 
+ * + * @param fileId + * @return + */ + InputStream download(String fileId); + + /** + *
+     * 删除附件
+     * 包括流信息
+     * 
+ * + * @param fileId + */ + void delete(String... fileId); + + /** + *
+     * 获取附件信息
+     * 
+ * + * @param fileId 文件ID + */ + SysFileDTO getById(String fileId); + +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/WorkCalendarApi.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/WorkCalendarApi.java new file mode 100644 index 00000000..9ac46f15 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/WorkCalendarApi.java @@ -0,0 +1,81 @@ +package com.dstz.sys.api; + +import com.dstz.sys.api.vo.WorkCalendarVO; + +import java.util.Date; +import java.util.List; + +/** + * @author jinxia.hou + * @Name WorkCalendarApi + * @description: 工作日历接口 + * @date 2022/3/1016:25 + */ +public interface WorkCalendarApi { + + /** + * 获取某一天日历信息 + * 可以判断是否为工作日,日历详情 + * + * @param day + * @return + */ + WorkCalendarVO getWorkCalendarByDay(Date day); + + /** + * 取某一天日历信息 + * + * @param day + * @param system + * @return + */ + WorkCalendarVO getWorkCalendarByDay(Date day, String system); + + + /** + * 通过时间区间返回 + * + * @param startDay + * @param endDay + * @return + */ + List getWorkCalendars(Date startDay, Date endDay); + + /** + * 通过时间区间返回 + * + * @param startDay + * @param endDay + * @param system + * @return + */ + List getWorkCalendars(Date startDay, Date endDay, String system); + + /** + * 获取指定工作日,N天数后的工作日期 + * + * @param startDay + * @param days + * @return + */ + Date getEndWorkDay(Date startDay, int days); + + /** + * 获取指定工作日,N天数后的工作日期 + * + * @param startDay + * @param days + * @param system + * @return + */ + Date getEndWorkDay(Date startDay, int days, String system); + + /** + * 获取多少分钟后的天数 + * + * @param startDay + * @param minute + * @return + */ + Date getEndWorkDayByMinute(Date startDay, int minute); +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/HolidayTypeConstants.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/HolidayTypeConstants.java new file mode 100644 index 00000000..2887b202 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/HolidayTypeConstants.java @@ -0,0 +1,36 @@ +package com.dstz.sys.api.constant; + +/** + * @author jinxia.hou + * @Name HolidayConstants + * @description: 节假日类型枚举 + * @date 2022/3/289:32 + */ +public enum HolidayTypeConstants { + + WORKDAY ("DW","工作日"), + + WEEKEND ("DR","周末"), + + LEGAL_HOLIDAY ("LR","法定节假日"), + + LEGAL_WORKDAY ( "LW","法定工作日"), + + COMPANY_HOLIDAY ( "CR","公司节假日"), + + COMPANY_WORKDAY("CW","公司工作日"), + ; + + private final String type; + private final String desc; + + HolidayTypeConstants(String type, String desc) { + this.type = type; + this.desc = desc; + } + + public String getType() { + return type; + } + public String getDesc() {return desc;} +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/OnlineDocMethod.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/OnlineDocMethod.java new file mode 100644 index 00000000..c8b37054 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/OnlineDocMethod.java @@ -0,0 +1,35 @@ +package com.dstz.sys.api.constant; + +/** + * @author jinxia.hou + * @Name OnlineDocMethod + * @description: 在线文档操作方法 + * @date 2023/5/2415:58 + */ +public enum OnlineDocMethod { + OPEN(1,"/api.do","打开文档"), + SAVE(2,"/api.do","保存文档"), + CLOSE(3,"/api.do","关闭文档"), + IS_OPEN(4,"/api.do","判断文档是否打开"), + + ; + + OnlineDocMethod(Integer key,String path,String desc) { + this.desc = desc; + this.key = key; + this.path = path; + } + + + + private String desc; + private String path; + private Integer key; + + public Integer getKey() { + return key; + } + public String getPath() { + return path; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/RightsObjectConstants.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/RightsObjectConstants.java new file mode 100644 index 00000000..a0eb3315 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/RightsObjectConstants.java @@ -0,0 +1,32 @@ +package com.dstz.sys.api.constant; + + +public enum RightsObjectConstants { + FLOW("FLOW","流程定义"), + CHART("CHART","自定义图表"), + WORKBENCH("WORKBENCH","工作台"), + HOME("HOME","首页组件"), + DOCUMENT("DOCUMENT","知识库"); + private final String key; + private final String label; + + RightsObjectConstants(String key,String label){ + this.key = key; + this.label = label; + } + public String key(){ + return key; + } + public String label(){ + return label; + } + + public static RightsObjectConstants getByKey(String key){ + for (RightsObjectConstants rights : RightsObjectConstants.values()) { + if(rights.key.equals(key)){ + return rights; + } + } + throw new RuntimeException(String.format(" key [%s] 对应RightsObjectConstants 不存在的权限常亮定义,请核查!",key)); + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysApiCodes.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysApiCodes.java new file mode 100644 index 00000000..ad577ff1 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysApiCodes.java @@ -0,0 +1,100 @@ +package com.dstz.sys.api.constant; + +import com.dstz.base.common.codes.IBaseCode; + +/** + * @author jinxia.hou + * @Name SysApiCodes + * @description: 系统设置状态码 + * @date 2022/2/1611:42 + */ +public enum SysApiCodes implements IBaseCode { + + KEY_WORD_DUPLICATE("CodeDuplicate", "{}系统中已存在!"), + CODE_DUPLICATE("CodeDuplicate", "编码已存在!"), + NAME_DUPLICATE("NameDuplicate", "名称已存在!"), + + + //系统属性相关 + NOT_FOND_ENV_PROPERTY("NotFoundEnvProperty", "查找不到正确的环境属性配置"), + + //数据字典相关 + DICT_KEY_TYPE_ERROR("DictKeyTypeError", "字典类型只能是dict/node"), + DICT_DELETE_ERROR("DictDeleteError", "系统内置【{}】不能删除"), + + //节假日 + WORK_CALENDAR_INITIAL_ERROR("WorkCalendarInitialError", "当前年份已经初始化过"), + WORK_CALENDAR_START_END_TIME_ERROR("WorkCalendarStartEndTimeError", "开始日期大于结束日期"), + WORK_CALENDAR_TIME_ERROR("WorkCalendarTimeError", "该时间段有日期已设定过不同节假日类型,请删除后添加或直接更新"), + WORK_CALENDAR_SYSTEM_ERROR("WorkCalendarSystemError", "系统与类型不一致"), + WORK_CALENDAR_CANT_NOT_CREATE("WorkCalendarCantNotCreate", "不支持创建"), + WORK_CALENDAR_CANT_NOT_UPDATE("WorkCalendarCantNotUpdate", "不支持更新"), + WORK_CALENDAR_YEAR_NOT_INITIAL("WorkCalendarYearNotInitial", "年份未初始化"), + HOLIDAY_CONF_NAME_DUPLICATE("HolidayConfNameDuplicate", "重复添加,相同日期内只能有一个同名节假日"), + HOLIDAY_CONF_NOT_FOUND("HolidayConfNotFound", "节假日配置不存在"), + WORKCALENDAR_NOT_INIT("sys-workCalendar-not-init","工作日计算不可用,请配置 {} 年节假日!"), + + //日程相关 + SCHEDULE_CONF_TIME_ERROR("ScheduleConfTimeError", "完成时间不能小于实际开始日期"), + + //工作交接相关 + WORK_HANDOVER_USER_ERROR("WorkHandoverUserError", "用户({})工作已交接,请勿指派为接收!"), + + //数据源相关 + DATA_SOURCE_CLASSPATH_ERROR("DataSourceClasspathError", "classPath[{}]不是javax.sql.DataSource的子类"), + DATA_SOURCE_CLASSPATH_PARAM_ERROR("DataSourceClasspathParamError", "根据classPath[{}]获取参数异常"), + DATA_SOURCE_NOT_FOUND_ERROR("DataSourceNotFoundError", "数据源({})不可用,请联系管理员"), + DATA_SOURCE_ATTRIBUTE_IS_EMPTY("DataSourceAttributeIsempty","数据源({})属性列表为空"), + DATASOURCE_CONNECTION_EXCEPTION("DatasourceConnectionException", "数据源连接异常:{}"), + + //文件操作相关 + FILE_NOT_FOUND_ERROR("FileNotFoundError", "附件【{}】不存在"), + FILE_NAME_LENGTH_ERROR("FileNameLengthError", "文件名字长度不能大于60字符"), + FILE_CREATE_DIR_ERROR("CanNotCreateDirectory","创建目录失败{}"), + FILE_OPEN_FILE_ERROR("OpenFileError","打开在线文档失败,【{}】"), + FILE_CREATE_FILE_ERROR("createFileError","创建文档失败!"), + + //审计日志相关 + OPERATE_LOG_REMOVE_ERROR("OperateLogRemoveError", "尚有操作日志[{}]条关联,请先删除操作日志再操作"), + + //流水号相关 + SERIAL_NO_CODE_NOT_FOUND_ERROR("SerialNoCodeNotFound", "找不到流水号的相关信息!"), + SERIAL_NO_GET_PARAM_ERROR("SerialNoGetParamError", "获取流水号参数错误,需要{}参数"), + SERIAL_NO_REVIVE_SAVE_ERROR("SerialNoReviveLogSaveError", "保存流水号规则记录失败,请联系管理员"), + SERIAL_NO_BUILD_ERROR("SerialNoBuildError", "生成流水号服务繁忙,请稍后再试!"), + SERIAL_NO_EXECUTE_SCRIPT_ERROR("SerialNoeExecuteScriptError", "流水号参数脚本执行错误请检查!{}"), + + //CMS相关 + NOTIFY_HAS_USED("NotifyHasUsed", "公告列表正在使用该类型,请先删除关联的公告列表!"), + REFLEX_WARNING("reflexWarning", "评论模块后台反射异常!"), + JSON_CONVERSION_ERROR("jsonConversionError", "翻译列表中,名为【{}】的数据存在异常,请重新修改此数据"), + TEMPLATE_CONVERSION_ERROR("templateConversionError", "模版数据转换出错,请检查消息模版内容!"), + INIT_DATA_CANT_DELETE("initDataCantDelete", "常用语[{}]为内置数据,无法删除!"), + KEY_REPEAT("keyRepeat", "别名[{}]与已有别名[{}]路径冲突"), + + CONNECT_RECORD_ERROR("ConnectRecordError", "信息:{}"), + ; + /** + * 数据源连接异常 + */ + + + private final String code; + + private final String message; + + SysApiCodes(String code, String message) { + this.code = code; + this.message = message; + } + + @Override + public String getCode() { + return this.code; + } + + @Override + public String getMessage() { + return this.message; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysCackeKeyConstant.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysCackeKeyConstant.java new file mode 100644 index 00000000..2e5173a8 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/constant/SysCackeKeyConstant.java @@ -0,0 +1,36 @@ +package com.dstz.sys.api.constant; +/** + * 系统缓存key定义 + * @author lightning + * @since 2022-08-17 + */ +public class SysCackeKeyConstant { + + + /** + * 字典方法缓存key + */ + public static final String GET_DICT_NODE_LIST = "getDictNodeListEl:"; + + /** + * 字典方法缓存key SpEL表达式 + */ + public static final String GET_DICT_NODE_LIST_EL = "'getDictNodeListEl:'"; + + /** + * 字典方法缓存key SpEL表达式 + */ + public static final String GET_DICT_NODE_LIST_RECEIVE_EL = GET_DICT_NODE_LIST_EL + ".concat(#root.args[0])"; + + + /** + * 消息模板方法缓存key SpEL表达式 + */ + public static final String GET_MESSAGE_TEMPLATE_LIST = "'getMessageTemplateListEl:'"; + + /** + * 字典方法缓存key SpEL表达式 + */ + public static final String ET_MESSAGE_TEMPLATE_LIST_RECEIVE_EL = GET_MESSAGE_TEMPLATE_LIST + ".concat(#root.args[0])"; + +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/DataDictDTO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/DataDictDTO.java new file mode 100644 index 00000000..8c778949 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/DataDictDTO.java @@ -0,0 +1,156 @@ +package com.dstz.sys.api.dto; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name DataDictDTO + * @description: + * @date 2022/2/2317:27 + */ +public class DataDictDTO implements Serializable { + private static final long serialVersionUID = -8765714844119300074L; + + /** + * ID + */ + private String id; + + /** + * 上级id + */ + private String parentId; + + /** + * 编码 + */ + private String code; + + /** + * name + */ + private String name; + + /** + * 字典key + */ + private String dictKey; + + /** + * 分组字典编码 + */ + private String typeCode; + + /** + * 排序 + */ + private Integer sn; + + /** + * dict/node字典项 + */ + private String dictType; + + /** + * 扩展字段1 + */ + private String extend1; + + /** + * 扩展字段2 + */ + private String extend2; + + /** + * 是否系统内置 + */ + private Integer isSystem; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDictKey() { + return dictKey; + } + + public void setDictKey(String dictKey) { + this.dictKey = dictKey; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getDictType() { + return dictType; + } + + public void setDictType(String dictType) { + this.dictType = dictType; + } + + public String getExtend1() { + return extend1; + } + + public void setExtend1(String extend1) { + this.extend1 = extend1; + } + + public String getExtend2() { + return extend2; + } + + public void setExtend2(String extend2) { + this.extend2 = extend2; + } + + public Integer getIsSystem() { + return isSystem; + } + + public void setIsSystem(Integer isSystem) { + this.isSystem = isSystem; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysAuthorizationDTO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysAuthorizationDTO.java new file mode 100644 index 00000000..8e300552 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysAuthorizationDTO.java @@ -0,0 +1,138 @@ +package com.dstz.sys.api.dto; + + +import java.io.Serializable; +import java.util.Date; + +/** + * 通用资源授权配置 + */ +public class SysAuthorizationDTO implements Serializable{ + + /** + * ID + */ + private String rightsId; + + /** + * 授权对象表分区用 + */ + private String rightsObject; + + /** + * 授权目标ID + */ + private String rightsTarget; + + /** + * 权限类型 + */ + private String rightsType; + + /** + * 授权标识 + */ + private String rightsIdentity; + + /** + * 标识名字 + */ + private String rightsIdentityName; + + /** + * 授权code=identity+type + */ + private String rightsPermissionCode; + + /** + * 创建时间 + */ + private Date rightsCreateTime; + + /** + * 创建人 + */ + public String getRightsId() { + return rightsId; + } + + public void setRightsId(String rightsId) { + this.rightsId = rightsId; + } + + public String getRightsObject() { + return rightsObject; + } + + public void setRightsObject(String rightsObject) { + this.rightsObject = rightsObject; + } + + public String getRightsTarget() { + return rightsTarget; + } + + public void setRightsTarget(String rightsTarget) { + this.rightsTarget = rightsTarget; + } + + public String getRightsType() { + return rightsType; + } + + public void setRightsType(String rightsType) { + this.rightsType = rightsType; + } + + public String getRightsIdentity() { + return rightsIdentity; + } + + public void setRightsIdentity(String rightsIdentity) { + this.rightsIdentity = rightsIdentity; + } + + public String getRightsIdentityName() { + return rightsIdentityName; + } + + public void setRightsIdentityName(String rightsIdentityName) { + this.rightsIdentityName = rightsIdentityName; + } + + public String getRightsPermissionCode() { + return rightsPermissionCode; + } + + public void setRightsPermissionCode(String rightsPermissionCode) { + this.rightsPermissionCode = rightsPermissionCode; + } + + public Date getRightsCreateTime() { + return rightsCreateTime; + } + + public void setRightsCreateTime(Date rightsCreateTime) { + this.rightsCreateTime = rightsCreateTime; + } + + public SysAuthorizationDTO() { + } + + public SysAuthorizationDTO(String rightsType, String rightsIdentity, String rightsIdentityName) { + this.rightsType = rightsType; + this.rightsIdentity = rightsIdentity; + this.rightsIdentityName = rightsIdentityName; + } + + public SysAuthorizationDTO(String rightsId, String rightsObject, String rightsTarget, String rightsType, String rightsIdentity, String rightsIdentityName, String rightsPermissionCode, Date rightsCreateTime) { + this.rightsId = rightsId; + this.rightsObject = rightsObject; + this.rightsTarget = rightsTarget; + this.rightsType = rightsType; + this.rightsIdentity = rightsIdentity; + this.rightsIdentityName = rightsIdentityName; + this.rightsPermissionCode = rightsPermissionCode; + this.rightsCreateTime = rightsCreateTime; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysConnectRecordDTO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysConnectRecordDTO.java new file mode 100644 index 00000000..0d353529 --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysConnectRecordDTO.java @@ -0,0 +1,91 @@ +package com.dstz.sys.api.dto; + +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name SysConnectRecordDTO + * @description: 公共业务关联记录DTO + * @date 2022/3/815:44 + */ +public class SysConnectRecordDTO implements Serializable { + private static final long serialVersionUID = 8745568231922260290L; + + /** + * 类型 + */ + @NotEmpty(message = "关联类型不能为空") + protected String type; + /** + * 源ID + */ + @NotEmpty(message = "关联源头ID不能为空") + protected String sourceId; + /** + * 关联ID + */ + @NotEmpty(message = "关联目标ID不能为空") + protected String targetId; + /** + * 提示信息 + */ + @NotEmpty(message = "关联提示信息不能为空") + protected String notice; + + + public void setType(String type) { + this.type = type; + } + + /** + * 返回 类型 + * + * @return + */ + public String getType() { + return this.type; + } + + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + /** + * 返回 源ID + * + * @return + */ + public String getSourceId() { + return this.sourceId; + } + + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + /** + * 返回 关联ID + * + * @return + */ + public String getTargetId() { + return this.targetId; + } + + + public void setNotice(String notice) { + this.notice = notice; + } + + /** + * 返回 提示信息 + * + * @return + */ + public String getNotice() { + return this.notice; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysFileDTO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysFileDTO.java new file mode 100644 index 00000000..7fc115db --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/dto/SysFileDTO.java @@ -0,0 +1,66 @@ +package com.dstz.sys.api.dto; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name SysFileDTO + * @description: + * @date 2022/2/249:37 + */ +public class SysFileDTO implements Serializable { + + + private static final long serialVersionUID = 645973812881769440L; + protected String id; + /** + * 附件名 + */ + private String name; + /** + *
+     * 这附件用的是上传器
+     * 具体类型可以看 IUploader 的实现类
+     * 
+ */ + private String uploader; + /** + *
+     * 路径,这个路径能从上传器中获取到对应的附件内容
+     * 所以也不一定是路径,根据不同上传器会有不同值
+     * 
+ */ + private String path; + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUploader() { + return uploader; + } + + public void setUploader(String uploader) { + this.uploader = uploader; + } + + public String getPath() { + return path; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public void setPath(String path) { + this.path = path; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/SysConnectRecordVO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/SysConnectRecordVO.java new file mode 100644 index 00000000..36c98e9a --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/SysConnectRecordVO.java @@ -0,0 +1,55 @@ +package com.dstz.sys.api.vo; + +import javax.validation.constraints.NotEmpty; +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name SysConnectRecordVO + * @description: + * @date 2022/3/815:50 + */ +public class SysConnectRecordVO implements Serializable { + private static final long serialVersionUID = 4047121912687250449L; + + + protected String type; + + protected String sourceId; + + protected String targetId; + + protected String notice; + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSourceId() { + return sourceId; + } + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getNotice() { + return notice; + } + + public void setNotice(String notice) { + this.notice = notice; + } +} diff --git a/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/WorkCalendarVO.java b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/WorkCalendarVO.java new file mode 100644 index 00000000..973fd06e --- /dev/null +++ b/ab-sys/ab-sys-api/src/main/java/com/dstz/sys/api/vo/WorkCalendarVO.java @@ -0,0 +1,64 @@ +package com.dstz.sys.api.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author jinxia.hou + * @Name WorkCalenDarVO + * @description: 工作日历VO + * @date 2022/3/1016:37 + */ +public class WorkCalendarVO implements Serializable { + private static final long serialVersionUID = 7796607053626856841L; + + private String id; + + private Date day; + + private String isWorkDay; + + private String type; + + private String system; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public Date getDay() { + return day; + } + + public void setDay(Date day) { + this.day = day; + } + + public String getIsWorkDay() { + return isWorkDay; + } + + public void setIsWorkDay(String isWorkDay) { + this.isWorkDay = isWorkDay; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSystem() { + return system; + } + + public void setSystem(String system) { + this.system = system; + } +} diff --git a/ab-sys/ab-sys-core/pom.xml b/ab-sys/ab-sys-core/pom.xml new file mode 100644 index 00000000..a3445654 --- /dev/null +++ b/ab-sys/ab-sys-core/pom.xml @@ -0,0 +1,54 @@ + + + + ab-sys + com.dstz + 2.5.0 + + 4.0.0 + + ab-sys-core + + + + com.dstz + ab-base-mapper + + + com.dstz + ab-sys-api + + + com.dstz + ab-groovy-script-api + + + org.springframework + spring-context + + + com.dstz + ab-base-web + + + com.dstz + ab-component-upload-api + ${project.version} + + + org.codehaus.groovy + groovy + + + org.lionsoul + ip2region + 2.7.0 + + + cn.hutool + hutool-http + + + diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysAuthorizationApiImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysAuthorizationApiImpl.java new file mode 100644 index 00000000..f8fdb22d --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysAuthorizationApiImpl.java @@ -0,0 +1,42 @@ +package com.dstz.sys.api; + +import cn.hutool.core.bean.BeanUtil; +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.api.dto.SysAuthorizationDTO; +import com.dstz.sys.core.manager.SysAuthorizationManager; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +@Service +public class SysAuthorizationApiImpl implements SysAuthorizationApi { + + private final SysAuthorizationManager sysAuthorizationManager; + + public SysAuthorizationApiImpl(SysAuthorizationManager sysAuthorizationManager) { + this.sysAuthorizationManager = sysAuthorizationManager; + } + + @Override + public Set getUserRights(String userId) { + return sysAuthorizationManager.getUserRights(userId); + } + + @Override + public Map getUserRightsSql(RightsObjectConstants rightsObject, String userId, String targetKey) { + return sysAuthorizationManager.getUserRightsSql(rightsObject, userId, targetKey); + } + + @Override + public List getAuthorizationByRights(RightsObjectConstants rightsObject, String rightsCode) { + return BeanUtil.copyToList(sysAuthorizationManager.getByTarget(rightsObject, rightsCode),SysAuthorizationDTO.class); + } + + @Override + public void deleteAuthorizationByRights(RightsObjectConstants rightsObject, String rightsTarget) { + sysAuthorizationManager.deleteAuthorizationByRights(rightsObject, rightsTarget); + } + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysConnectRecordApiImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysConnectRecordApiImpl.java new file mode 100644 index 00000000..f8f490d7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysConnectRecordApiImpl.java @@ -0,0 +1,83 @@ +package com.dstz.sys.api; + +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.api.dto.SysConnectRecordDTO; +import com.dstz.sys.api.vo.SysConnectRecordVO; +import com.dstz.sys.core.entity.SysConnectRecord; +import com.dstz.sys.core.manager.SysConnectRecordManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.validation.annotation.Validated; + +import javax.validation.Valid; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author jinxia.hou + * @Name SysConnectRecordServiceImpl + * @description: 公共业务关联记录 接口实现 + * @date 2022/3/815:48 + */ + +@Service +@Validated +public class SysConnectRecordApiImpl implements SysConnectRecordApi { + public SysConnectRecordApiImpl(SysConnectRecordManager connectRecordManager) { + this.connectRecordManager = connectRecordManager; + } + + private final SysConnectRecordManager connectRecordManager; + + @Override + public List getByTargetId(String id, String type) { + List list = connectRecordManager.getByTargetId(id, type); + return BeanCopierUtils.transformList(list, SysConnectRecordVO.class); + } + + @Override + public void save(@Valid List records) { + List recordsList = BeanCopierUtils.transformList(records, SysConnectRecord.class); + connectRecordManager.bulkCreate(recordsList); + } + + @Override + + public void save(@Valid SysConnectRecordDTO records) { + connectRecordManager.create(BeanCopierUtils.transformBean(records, SysConnectRecord.class)); + } + + + @Override + public void removeBySourceId(String sourceId, String type) { + connectRecordManager.removeBySourceId(sourceId, type); + } + + @Override + public void checkIsRelatedWithBusinessMessage(String targetId, String type) { + List list = connectRecordManager.getByTargetId(targetId, type); + if (list.isEmpty()) { + return; + } + + Set notices = new HashSet<>(); + + StringBuilder sb = new StringBuilder(); + list.forEach(record -> { + // 排下重 + if (!notices.contains(record.getNotice())) { + if (sb.length() > 0) { + sb.append("
"); + } + sb.append(record.getNotice()); + notices.add(record.getNotice()); + } + + }); + + throw new BusinessMessage(SysApiCodes.CONNECT_RECORD_ERROR.formatDefaultMessage(sb.toString())); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysDataDictApiImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysDataDictApiImpl.java new file mode 100644 index 00000000..73097152 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysDataDictApiImpl.java @@ -0,0 +1,47 @@ +package com.dstz.sys.api; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.sys.api.constant.SysCackeKeyConstant; +import com.dstz.sys.api.dto.DataDictDTO; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.sys.core.mapper.SysDataDictMapper; +import org.springframework.cache.annotation.Cacheable; +import org.springframework.stereotype.Service; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysDataDictServiceImpl + * @description: + * @date 2022/2/2317:33 + */ + +@Service +public class SysDataDictApiImpl implements SysDataDictApi { + + private final SysDataDictMapper sysDataDictMapper; + + public SysDataDictApiImpl(SysDataDictMapper sysDataDictMapper) { + this.sysDataDictMapper = sysDataDictMapper; + } + + @Override + @Cacheable(cacheNames = AbCacheRegionConstant.DICT_CACHE_REGION, key = SysCackeKeyConstant.GET_DICT_NODE_LIST_RECEIVE_EL) + public List getDictNodeList(String dictKey, Boolean hasRoot) { + List dictNodeList = sysDataDictMapper.getDictNodeList(dictKey, hasRoot); + if (dictNodeList == null) { + return null; + } + List dictList = new ArrayList(); + for (SysDataDict dataDict : dictNodeList) { + DataDictDTO dto = new DataDictDTO(); + BeanCopierUtils.copyProperties(dataDict, dto); + dictList.add(dto); + } + return dictList; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysFileApiImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysFileApiImpl.java new file mode 100644 index 00000000..651a569f --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/SysFileApiImpl.java @@ -0,0 +1,51 @@ +package com.dstz.sys.api; + +import cn.hutool.core.util.ArrayUtil; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.sys.api.dto.SysFileDTO; +import com.dstz.sys.core.entity.SysFile; +import com.dstz.sys.core.manager.SysFileManager; +import org.springframework.stereotype.Service; + +import java.io.InputStream; + +/** + * @author jinxia.hou + * @Name SysFileServiceImpl + * @description: 文件附件服务 + * @date 2022/2/249:38 + */ +@Service +public class SysFileApiImpl implements SysFileApi { + private final SysFileManager sysFileManager; + + public SysFileApiImpl(SysFileManager sysFileManager) { + this.sysFileManager = sysFileManager; + } + + @Override + public SysFileDTO upload(InputStream is, String fileName) { + SysFile file = sysFileManager.upload(is, fileName, null); + return BeanCopierUtils.transformBean(file, SysFileDTO.class); + } + + @Override + public InputStream download(String fileId) { + return sysFileManager.download(fileId); + } + + @Override + public void delete(String... fileId) { + if (ArrayUtil.isEmpty(fileId)) { + return; + } + for (String id : fileId) { + sysFileManager.delete(id); + } + } + + @Override + public SysFileDTO getById(String fileId) { + return BeanCopierUtils.transformBean(getById(fileId), SysFileDTO.class); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/WorkCalendarApiImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/WorkCalendarApiImpl.java new file mode 100644 index 00000000..acd5e55f --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/api/WorkCalendarApiImpl.java @@ -0,0 +1,54 @@ +package com.dstz.sys.api; + +import java.util.Date; +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.dstz.sys.api.vo.WorkCalendarVO; +@Service +public class WorkCalendarApiImpl implements WorkCalendarApi{ + + @Override + public WorkCalendarVO getWorkCalendarByDay(Date day) { + // TODO Auto-generated method stub + return null; + } + + @Override + public WorkCalendarVO getWorkCalendarByDay(Date day, String system) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getWorkCalendars(Date startDay, Date endDay) { + // TODO Auto-generated method stub + return null; + } + + @Override + public List getWorkCalendars(Date startDay, Date endDay, String system) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date getEndWorkDay(Date startDay, int days) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date getEndWorkDay(Date startDay, int days, String system) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Date getEndWorkDayByMinute(Date startDay, int minute) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysAuthorization.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysAuthorization.java new file mode 100644 index 00000000..a67ec525 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysAuthorization.java @@ -0,0 +1,163 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 通用资源授权配置 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@TableName("sys_authorization") +public class SysAuthorization extends AbModel { + + /** + * ID + */ + @TableId(value = "rights_id_", type = IdType.ASSIGN_ID) + private String rightsId; + + /** + * 授权对象表分区用 + */ + @TableField("rights_object_") + private String rightsObject; + + /** + * 授权目标ID + */ + @TableField("rights_target_") + private String rightsTarget; + + /** + * 权限类型 + */ + @TableField("rights_type_") + private String rightsType; + + /** + * 授权标识 + */ + @TableField("rights_identity_") + private String rightsIdentity; + + /** + * 标识名字 + */ + @TableField("rights_identity_name_") + private String rightsIdentityName; + + /** + * 授权code=identity+type + */ + @TableField("rights_permission_code_") + private String rightsPermissionCode; + + /** + * 创建时间 + */ + @TableField("rights_create_time_") + private Date rightsCreateTime; + + /** + * 创建人 + */ + @TableField("rights_create_by_") + private String rightsCreateBy; + + public String getRightsId() { + return rightsId; + } + + public void setRightsId(String rightsId) { + this.rightsId = rightsId; + } + + public String getRightsObject() { + return rightsObject; + } + + public void setRightsObject(String rightsObject) { + this.rightsObject = rightsObject; + } + + public String getRightsTarget() { + return rightsTarget; + } + + public void setRightsTarget(String rightsTarget) { + this.rightsTarget = rightsTarget; + } + + public String getRightsType() { + return rightsType; + } + + public void setRightsType(String rightsType) { + this.rightsType = rightsType; + } + + public String getRightsIdentity() { + return rightsIdentity; + } + + public void setRightsIdentity(String rightsIdentity) { + this.rightsIdentity = rightsIdentity; + } + + public String getRightsIdentityName() { + return rightsIdentityName; + } + + public void setRightsIdentityName(String rightsIdentityName) { + this.rightsIdentityName = rightsIdentityName; + } + + public String getRightsPermissionCode() { + return rightsPermissionCode; + } + + public void setRightsPermissionCode(String rightsPermissionCode) { + this.rightsPermissionCode = rightsPermissionCode; + } + + public Date getRightsCreateTime() { + return rightsCreateTime; + } + + public void setRightsCreateTime(Date rightsCreateTime) { + this.rightsCreateTime = rightsCreateTime; + } + + public String getRightsCreateBy() { + return rightsCreateBy; + } + + public void setRightsCreateBy(String rightsCreateBy) { + this.rightsCreateBy = rightsCreateBy; + } + + @Override + public Serializable pkVal() { + return this.rightsId; + } + + @Override + public void setId(String id) { + setRightsId(id); + } + + @Override + public String getId() { + return getRightsId(); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConfiguration.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConfiguration.java new file mode 100644 index 00000000..87e77061 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConfiguration.java @@ -0,0 +1,174 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.dstz.base.entity.AbModel; +import java.util.Date; +import java.io.Serializable; + +/** + *

+ * + *

+ * + * @author lightning + * @since 2023-05-11 + */ +@TableName("sys_configuration") +public class SysConfiguration extends AbModel { + + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 配置类型 + */ + @TableField("conf_type_") + private String confType; + + /** + * 配置所属环境 + */ + @TableField("conf_env_") + private String confEnv; + + /** + * 配置json + */ + @TableField("conf_json_") + private String confJson; + + /** + * 是否可用 + */ + @TableField("is_enable_") + private Integer isEnable; + + /** + * 是否加密 + */ + @TableField("is_encrypt_") + private Integer isEncrypt; + + /** + * 创建人 + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 修改人 + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 修改时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getConfType() { + return confType; + } + + public void setConfType(String confType) { + this.confType = confType; + } + + public String getConfEnv() { + return confEnv; + } + + public void setConfEnv(String confEnv) { + this.confEnv = confEnv; + } + + public String getConfJson() { + return confJson; + } + + public void setConfJson(String confJson) { + this.confJson = confJson; + } + + public Integer getIsEnable() { + return isEnable; + } + + public void setIsEnable(Integer isEnable) { + this.isEnable = isEnable; + } + + public Integer getIsEncrypt() { + return isEncrypt; + } + + public void setIsEncrypt(Integer isEncrypt) { + this.isEncrypt = isEncrypt; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConnectRecord.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConnectRecord.java new file mode 100644 index 00000000..71a61638 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysConnectRecord.java @@ -0,0 +1,107 @@ +package com.dstz.sys.core.entity; + +import com.dstz.base.entity.IPersistentEntity; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.activerecord.Model; + +import java.io.Serializable; + +/** + *

+ * 公共业务关联记录 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@TableName("sys_connect_record") +public class SysConnectRecord extends Model implements IPersistentEntity { + + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 类型 + */ + @TableField("type_") + private String type; + + /** + * 源ID + */ + @TableField("source_id_") + private String sourceId; + + /** + * 关联ID + */ + @TableField("target_id_") + private String targetId; + + /** + * 提示信息 + */ + @TableField("notice") + private String notice; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getSourceId() { + return sourceId; + } + + public void setSourceId(String sourceId) { + this.sourceId = sourceId; + } + + public String getTargetId() { + return targetId; + } + + public void setTargetId(String targetId) { + this.targetId = targetId; + } + + public String getNotice() { + return notice; + } + + public void setNotice(String notice) { + this.notice = notice; + } + + @Override + public Serializable pkVal() { + return this.id; + } + + @Override + public String toString() { + return "SysConnectRecord{" + + "id=" + id + + ", type=" + type + + ", sourceId=" + sourceId + + ", targetId=" + targetId + + ", notice=" + notice + + "}"; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDailyPhrases.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDailyPhrases.java new file mode 100644 index 00000000..7ff61108 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDailyPhrases.java @@ -0,0 +1,138 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.util.Date; + +/** + *

+ * 用户常用语 + *

+ * + * @author niu + * @since 2022-03-14 + */ +@TableName("sys_daily_phrases") +public class SysDailyPhrases extends AbModel { + + /** + * 主键id非空 + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 常用语文本 + */ + @TableField("locution_") + private String locution; + + /** + * 是否启用 + */ + @TableField("enable_") + private Integer enable; + + /** + * 是否默认 0自定义 1默认 + */ + @TableField("is_default_") + private Integer isDefault = 0; + + /** + * 创建者 + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 创建者姓名 + */ + @TableField(value = "creator_", fill = FieldFill.INSERT) + private String creator; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 修改时间 + */ + @TableField(value = "update_time_", fill = FieldFill.UPDATE) + private Date updateTime; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getLocution() { + return locution; + } + + public void setLocution(String locution) { + this.locution = locution; + } + + public Integer getEnable() { + return enable; + } + + public void setEnable(Integer enable) { + this.enable = enable; + } + + public Integer getIsDefault() { + return isDefault; + } + + public void setIsDefault(Integer isDefault) { + this.isDefault = isDefault; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreator() { + return creator; + } + + public void setCreator(String creator) { + this.creator = creator; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDataDict.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDataDict.java new file mode 100644 index 00000000..4c664ac7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysDataDict.java @@ -0,0 +1,286 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 数据字典 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +@TableName("sys_data_dict") +public class SysDataDict extends AbModel { + + public static final String TYPE_DICT = "dict"; + public static final String TYPE_NODE = "node"; + + /** + * 数据字典左边树分类code + */ + public static final String TYPE_CODE = "system"; + /** + * 系統内置分类code + */ + public static final String SYSTEM_DEFAULT_TYPE="systemDefault"; + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 上级id + */ + @TableField("parent_id_") + private String parentId = "0"; + + /** + * 编码 + */ + @TableField("code_") + private String code; + + /** + * name + */ + @TableField("name_") + private String name; + + /** + * 字典key + */ + @TableField("dict_key_") + private String dictKey; + + /** + * 分组字典编码 + */ + @TableField("type_code_") + private String typeCode; + + /** + * 排序 + */ + @TableField("sn_") + private Integer sn; + + /** + * dict/node字典项 + */ + @TableField("dict_type_") + private String dictType; + + /** + * 扩展字段1 + */ + @TableField("extend1") + private String extend1; + + /** + * 扩展字段2 + */ + @TableField("extend2") + private String extend2; + + /** + * 是否系统内置 + */ + @TableField("is_system_") + private Integer isSystem = 0; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDictKey() { + return dictKey; + } + + public void setDictKey(String dictKey) { + this.dictKey = dictKey; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getDictType() { + return dictType; + } + + public void setDictType(String dictType) { + this.dictType = dictType; + } + + public Integer getIsSystem() { + return isSystem; + } + + public void setIsSystem(Integer isSystem) { + this.isSystem = isSystem; + } + + public String getExtend1() { + return extend1; + } + + public void setExtend1(String extend1) { + this.extend1 = extend1; + } + + public String getExtend2() { + return extend2; + } + + public void setExtend2(String extend2) { + this.extend2 = extend2; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysFile.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysFile.java new file mode 100644 index 00000000..6d1e494f --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysFile.java @@ -0,0 +1,207 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 系统附件 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@TableName("sys_file") +public class SysFile extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 附件名 + */ + @TableField("name_") + private String name; + + /** + * 上传器 + */ + @TableField("uploader_") + private String uploader; + + /** + * 附件路径 + */ + @TableField("path_") + private String path; + + /** + * 字典分类 + */ + @AbValueMap(type = AbValueMapType.DICT, fixValue = "fileType", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + @TableField("type_code_") + private String typeCode; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + /** + * 逻辑删除标记 + */ + @TableField("delete_") + private Integer delete; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getUploader() { + return uploader; + } + + public void setUploader(String uploader) { + this.uploader = uploader; + } + + public String getPath() { + return path; + } + + public void setPath(String path) { + this.path = path; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public Integer getDelete() { + return delete; + } + + public void setDelete(Integer delete) { + this.delete = delete; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysLogErr.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysLogErr.java new file mode 100644 index 00000000..4192df60 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysLogErr.java @@ -0,0 +1,182 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 系统异常日志 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@TableName("sys_log_err") +public class SysLogErr extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 帐号 + */ + @TableField("account_") + private String account; + + /** + * IP来源 + */ + @TableField("ip_") + private String ip; + + /** + * IP地址 + */ + @TableField("ip_address_") + private String ipAddress; + + /** + * 状态:unchecked,checked,fixed + */ + @TableField("status_") + private String status = "unchecked"; + + /** + * 错误URL + */ + @TableField("url_") + private String url; + + /** + * 出错信息 + */ + @TableField("content_") + private String content; + + /** + * 请求Head + */ + @TableField("heads_") + private String heads; + + /** + * 请求参数 + */ + @TableField("request_param_") + private String requestParam; + + /** + * 出错时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 出错异常堆栈 + */ + @TableField("stack_trace_") + private String stackTrace; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getAccount() { + return account; + } + + public void setAccount(String account) { + this.account = account; + } + + public String getIp() { + return ip; + } + + public void setIp(String ip) { + this.ip = ip; + } + + public String getIpAddress() { + return ipAddress; + } + + public void setIpAddress(String ipAddress) { + this.ipAddress = ipAddress; + } + + public String getStatus() { + return status; + } + + public void setStatus(String status) { + this.status = status; + } + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getContent() { + return content; + } + + public void setContent(String content) { + this.content = content; + } + + public String getHeads() { + return heads; + } + + public void setHeads(String heads) { + this.heads = heads; + } + + public String getRequestParam() { + return requestParam; + } + + public void setRequestParam(String requestParam) { + this.requestParam = requestParam; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getStackTrace() { + return stackTrace; + } + + public void setStackTrace(String stackTrace) { + this.stackTrace = stackTrace; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysProperties.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysProperties.java new file mode 100644 index 00000000..d2643d35 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysProperties.java @@ -0,0 +1,236 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 系统属性 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +@TableName("sys_properties") +public class SysProperties extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 属性编码 + */ + @TableField("code_") + private String code; + + /** + * 属性名字 + */ + @TableField("name_") + private String name; + + /** + * 值 + */ + @TableField("value_") + private String value; + + /** + * 描述 + */ + @TableField("desc_") + private String desc; + + /** + * 是否加密 + */ + @TableField("encrypt_") + private Integer encrypt; + + /** + * 环境参数 + */ + @TableField("environment_") + @AbValueMap(type = AbValueMapType.DICT, fixValue = "environment", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + private String environment; + + /** + * 分组 + */ + @TableField("type_code_") + // @AbValueMap(type = AbValueMapType.DICT, fixValue = "property", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + private String typeCode; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public Integer getEncrypt() { + return encrypt; + } + + public void setEncrypt(Integer encrypt) { + this.encrypt = encrypt; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysScript.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysScript.java new file mode 100644 index 00000000..d70d82c3 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/entity/SysScript.java @@ -0,0 +1,193 @@ +package com.dstz.sys.core.entity; + +import com.baomidou.mybatisplus.annotation.*; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.entity.AbModel; + +import java.io.Serializable; +import java.util.Date; + +/** + *

+ * 常用脚本 + *

+ * + * @author wacxhs + * @since 2022-01-25 + */ +@TableName("sys_script") +public class SysScript extends AbModel { + + /** + * ID + */ + @TableId(value = "id_", type = IdType.ASSIGN_ID) + private String id; + + /** + * 脚本名称 + */ + @TableField("name_") + private String name; + + /** + * 脚本 + */ + @TableField("script_") + private String script; + + /** + * 分类字典编码 + */ + @TableField("type_code_") + @AbValueMap(type = AbValueMapType.DICT, fixValue="script", matchField = "code", attrMap = @AbValueMap.AttrMap(originName = "name")) + private String typeCode; + + /** + * 描述 + */ + @TableField("desc_") + private String desc; + + /** + * 创建时间 + */ + @TableField(value = "create_time_", fill = FieldFill.INSERT) + private Date createTime; + + /** + * 创建人ID + */ + @TableField(value = "create_by_", fill = FieldFill.INSERT) + private String createBy; + + /** + * 所属组织 + */ + @TableField(value = "create_org_id_", fill = FieldFill.INSERT) + private String createOrgId; + + /** + * 更新时间 + */ + @TableField(value = "update_time_", fill = FieldFill.INSERT_UPDATE) + private Date updateTime; + + /** + * 更新人 + */ + @TableField(value = "updater_", fill = FieldFill.INSERT_UPDATE) + private String updater; + + /** + * 更新人ID + */ + @TableField(value = "update_by_", fill = FieldFill.INSERT_UPDATE) + private String updateBy; + + @Override + public String getId() { + return id; + } + + @Override + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getScript() { + return script; + } + + public void setScript(String script) { + this.script = script; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + @Override + public Date getCreateTime() { + return createTime; + } + + @Override + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + @Override + public String getCreateBy() { + return createBy; + } + + @Override + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public String getCreateOrgId() { + return createOrgId; + } + + public void setCreateOrgId(String createOrgId) { + this.createOrgId = createOrgId; + } + + @Override + public Date getUpdateTime() { + return updateTime; + } + + @Override + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + @Override + public String getUpdater() { + return updater; + } + + @Override + public void setUpdater(String updater) { + this.updater = updater; + } + + @Override + public String getUpdateBy() { + return updateBy; + } + + @Override + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + @Override + public Serializable pkVal() { + return this.id; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysAuthorizationManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysAuthorizationManager.java new file mode 100644 index 00000000..fe1bae08 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysAuthorizationManager.java @@ -0,0 +1,37 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.core.entity.SysAuthorization; +import com.dstz.sys.rest.model.dto.AuthorizationDTO; + +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + *

+ * 通用资源授权配置 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +public interface SysAuthorizationManager extends AbBaseManager { + Set getUserRights(String userId); + + /** + * 获取授权sql + * + * @param userId 用户id + * @param targetKey 默认为Id_ 关联authorization的 targetId在数据库中的字段名字, + * @return + */ + Map getUserRightsSql(RightsObjectConstants rightsObject, String userId, String targetKey); + + List getByTarget(RightsObjectConstants rightsObject, String rightsTarget); + + void createAll(AuthorizationDTO saveDto); + + void deleteAuthorizationByRights(RightsObjectConstants rightsObject, String rightsTarget); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConfigurationManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConfigurationManager.java new file mode 100644 index 00000000..a91b23db --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConfigurationManager.java @@ -0,0 +1,23 @@ +package com.dstz.sys.core.manager; + +import com.dstz.sys.core.entity.SysConfiguration; +import com.dstz.base.manager.AbBaseManager; + +/** + *

+ * 通用业务类 + *

+ * + * @author lightning + * @since 2023-05-11 + */ +public interface SysConfigurationManager extends AbBaseManager { + + /** + * 根据编码获取系统配置 + * @param code 配置编码 + * @return 配置信息 + */ + String getConfByCode(String code); + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConnectRecordManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConnectRecordManager.java new file mode 100644 index 00000000..33a28c5d --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysConnectRecordManager.java @@ -0,0 +1,34 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysConnectRecord; + +import java.util.List; + +/** + *

+ * 公共业务关联记录 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +public interface SysConnectRecordManager extends AbBaseManager { + + /** + * 根据目标Id 和类型获取 + * + * @param targetId + * @param type + * @return + */ + List getByTargetId(String targetId, String type); + + /** + * 根据源Id删除记录 + * + * @param sourceId + * @param type + */ + void removeBySourceId(String sourceId, String type); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDailyPhrasesManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDailyPhrasesManager.java new file mode 100644 index 00000000..0fe5fca3 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDailyPhrasesManager.java @@ -0,0 +1,39 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysDailyPhrases; + +import java.util.List; + +/** + *

+ * 用户常用语 通用业务类 + *

+ * + * @author niu + * @since 2022-03-14 + */ +public interface SysDailyPhrasesManager extends AbBaseManager { + + /** + * 分页查询 + * + * @param paramDTO 查询参数 + */ + PageListDTO listJson(QueryParamDTO paramDTO); + + /** + * 当前用户启用的所有常用语 + * + */ + List enableList(); + + /** + * 新增或修改 + * + * @param sysDailyPhrases 常用语对象 + */ + void saveOrUpdate(SysDailyPhrases sysDailyPhrases); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataDictManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataDictManager.java new file mode 100644 index 00000000..43bbcc50 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataDictManager.java @@ -0,0 +1,55 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.sys.rest.model.vo.SysDataDictVO; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.base.manager.AbBaseManager; + +import java.util.List; + +/** + *

+ * 数据字典 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +public interface SysDataDictManager extends AbBaseManager { + /** + * 通过dicKey获取字典项。若hasRoot则包含字典本身 + * @param dictKey + * @param hasRoot + * @return + */ + List getDictNodeList(String dictKey, Boolean hasRoot); + + /** + * 获取字典树 + * @param paramDTO + * @return + */ + List getDictTree(QueryParamDTO paramDTO); + + /** + * 获取字典分类及下面的字典,构造成树桩数据结构 + * @return + */ + List getDictTypeTree(); + + /** + * 新增 + * @param entry + * @return id + */ + @Override + int create(SysDataDict entry); + + /** + * 修改 + * @param entry + * @return id + */ + @Override + int update(SysDataDict entry); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataSourceManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataSourceManager.java new file mode 100644 index 00000000..8b5687e5 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysDataSourceManager.java @@ -0,0 +1,21 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.query.AbQueryFilter; + +/** + * 系统数据源通用业务处理 + * + * @author wacxhs + */ +public interface SysDataSourceManager { + + /** + * 分页列表 + * + * @param queryFilter 分页 + * @return 分页列表 + */ + PageListDTO query(AbQueryFilter queryFilter); + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysFileManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysFileManager.java new file mode 100644 index 00000000..2d977e84 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysFileManager.java @@ -0,0 +1,79 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysFile; +import com.dstz.sys.rest.model.dto.OperateOnlineDocDTO; +import com.dstz.sys.rest.model.dto.UpdateFileDTO; + +import java.io.IOException; +import java.io.InputStream; + +/** + *

+ * 系统附件 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +public interface SysFileManager extends AbBaseManager { + /** + *
+     * 上传附件
+     * 
+ * + * @param is + * @param fileName + * @param type + * @return + */ + SysFile upload(InputStream is, String fileName, String type); + + /** + *
+     * 下载附件
+     * 返回流
+     * 
+ * + * @param fileId + * @return + */ + InputStream download(String fileId); + + /** + *
+     * 删除附件
+     * 包括流信息
+     * 
+ * + * @param fileId + */ + void delete(String fileId); + + /** + *
+     * 更新附件
+     * 更新只是更新数据 sys_file 数据中的path id 不会更改,
+     * 会一直新增 db_upload 表中的数据
+     * 
+ * + * @param updateDTO + * @return + */ + int update(UpdateFileDTO updateDTO); + + /** + * 打开在线文档 + * @param operateDTO + * @param requestUrl agileBpm 服务地址 + * @return + */ + String openFile(OperateOnlineDocDTO operateDTO,String requestUrl); + + /** + * 关闭在线文档 + * @param operateDTO + * @return + */ + Boolean closeFile(OperateOnlineDocDTO operateDTO); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysLogErrManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysLogErrManager.java new file mode 100644 index 00000000..c1806bce --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysLogErrManager.java @@ -0,0 +1,16 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysLogErr; + +/** + *

+ * 系统异常日志 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +public interface SysLogErrManager extends AbBaseManager { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysPropertiesManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysPropertiesManager.java new file mode 100644 index 00000000..8cb84901 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysPropertiesManager.java @@ -0,0 +1,28 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysProperties; + +/** + *

+ * 系统属性 通用业务类 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +public interface SysPropertiesManager extends AbBaseManager { + + /** + * 判断别名是否存在。 + * + * @param sysProperties + * @return + */ + boolean isExist(SysProperties sysProperties); + + /** + * 重新读取属性配置。 + */ + void reloadProperty(); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysScriptManager.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysScriptManager.java new file mode 100644 index 00000000..bb639c7c --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/SysScriptManager.java @@ -0,0 +1,16 @@ +package com.dstz.sys.core.manager; + +import com.dstz.base.manager.AbBaseManager; +import com.dstz.sys.core.entity.SysScript; + +/** + *

+ * 常用脚本 通用业务类 + *

+ * + * @author wacxhs + * @since 2022-01-25 + */ +public interface SysScriptManager extends AbBaseManager { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysAuthorizationManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysAuthorizationManagerImpl.java new file mode 100644 index 00000000..42761ca8 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysAuthorizationManagerImpl.java @@ -0,0 +1,139 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.collection.CollectionUtil; +import cn.hutool.core.collection.IterUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.org.api.GroupApi; +import com.dstz.org.api.model.IGroup; +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.core.entity.SysAuthorization; +import com.dstz.sys.core.manager.SysAuthorizationManager; +import com.dstz.sys.core.mapper.SysAuthorizationMapper; +import com.dstz.sys.rest.model.dto.AuthorizationDTO; +import org.springframework.stereotype.Service; + +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * 通用资源授权配置 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Service("sysAuthorizationManager") +public class SysAuthorizationManagerImpl extends AbBaseManagerImpl implements SysAuthorizationManager { + + + private static final String RIGHT_TYPE_USER = "user"; + private static final String RIGHT_TYPE_ALL = "all"; + + private final SysAuthorizationMapper sysAuthorizationMapper; + private final GroupApi groupApi; + + public SysAuthorizationManagerImpl(SysAuthorizationMapper sysAuthorizationMapper, GroupApi groupApi) { + this.sysAuthorizationMapper = sysAuthorizationMapper; + this.groupApi = groupApi; + } + + /** + * 获取用户的权限 + * type-vale + */ + @Override + public Set getUserRights(String userId) { + List list = groupApi.getByUserId(userId); + + Set rights = new HashSet(); + rights.add(String.format("%s-%s", userId, RIGHT_TYPE_USER)); + rights.add(String.format("%s-%s", RIGHT_TYPE_USER, RIGHT_TYPE_ALL)); + + + if (CollectionUtil.isEmpty(list)) { + return rights; + } + + for (IGroup group : list) { + rights.add(String.format("%s-%s", group.getGroupId(), group.getGroupType())); + } + + return rights; + } + + + /** + * 获取用户的权限sql, sql中需要使用 ${rightsSql},若不使用可以使用 Set rights, 自行拼装 + * + * @param rightsObject + * @param userId 用户id + * @param targetKey 默认为Id_ 关联authorization的 targetId在数据库中的字段名字, + * @return @rightsSql = inner join sys_authorization rights on id_ = rights.rights_id_ and rights_permission_code_ in ( id-type,groupid-type ) + */ + @Override + public Map getUserRightsSql(RightsObjectConstants rightsObject, String userId, String targetKey) { + if (StrUtil.isEmpty(targetKey)) { + targetKey = "id_"; + } + + StringBuilder sb = new StringBuilder(); + + Set rights = getUserRights(userId); + + for (String r : rights) { + if (sb.length() > 0) { + sb.append(","); + } + sb.append("'").append(r).append("'"); + } + sb.insert(0, "inner join sys_authorization rights on " + targetKey + " = rights.rights_target_ and rights.rights_object_ ='" + rightsObject.key() + "' and rights_permission_code_ in ( "); + sb.append(")"); + + Map param = new HashMap(); + param.put("rightsSql", sb.toString()); + + param.put("rights", rights); + + return param; + } + + + @Override + public List getByTarget(RightsObjectConstants rightsObject, String rightsTarget) { + return sysAuthorizationMapper.getByTarget(rightsObject.key(), rightsTarget); + } + + + @Override + public void createAll(AuthorizationDTO saveDto) { + sysAuthorizationMapper.deleteByTarget(saveDto.getRightsObject().key(), saveDto.getRightsTarget()); + final Date nowTime = new Date(); + final String currentUserId = UserContextUtils.getUserId(); + Iterator sysAuthorizationIterator = saveDto.getAuthorizationList().stream().peek(authorization -> { + authorization.setRightsPermissionCode(String.format("%s-%s", authorization.getRightsIdentity(), authorization.getRightsType())); + if (StrUtil.isEmpty(authorization.getRightsObject())) { + authorization.setRightsObject(saveDto.getRightsObject().key()); + } + if (StrUtil.isEmpty(authorization.getRightsTarget())) { + authorization.setRightsTarget(saveDto.getRightsTarget()); + } + authorization.setRightsCreateBy(currentUserId); + authorization.setRightsCreateTime(nowTime); + }).iterator(); + bulkCreate(IterUtil.asIterable(sysAuthorizationIterator)); + } + + @Override + public void deleteAuthorizationByRights(RightsObjectConstants rightsObject, String rightsTarget) { + remove(Wrappers.lambdaQuery() + .eq(SysAuthorization::getRightsObject, rightsObject) + .eq(SysAuthorization::getRightsTarget, rightsTarget)); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConfigurationManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConfigurationManagerImpl.java new file mode 100644 index 00000000..6229f1e6 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConfigurationManagerImpl.java @@ -0,0 +1,27 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.sys.core.entity.SysConfiguration; +import com.dstz.sys.core.manager.SysConfigurationManager; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 通用服务实现类 + * + * @author lightning + * @since 2023-05-11 + */ +@Service("sysConfigurationManager") +public class SysConfigurationManagerImpl extends AbBaseManagerImpl implements SysConfigurationManager { + + @Override + public String getConfByCode(String code) { + List sysConfigurationList = this.selectByWrapper(Wrappers.lambdaQuery(SysConfiguration.class).eq(SysConfiguration::getConfType,code).eq(SysConfiguration::getIsEnable, NumberPool.INTEGER_ONE)); + return CollUtil.isEmpty(sysConfigurationList)?"":sysConfigurationList.get(0).getConfJson(); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConnectRecordManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConnectRecordManagerImpl.java new file mode 100644 index 00000000..0458c78c --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysConnectRecordManagerImpl.java @@ -0,0 +1,47 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.sys.core.entity.SysConnectRecord; +import com.dstz.sys.core.manager.SysConnectRecordManager; +import com.dstz.sys.core.mapper.SysConnectRecordMapper; +import org.springframework.stereotype.Service; + +import java.util.List; + +/** + * 公共业务关联记录 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Service("sysConnectRecordManager") +public class SysConnectRecordManagerImpl extends AbBaseManagerImpl implements SysConnectRecordManager { + + private final SysConnectRecordMapper sysConnectRecordMapper; + + public SysConnectRecordManagerImpl(SysConnectRecordMapper sysConnectRecordMapper) { + this.sysConnectRecordMapper = sysConnectRecordMapper; + } + + @Override + public List getByTargetId(String targetId, String type) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("target_id_", targetId); + if (StrUtil.isNotEmpty(type)) { + queryWrapper.eq("type_", type); + } + return sysConnectRecordMapper.selectList(queryWrapper); + } + + @Override + public void removeBySourceId(String sourceId, String type) { + QueryWrapper queryWrapper = new QueryWrapper(); + queryWrapper.eq("source_id_", sourceId); + if (StrUtil.isNotEmpty(type)) { + queryWrapper.eq("type_", type); + } + sysConnectRecordMapper.delete(queryWrapper); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDailyPhrasesManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDailyPhrasesManagerImpl.java new file mode 100644 index 00000000..08b1fafc --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDailyPhrasesManagerImpl.java @@ -0,0 +1,110 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.map.MapUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.core.entity.SysDailyPhrases; +import com.dstz.sys.core.manager.SysDailyPhrasesManager; +import com.dstz.sys.core.mapper.SysDailyPhrasesMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.Collection; +import java.util.Date; +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.sys.api.constant.SysApiCodes.INIT_DATA_CANT_DELETE; + +/** + * 用户常用语 通用服务实现类 + * + * @author niu + * @since 2022-03-14 + */ +@Service("sysDailyPhrasesManager") +public class SysDailyPhrasesManagerImpl extends AbBaseManagerImpl implements SysDailyPhrasesManager { + + @Autowired + private SysDailyPhrasesMapper sysDailyPhrasesMapper; + + /** + * 分页查询 + * + * @param paramDTO 查询条件 + */ + @Override + public PageListDTO listJson(QueryParamDTO paramDTO) { + DefaultAbQueryFilter filter = new DefaultAbQueryFilter(paramDTO); + filter.addParam("userId", UserContextUtils.getUserId()); + return sysDailyPhrasesMapper.listJson(filter); + } + + + /** + * 查询当前用户的所有常用语 + */ + @Override + public List enableList() { + AbQueryFilter filter = new DefaultAbQueryFilter(new QueryParamDTO()); + filter.addParam("userId", UserContextUtils.getUserId()); + filter.addFilter("enable", 1, ConditionType.EQUAL); + filter.noPage(); + return sysDailyPhrasesMapper.listJson(filter); + } + + + /** + * 新增或修改 + * + * @param sysDailyPhrases 常用语对象 + */ + @Override + public void saveOrUpdate(SysDailyPhrases sysDailyPhrases) { + validate(sysDailyPhrases); + if (StrUtil.isEmpty(sysDailyPhrases.getId())) { + sysDailyPhrases.setUpdateTime(new Date()); + } + super.createOrUpdate(sysDailyPhrases); + } + + /** + * 查重操作 + * + * @param sysDailyPhrases: 修改后的对象 + **/ + private void validate(SysDailyPhrases sysDailyPhrases) { + //别名查重 + if (StrUtil.isEmpty(sysDailyPhrases.getId()) || !StrUtil.equals(getById(sysDailyPhrases.getId()).getLocution(), sysDailyPhrases.getLocution())) { + if (selectCount(Wrappers.lambdaQuery(SysDailyPhrases.class) + .eq(SysDailyPhrases::getLocution, sysDailyPhrases.getLocution()) + .eq(SysDailyPhrases::getCreateBy, UserContextUtils.getUserId())) > 0) { + throw new BusinessMessage(SysApiCodes.CODE_DUPLICATE); + } + } + } + + /** + * 删除批量删除常用语(内置数据禁止删除) + **/ + @Override + public int removeByIds(Collection list) { + List sysDailyPhrases = selectByWrapper(Wrappers.lambdaQuery().in(SysDailyPhrases::getId, list)); + for (SysDailyPhrases sysDailyPhrase : sysDailyPhrases) { + if (sysDailyPhrase.getIsDefault() == 1) { + throw new BusinessMessage(INIT_DATA_CANT_DELETE.formatDefaultMessage(sysDailyPhrase.getLocution())); + } + } + return super.removeByIds(list); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataDictManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataDictManagerImpl.java new file mode 100644 index 00000000..4e88c6b8 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataDictManagerImpl.java @@ -0,0 +1,159 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.sys.core.manager.SysDataDictManager; +import com.dstz.sys.core.mapper.SysDataDictMapper; +import com.dstz.sys.rest.model.vo.SysDataDictVO; +import org.springframework.stereotype.Service; + +import java.io.Serializable; +import java.util.*; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * 数据字典 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-11 + */ +@Service("sysDataDictManager") +public class SysDataDictManagerImpl extends AbBaseManagerImpl implements SysDataDictManager { + + private final SysDataDictMapper sysDataDictMapper; + + public SysDataDictManagerImpl(SysDataDictMapper sysDataDictMapper) { + this.sysDataDictMapper = sysDataDictMapper; + } + + @Override + public List getDictNodeList(String dictKey, Boolean hasRoot) { + return sysDataDictMapper.getDictNodeList(dictKey, hasRoot); + } + + @Override + public List getDictTree(QueryParamDTO paramDTO) { + AbQueryFilter filter = new DefaultAbQueryFilter(paramDTO); + //filter.eqFilter("dictType", SysDataDict.TYPE_DICT); + filter.noPage(); + //查询所有的字典 + List sysDataDictVOList = BeanCopierUtils.transformList(this.query(filter).getRows(), SysDataDictVO.class); + + if (CollUtil.isEmpty(sysDataDictVOList)) { + return sysDataDictVOList; + } + //设置根 + return setRoot(BeanConversionUtils.listToTree(sysDataDictVOList)); + } + + private List setRoot(List dictTree){ + SysDataDict parent = new SysDataDict(); + parent.setName("所有数据"); + + SysDataDictVO vo= BeanCopierUtils.transformBean(parent,SysDataDictVO.class); + vo.setChildren(dictTree); + return Arrays.asList(vo); + } + + @Override + public List getDictTypeTree() { + //先查询数据字典的分类 + List dictTypeList = getDictNodeList("dictType", Boolean.FALSE); + Map dictTypeMap = dictTypeList.stream().collect(Collectors.toMap(SysDataDict::getCode, Function.identity(), (k1, k2) -> k1)); + //查询每个分类下的字典(不包含字典项) + List dataDictList = sysDataDictMapper.selectList(Wrappers.lambdaQuery(SysDataDict.class).eq(SysDataDict::getDictType, SysDataDict.TYPE_DICT)); + if (CollUtil.isEmpty(dataDictList)) { + return new ArrayList<>(); + } + Map> dataDictMap = dataDictList.stream().collect(Collectors.groupingBy(SysDataDict::getTypeCode)); + List result = new ArrayList<>(dictTypeList.size()); + dataDictMap.forEach((typeCode, item) -> { + SysDataDict type = dictTypeMap.get(typeCode); + if (type != null) { + SysDataDictVO vo = BeanCopierUtils.transformBean(type, SysDataDictVO.class); + vo.setChildren(BeanCopierUtils.transformList(item, SysDataDictVO.class)); + result.add(vo); + } + }); + return result; + } + + private List getDictByTypeCode(String typeCode){ + List dictTypeList = sysDataDictMapper.selectList(Wrappers.lambdaQuery().eq(SysDataDict::getDictType,SysDataDict.TYPE_DICT) + .eq(SysDataDict::getTypeCode,typeCode)); + return BeanCopierUtils.transformList(dictTypeList, SysDataDictVO.class); + } + + + @Override + public int removeByIds(Collection ids){ + for (Serializable entityId : ids){ + SysDataDict entity = sysDataDictMapper.selectById(entityId); + if (entity == null) { + continue; + } + if (Integer.valueOf(StrPool.BOOLEAN_TRUE).equals(entity.getIsSystem())){ + throw new BusinessMessage(SysApiCodes.DICT_DELETE_ERROR.formatDefaultMessage(entity.getName())); + } + removeByEntity(entity); + } + return ids.size(); + } + + /** + * 级联删除子集 + * + * @param entity + */ + private void removeByEntity(SysDataDict entity) { + List dataDictChildren = sysDataDictMapper.getByParentId(entity.getId()); + for (SysDataDict dict : dataDictChildren) { + removeByEntity(dict); + } + sysDataDictMapper.deleteById(entity.getId()); + } + + + @Override + public int create(SysDataDict dataDict) { + saveCheck(dataDict); + return super.create(dataDict); + } + + + @Override + public int update(SysDataDict dataDict) { + saveCheck(dataDict); + return super.update(dataDict); + } + + private void saveCheck(SysDataDict dataDict) { + String dictType = dataDict.getDictType(); + String code = dataDict.getCode(); + String id = dataDict.getId(); + String dictKey = dataDict.getDictKey(); + + if (SysDataDict.TYPE_DICT.equals(dictType) && sysDataDictMapper.isExistDict(code, id) > 0) { + throw new BusinessMessage(SysApiCodes.KEY_WORD_DUPLICATE.formatDefaultMessage("字典" + code)); + } + + if (SysDataDict.TYPE_NODE.equals(dictType) && sysDataDictMapper.isExistNode(dictKey, code, id) > 0) { + throw new BusinessMessage(SysApiCodes.KEY_WORD_DUPLICATE.formatDefaultMessage("字典项" + code)); + } + // 类型只能是dict node + if (!SysDataDict.TYPE_DICT.equals(dictType) && !SysDataDict.TYPE_NODE.equals(dictType)) { + throw new BusinessMessage(SysApiCodes.DICT_KEY_TYPE_ERROR); + } + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataSourceManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataSourceManagerImpl.java new file mode 100644 index 00000000..5948bd8d --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysDataSourceManagerImpl.java @@ -0,0 +1,31 @@ +package com.dstz.sys.core.manager.impl; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.sys.core.manager.SysDataSourceManager; +import com.google.common.collect.ImmutableMap; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.Optional; + +/** + * 系统数据源通用业务处理实现 + * + * @author wacxhs + */ +@Service("sysDataSourceManager") +public class SysDataSourceManagerImpl implements SysDataSourceManager { + + @Override + public PageListDTO query(AbQueryFilter queryFilter) { + return new PageListDTO<>( + Optional.ofNullable(queryFilter.getPage()).map(Page::getSize).orElse(10L), + 1L, + 1L, + Collections.singletonList(ImmutableMap.of("alias", StrPool.DEFAULT_DATASOURCE_ALIAS, "name", "本地数据源")) + ); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysFileManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysFileManagerImpl.java new file mode 100644 index 00000000..e13467cb --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysFileManagerImpl.java @@ -0,0 +1,200 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.IdUtil; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.property.PropertyEnum; +import com.dstz.base.common.utils.IdGeneratorUtils; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.component.upload.api.IUploader; +import com.dstz.component.upload.api.UploaderFactory; +import com.dstz.org.api.model.IUser; +import com.dstz.sys.api.constant.OnlineDocMethod; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.core.entity.SysFile; +import com.dstz.sys.core.entity.SysLogErr; +import com.dstz.sys.core.manager.SysFileManager; +import com.dstz.sys.core.mapper.SysFileMapper; +import com.dstz.sys.rest.model.dto.OnlineDocParam; +import com.dstz.sys.rest.model.dto.OnlineDocParamDTO; +import com.dstz.sys.rest.model.dto.OperateOnlineDocDTO; +import com.dstz.sys.rest.model.dto.UpdateFileDTO; +import com.dstz.sys.rest.model.vo.OnlineDocApiVO; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.InputStream; +import java.time.LocalDateTime; +import java.util.Date; +import java.util.Map; + +/** + * 系统附件 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Service("sysFileManager") +public class SysFileManagerImpl extends AbBaseManagerImpl implements SysFileManager { + @Autowired + SysFileMapper sysFileMapper; + + @Value(AbAppRestConstant.SYS_SERVICE_PREFIX) + private String sysServicePrefix; + + @Override + public SysFile upload(InputStream is, String fileName, String type) { + String ext = ""; + if (fileName.contains(StrPool.DOT)) { + ext = fileName.substring(fileName.lastIndexOf('.')); + } + String id = IdGeneratorUtils.nextId(); + + // 1 先上传文件,以id为文件名保证不重复 + IUploader uploader = UploaderFactory.getDefault(); + String path = uploader.upload(is, id + ext, type); + + // 2 建立SysFile数据 + SysFile sysFile = new SysFile(); + sysFile.setId(id); + sysFile.setName(fileName); + sysFile.setUploader(uploader.type()); + sysFile.setPath(path); + + sysFile.setCreateOrgId(UserContextUtils.getGroupId()); + sysFile.setCreateBy(UserContextUtils.getUserId()); + sysFile.setTypeCode(type); + create(sysFile); + + return sysFile; + } + + @Override + public int update(UpdateFileDTO updateDTO) { + SysFile sysFile = getById(updateDTO.getFileId()); + if (sysFile == null) { + throw new BusinessMessage(SysApiCodes.FILE_NOT_FOUND_ERROR.formatDefaultMessage(updateDTO.getFileId())); + } + + String fileName = sysFile.getName(); + String ext = ""; + if (fileName.contains(StrPool.DOT)) { + ext = fileName.substring(fileName.lastIndexOf('.')); + } + + // 1 先上传文件,以id+时间戳 为文件名保证不重复 + IUploader uploader = UploaderFactory.getDefault(); + + //以老的 id +时间创建新的文件 path + String idDate = sysFile.getId() + "-" + DateUtil.format(LocalDateTime.now(), DatePattern.PURE_DATETIME_PATTERN); + String path = uploader.upload(updateDTO.getFileStream(), idDate + ext, sysFile.getTypeCode()); + + // 2 更新SysFile数据,只更新 + return super.update(null, Wrappers.lambdaUpdate(SysFile.class) + .eq(SysFile::getId, sysFile.getId()) + .set(SysFile::getPath, path) + .set(SysFile::getUpdateBy, updateDTO.getUserId()) + .set(SysFile::getUpdater, updateDTO.getUserName()) + .set(SysFile::getUpdateTime, new Date()) + .set(SysFile::getUploader, uploader.type())); + } + + @Override + public InputStream download(String fileId) { + SysFile sysFile = sysFileMapper.selectById(fileId); + IUploader uploader = UploaderFactory.getUploader(sysFile.getUploader()); + InputStream is = uploader.take(sysFile.getPath()); + if (is == null) { + throw new BusinessMessage(SysApiCodes.FILE_NOT_FOUND_ERROR.formatDefaultMessage(sysFile.getName())); + } + return is; + } + + @Override + public void delete(String fileId) { + SysFile sysFile = sysFileMapper.selectById(fileId); + + if (sysFile != null) { + //做逻辑删除 + sysFile.setDelete(1); + update(sysFile); + } + } + + private OnlineDocParamDTO buildOpenParam(OperateOnlineDocDTO operateDTO, String requestUrl) { + SysFile file = super.getById(operateDTO.getFileId()); + if (file == null) { + throw new BusinessMessage(SysApiCodes.FILE_NOT_FOUND_ERROR.formatDefaultMessage(operateDTO.getFileId())); + } + + OnlineDocParam onlineDocParam = new OnlineDocParam(); + String sysFileServicePrefix = requestUrl + sysServicePrefix + "/sysFile"; + String downLoadPath = sysFileServicePrefix + "/download?fileId="; + + //文件信息 + onlineDocParam.setFileId(operateDTO.getFileId()); + String filePath = downLoadPath + operateDTO.getFileId(); + onlineDocParam.setFilePath(filePath); + onlineDocParam.setFileName(file.getName()); + + //用户信息 + IUser user = UserContextUtils.getValidUser(); + onlineDocParam.setUserName(user.getFullName()); + onlineDocParam.setUserId(user.getUserId()); + //onlineDocParam.setUserAvatar(user.g); + + onlineDocParam.setUserRight(operateDTO.getUserRight()); + onlineDocParam.setCallbackUrl(sysFileServicePrefix + "/update"); + onlineDocParam.setExtraData(operateDTO.getExtraData()); + onlineDocParam.setSaveFlag(operateDTO.getSaveFlag()); + onlineDocParam.setUserMenuPermission(operateDTO.getUserMenuPermission()); + + return new OnlineDocParamDTO(OnlineDocMethod.OPEN, onlineDocParam); + } + + private OnlineDocApiVO sendRequest(OnlineDocMethod method, OnlineDocParamDTO paramDTO) { + String url = PropertyEnum.ONLINE_DOC_SERVICE_URL.getPropertyValue(String.class) + method.getPath(); + try (HttpResponse se = HttpUtil.createPost(url).form("jsonParams", JsonUtils.toJSONString(paramDTO)).execute()) { + OnlineDocApiVO apiVO = JsonUtils.parseObject(se.body(), OnlineDocApiVO.class); + if (apiVO.getErrorCode().equals(StrPool.BOOLEAN_FALSE)) { + return apiVO; + } else { + throw new BusinessMessage(SysApiCodes.FILE_OPEN_FILE_ERROR.formatDefaultMessage(apiVO.getErrorMessage())); + } + } + } + + + @Override + public String openFile(OperateOnlineDocDTO operateDTO, String requestUrl) { + OnlineDocApiVO apiVO = sendRequest(OnlineDocMethod.OPEN, buildOpenParam(operateDTO, requestUrl)); + Map result = (Map) apiVO.getResult(); + return result.get("urls"); + } + + @Override + public Boolean closeFile(OperateOnlineDocDTO operateDTO) { + OnlineDocParam param = new OnlineDocParam(operateDTO.getFileId()); + param.setSaveFlag(operateDTO.getSaveFlag()); + OnlineDocParamDTO paramDTO = new OnlineDocParamDTO(OnlineDocMethod.IS_OPEN, param); + + //先判断文档是否打开,已打开则关闭,否则不处理 + OnlineDocApiVO isOpenVO = sendRequest(OnlineDocMethod.IS_OPEN, paramDTO); + if ((Boolean) isOpenVO.getResult()) { + paramDTO.setMethod(OnlineDocMethod.CLOSE); + OnlineDocApiVO closeVO = sendRequest(OnlineDocMethod.CLOSE, paramDTO); + return (Boolean) closeVO.getResult(); + } + return Boolean.TRUE; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysLogErrManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysLogErrManagerImpl.java new file mode 100644 index 00000000..64a9b657 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysLogErrManagerImpl.java @@ -0,0 +1,73 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.exceptions.ExceptionUtil; +import cn.hutool.core.map.MapBuilder; +import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.events.AbRequestLogEvent; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.requestlog.AbRequestLog; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.sys.core.entity.SysLogErr; +import com.dstz.sys.core.manager.SysLogErrManager; +import com.google.common.collect.ImmutableMap; +import org.springframework.context.event.EventListener; +import org.springframework.stereotype.Service; + +import java.util.HashMap; +import java.util.Map; + +/** + * 系统异常日志 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Service("sysLogErrManager") +public class SysLogErrManagerImpl extends AbBaseManagerImpl implements SysLogErrManager { + + /** + * 异常日志只允许更改状态,其他字段只能回显,禁止修改 + * @param entity 实体 + * @return + */ + @Override + public int update(SysLogErr entity) { + return update(null, Wrappers.lambdaUpdate(SysLogErr.class) + .eq(SysLogErr::getId, entity.getId()) + .set(SysLogErr::getStatus, entity.getStatus()) + .set(SysLogErr::getIpAddress,entity.getIpAddress())); + } + + @EventListener(AbRequestLogEvent.class) + public void abRequestLogEventListener(AbRequestLogEvent abRequestLogEvent){ + if (!AbRequestLogEvent.EventType.POST_PROCESS.equals(abRequestLogEvent.getEventType())) { + return; + } + AbRequestLog requestLog = abRequestLogEvent.getRequestLog(); + Throwable exception = requestLog.getException(); + // 跳过提示信息异常 + if(exception == null || exception instanceof BusinessMessage){ + return; + } + ThreadUtil.execute(() -> { + SysLogErr sysLogErr = new SysLogErr(); + sysLogErr.setAccount(requestLog.getUsername()); + sysLogErr.setIp(requestLog.getClientIp()); + sysLogErr.setIpAddress(StrUtil.EMPTY); + sysLogErr.setStatus("unchecked"); + sysLogErr.setUrl(requestLog.getUrl()); + sysLogErr.setContent(ExceptionUtil.getRootCauseMessage(exception)); + sysLogErr.setHeads(JsonUtils.toJSONString(requestLog.getRequestHeaderMap())); + + Map paramMap = new HashMap<>(requestLog.getRequestParameterMap()); + paramMap.put("@RequestBody", requestLog.getRequestBody()); + + sysLogErr.setRequestParam(JsonUtils.toJSONString(paramMap)); + sysLogErr.setStackTrace(ExceptionUtil.stacktraceToString(requestLog.getException(), -1)); + getBaseMapper().insert(sysLogErr); + }); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysPropertiesManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysPropertiesManagerImpl.java new file mode 100644 index 00000000..a988d47d --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysPropertiesManagerImpl.java @@ -0,0 +1,100 @@ +package com.dstz.sys.core.manager.impl; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.constats.NumberPool; +import com.dstz.base.common.encrypt.EncryptUtil; +import com.dstz.base.common.enums.EnvironmentConstants; +import com.dstz.base.common.property.SysPropertyService; +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.sys.core.entity.SysProperties; +import com.dstz.sys.core.manager.SysPropertiesManager; +import com.dstz.sys.core.mapper.SysPropertiesMapper; + +import java.util.List; + +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/** + * 系统属性 通用服务实现类 + * + * @author jinxia.hou + * @since 2022-02-11 + */ +@Service("sysPropertiesManager") +public class SysPropertiesManagerImpl extends AbBaseManagerImpl implements SysPropertiesManager, SysPropertyService { + + private final SysPropertiesMapper sysPropertiesMapper; + + private final ICache cache; + + public SysPropertiesManagerImpl(SysPropertiesMapper sysPropertiesMapper, ICache cache) { + this.sysPropertiesMapper = sysPropertiesMapper; + this.cache = cache; + } + + @Override + public boolean isExist(SysProperties sysProperties) { + return sysPropertiesMapper.isExist(sysProperties) > 0; + } + + @Override + public String getValByCode(String code) { + final String currentEnv = StrUtil.emptyToDefault(SpringUtil.getActiveProfile(), EnvironmentConstants.DEV.getKey()); + final String cacheKey = StrUtil.join(StrUtil.COLON, currentEnv, code); + String value = cache.get(AbCacheRegionConstant.PROPERTIES_CACHE_REGION, cacheKey, () -> loadPropertyValue(currentEnv, code)); + return StrUtil.emptyToNull(value); + } + + private String loadPropertyValue(String environment, String code) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(SysProperties.class) + .select(SysProperties::getEncrypt, SysProperties::getValue) + .in(SysProperties::getEnvironment, StringUtils.upperCase(environment),EnvironmentConstants.DEV.getKey()) + .eq(SysProperties::getCode, code); + + List sysPropertiesList = sysPropertiesMapper.selectList(queryWrapper); + + // 多环境下,取出 指定环境以及dev环境的参数,如果只有一个配置项,返回,如果多个,则过滤出对应环境的返回 + SysProperties sysProperties = sysPropertiesList.size() == 1 ? sysPropertiesList.get(0): null; + if(sysProperties == null && sysPropertiesList.size()>1) { + sysProperties = sysPropertiesList.stream().filter(o-> o.getEnvironment().equals(StringUtils.upperCase(environment))).findFirst().orElse(null); + } + + if (sysProperties == null) { + return StrUtil.EMPTY; + } + return NumberPool.BOOLEAN_TRUE.equals(sysProperties.getEncrypt()) ? EncryptUtil.decrypt(sysProperties.getValue()) : sysProperties.getValue(); + } + + + /** + * 将所有系统属性通过不同环境分组加入进缓存中 + */ + @Override + public void reloadProperty() { + cache.invalidateRegion(AbCacheRegionConstant.PROPERTIES_CACHE_REGION); + } + + @Override + public Integer getIntByCode(String code) { + return Convert.toInt(getByCode(code), 0); + } + + + @Override + public Long getLongByCode(String code) { + return Convert.toLong(getByCode(code), 0L); + } + + @Override + public Boolean getBooleanByCode(String code) { + return Convert.toBool(getByCode(code), false); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysScriptManagerImpl.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysScriptManagerImpl.java new file mode 100644 index 00000000..cbada441 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/manager/impl/SysScriptManagerImpl.java @@ -0,0 +1,17 @@ +package com.dstz.sys.core.manager.impl; + +import com.dstz.base.manager.impl.AbBaseManagerImpl; +import com.dstz.sys.core.entity.SysScript; +import com.dstz.sys.core.manager.SysScriptManager; +import org.springframework.stereotype.Service; + +/** + * 常用脚本 通用服务实现类 + * + * @author wacxhs + * @since 2022-01-25 + */ +@Service("sysScriptManager") +public class SysScriptManagerImpl extends AbBaseManagerImpl implements SysScriptManager { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysAuthorizationMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysAuthorizationMapper.java new file mode 100644 index 00000000..da0ca0bf --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysAuthorizationMapper.java @@ -0,0 +1,23 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.sys.core.entity.SysAuthorization; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 通用资源授权配置 Mapper 接口 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Mapper +public interface SysAuthorizationMapper extends AbBaseMapper { + List getByTarget(@Param("rightsObject") String rightsObject, @Param("rightsTarget") String rightsTarget); + + void deleteByTarget(@Param("rightsObject") String rightsObject, @Param("rightsTarget") String rightsTarget); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConfigurationMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConfigurationMapper.java new file mode 100644 index 00000000..188a7605 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConfigurationMapper.java @@ -0,0 +1,18 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.sys.core.entity.SysConfiguration; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author lightning + * @since 2023-05-11 + */ +@Mapper +public interface SysConfigurationMapper extends AbBaseMapper { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConnectRecordMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConnectRecordMapper.java new file mode 100644 index 00000000..170077dc --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysConnectRecordMapper.java @@ -0,0 +1,18 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.sys.core.entity.SysConnectRecord; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 公共业务关联记录 Mapper 接口 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Mapper +public interface SysConnectRecordMapper extends AbBaseMapper { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.java new file mode 100644 index 00000000..625fe8f7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.java @@ -0,0 +1,24 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.sys.core.entity.SysDailyPhrases; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 用户常用语 Mapper 接口 + *

+ * + * @author niu + * @since 2022-03-14 + */ +@Mapper +public interface SysDailyPhrasesMapper extends AbBaseMapper { + + /** + * 常用语的分页接口 (系统内置的常用语或自己的常用语) + */ + PageListDTO listJson(AbQueryFilter filter); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDataDictMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDataDictMapper.java new file mode 100644 index 00000000..adb5be50 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysDataDictMapper.java @@ -0,0 +1,52 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +/** + *

+ * 数据字典 Mapper 接口 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +@Mapper +public interface SysDataDictMapper extends AbBaseMapper { + + /** + * 通过dicKey获取字典项。若hasRoot则包含字典本身 + * @param dictKey + * @param hasRoot + * @return + */ + List getDictNodeList(@Param("dictKey")String dictKey, @Param("hasRoot") Boolean hasRoot); + + /** + * 判断字典是否存在, + * @param code + * @param id 若不为null,则排除id进行判断是否存在。用于更新时 + * @return + */ + Integer isExistDict(@Param("code")String code,@Param("id") String id); + + /** + * 判断字典项是否存在 + * @param dictKey + * @param id id 若不为null,则排除id进行判断是否存在。用于更新时 + * @return + */ + Integer isExistNode(@Param("dictKey")String dictKey,@Param("code")String code,@Param("id") String id); + + /** + * 通过ParentId 获取字典 + * @param id + * @return + */ + List getByParentId(String id); + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysFileMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysFileMapper.java new file mode 100644 index 00000000..a8f24df1 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysFileMapper.java @@ -0,0 +1,18 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.sys.core.entity.SysFile; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 系统附件 Mapper 接口 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Mapper +public interface SysFileMapper extends AbBaseMapper { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysLogErrMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysLogErrMapper.java new file mode 100644 index 00000000..f09bd368 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysLogErrMapper.java @@ -0,0 +1,27 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.sys.core.entity.SysLogErr; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 系统异常日志 Mapper 接口 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@Mapper +public interface SysLogErrMapper extends AbBaseMapper { + + /** + * queryFilter 分页列表查询 + * @param abQueryFilter 查询参数 + * @return + */ + PageListDTO query(AbQueryFilter abQueryFilter); + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysPropertiesMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysPropertiesMapper.java new file mode 100644 index 00000000..4b9fde9c --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysPropertiesMapper.java @@ -0,0 +1,37 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.sys.core.entity.SysProperties; +import com.dstz.base.mapper.AbBaseMapper; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Select; + +import java.util.List; + +/** + *

+ * 系统属性 Mapper 接口 + *

+ * + * @author Administrator + * @since 2022-02-11 + */ +@Mapper +public interface SysPropertiesMapper extends AbBaseMapper { + + /** + * 判断属性是否存在。 + * + * @param sysProperties + * @return + */ + Integer isExist(SysProperties sysProperties); + + /** + * 分组列表。 + * + * @return + */ + @Select("SELECT distinct group_ FROM SYS_PROPERTIES") + List getGroups(); + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysScriptMapper.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysScriptMapper.java new file mode 100644 index 00000000..e9b4cf16 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/mapper/SysScriptMapper.java @@ -0,0 +1,18 @@ +package com.dstz.sys.core.mapper; + +import com.dstz.base.mapper.AbBaseMapper; +import com.dstz.sys.core.entity.SysScript; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * 常用脚本 Mapper 接口 + *

+ * + * @author wacxhs + * @since 2022-01-25 + */ +@Mapper +public interface SysScriptMapper extends AbBaseMapper { + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/valuemap/AbDsValueMapLoader.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/valuemap/AbDsValueMapLoader.java new file mode 100644 index 00000000..a35cf191 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/core/valuemap/AbDsValueMapLoader.java @@ -0,0 +1,46 @@ +package com.dstz.sys.core.valuemap; + +import cn.hutool.core.bean.BeanUtil; +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.convert.Convert; +import cn.hutool.core.text.CharSequenceUtil; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.sys.core.manager.SysDataSourceManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.List; +import java.util.Map; + +/** + *
+ * 系统数据源属性映射器
+ * 
+ * + * @author aschs + * @date 2022年4月6日 + * @owner 深圳市大世同舟信息科技有限公司 + */ +@Component +public class AbDsValueMapLoader implements AbValueMapLoader { + private static final String DEFAULT_ATTR_NAME = "alias"; + + @Autowired + private SysDataSourceManager sysDataSourceManager; + + public AbDsValueMapLoader() { + AbValueMapLoaderProvider.register(AbValueMapType.SYS_DATA_SOURCE, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + List rows = sysDataSourceManager.query(new DefaultAbQueryFilter()).getRows(); + final String attrName = CharSequenceUtil.emptyToDefault(abValueMap.matchField(), DEFAULT_ATTR_NAME); + return CollUtil.toMap(rows, null, o -> Convert.toStr(BeanUtil.getFieldValue(o, attrName))); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/groovy/SysScript.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/groovy/SysScript.java new file mode 100644 index 00000000..21501d25 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/groovy/SysScript.java @@ -0,0 +1,109 @@ +package com.dstz.sys.groovy; + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.identityconvert.SysIdentity; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.groovy.script.api.IScript; +import com.dstz.org.api.model.IGroup; +import com.dstz.org.api.model.IUser; +import com.dstz.sys.core.entity.SysProperties; +import com.dstz.sys.core.manager.SysPropertiesManager; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Component; + +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +/** + * @author jinxia.hou + * @Name SysScript + * @description: 系统默认的一些脚本 + * @date 2022/7/2814:13 + */ +@Component +public class SysScript implements IScript { + + private static final Logger logger = LoggerFactory.getLogger(SysScript.class); + + @Autowired + private JdbcTemplate jdbcTemplate; + @Autowired + private SysPropertiesManager sysPropertiesManager; + + + /** + * 获取系统属性 + * @param key code + * @return value + */ + public String getProperty(String key) { + SysProperties properties = sysPropertiesManager.selectOne(Wrappers.lambdaQuery(SysProperties.class).eq(SysProperties::getCode,key)); + if (properties!=null){ + return properties.getValue(); + } + return null; + } + + + public IUser getCurrentUser() { + IUser user = UserContextUtils.getValidUser(); + return user; + } + + public String getCurrentGroupName() { + IGroup iGroup =UserContextUtils.getGroup().get(); + if (iGroup!= null) { + return iGroup.getGroupName(); + } else { + return ""; + } + } + + public String getCurrentUserName() { + return UserContextUtils.getValidUser().getFullName(); + } + + public Integer executeUpdateSql(String sql, Object ... params) { + Integer result = jdbcTemplate.update(sql, params); + return result ; + } + + public Integer executeIntegerSql(String sql, Object ... params) { + Integer result = jdbcTemplate.queryForObject(sql, Integer.class, params); + return result ; + } + + /** + * 通过一个sql 获取候选人 + * @param sql + * @param params + * @return + */ + public Set getIdentityBySql(String sql, Object ... params) { + List list = jdbcTemplate.queryForList(sql, SysIdentity.class, params); + return transitionIdentitys(list); + } + + private Set transitionIdentitys(List list) { + Set set = new HashSet<>(); + + for(SysIdentity identity : list) { + // 如果SQL 返回的候选人不完整则不予添加 + if(StrUtil.isEmpty(identity.getType()) || StrUtil.isEmpty(identity.getName()) + || StrUtil.isEmpty(identity.getId())) { + + logger.debug("通过 sql 获取用户候选人失败,sql 返回的 identity 信息不完整,请检查{}", JsonUtils.toJSONString(identity)); + continue; + } + set.add(identity); + } + + return set; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysAuthorizationController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysAuthorizationController.java new file mode 100644 index 00000000..a18e30d5 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysAuthorizationController.java @@ -0,0 +1,63 @@ +package com.dstz.sys.rest.controller; + + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.rest.model.dto.AuthorizationDTO; +import com.dstz.sys.core.entity.SysAuthorization; +import com.dstz.sys.core.manager.SysAuthorizationManager; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; + +import static com.dstz.base.api.vo.ApiResponse.success; + +/** + *

+ * 通用资源授权配置 前端控制器 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/authorization") +public class SysAuthorizationController extends AbCrudController { + + public final SysAuthorizationManager sysAuthorizationManager; + + public SysAuthorizationController(SysAuthorizationManager sysAuthorizationManager) { + this.sysAuthorizationManager = sysAuthorizationManager; + } + + /** + * 保存授权结果 + * + * @param saveDTO 参数对象 + */ + @RequestMapping("saveAuthorization") + public ApiResponse saveAuthorization(@RequestBody AuthorizationDTO saveDTO) { + return success(() -> sysAuthorizationManager.createAll(saveDTO)); + } + + /** + * 获取授权结果用来初始化 + * + * @param dto 要获取授权对象的参数dto + */ + @PostMapping("getAuthorizations") + public ApiResponse> getAuthorizations(@RequestBody AuthorizationDTO dto) { + return success(sysAuthorizationManager.getByTarget(dto.getRightsObject(), dto.getRightsTarget())); + } + + @Override + protected String getEntityDesc() { + return "通用资源授权配置"; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysConfigurationController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysConfigurationController.java new file mode 100644 index 00000000..50b9172e --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysConfigurationController.java @@ -0,0 +1,42 @@ +package com.dstz.sys.rest.controller; + + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.sys.core.manager.SysConfigurationManager; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.core.entity.SysConfiguration; + +/** + *

+ * 前端控制器 + *

+ * + * @author lightning + * @since 2023-05-11 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/sysConfiguration") +public class SysConfigurationController extends AbCrudController { + + @Override + protected String getEntityDesc() { + return ""; + } + + @Autowired + SysConfigurationManager sysConfigurationManager; + /** + * 根据编码获取系统配置 + * @param code 配置编码 + * @return 配置信息 + */ + @GetMapping({"/getConfByCode/{code}"}) + public ApiResponse getConfByCode(@PathVariable String code) { + return ApiResponse.success(sysConfigurationManager.getConfByCode(code)); + } + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDailyPhrasesController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDailyPhrasesController.java new file mode 100644 index 00000000..95158b61 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDailyPhrasesController.java @@ -0,0 +1,96 @@ +package com.dstz.sys.rest.controller; + + +import static com.dstz.base.api.vo.ApiResponse.success; +import static com.dstz.base.common.constats.AbAppRestConstant.SYS_SERVICE_PREFIX; + +import java.util.List; + +import javax.validation.Valid; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.api.vo.PageListVO; +import com.dstz.base.common.utils.UserContextUtils; +import com.dstz.base.query.ConditionType; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.core.entity.SysDailyPhrases; +import com.dstz.sys.core.manager.SysDailyPhrasesManager; + +/** + *

+ * 用户常用语 前端控制器 + *

+ * + * @author niu + * @since 2022-03-14 + */ +@RestController +@RequestMapping(SYS_SERVICE_PREFIX + "/sysDailyPhrases") +public class SysDailyPhrasesController extends AbCrudController { + + @Autowired + private SysDailyPhrasesManager sysDailyPhrasesManager; + + @Override + protected String getEntityDesc() { + return "用户常用语"; + } + + /** + * 分页查询 + * + * @param queryParamDto 查询对象 + * @return PageListDTO 展示的公告列表 + */ + @RequestMapping("listJson") + @Override + public ApiResponse> listJson(@Valid @RequestBody QueryParamDTO queryParamDto) { + return success(sysDailyPhrasesManager.listJson(queryParamDto)); + } + + /** + * 保存或修改实体数据 + * + * @param sysDailyPhrases 常用语对象 + * @return 接口响应对象 + */ + @PostMapping("saveOrUpdate") + public ApiResponse saveOrUpdate(@RequestBody SysDailyPhrases sysDailyPhrases) { + return success(() -> sysDailyPhrasesManager.saveOrUpdate(sysDailyPhrases)); + } + + + + /** + * 启动或禁用 + * + * @param sysDailyPhrases 常用语对象 + */ + @PostMapping("updateEnable") + public ApiResponse updateEnable(@RequestBody SysDailyPhrases sysDailyPhrases) { + return success(() -> sysDailyPhrasesManager.update(null,Wrappers.lambdaUpdate(SysDailyPhrases.class) + .eq(SysDailyPhrases::getId,sysDailyPhrases.getId()) + .set(SysDailyPhrases::getEnable,sysDailyPhrases.getEnable()))); + } + + /** + * 获取当前用户的所有常用语 (筛选已启用) + * + * @return List 常用语集合 + */ + @GetMapping("list") + public ApiResponse> list() { + return success(sysDailyPhrasesManager.enableList()); + } + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataDictController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataDictController.java new file mode 100644 index 00000000..84fa1d1f --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataDictController.java @@ -0,0 +1,200 @@ +package com.dstz.sys.rest.controller; + + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.collection.ListUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.cache.ICache; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.AbCacheRegionConstant; +import com.dstz.base.common.utils.BeanConversionUtils; +import com.dstz.base.common.utils.BeanCopierUtils; +import com.dstz.base.query.AbQueryFilter; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.api.constant.SysCackeKeyConstant; +import com.dstz.sys.api.dto.DataDictDTO; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.sys.core.manager.SysDataDictManager; +import com.dstz.sys.rest.model.dto.GetDictDTO; +import com.dstz.sys.rest.model.vo.SysDataDictVO; +import org.springframework.http.MediaType; +import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; +import java.util.*; +import java.util.stream.Collectors; + +/** + *

+ * 数据字典 前端控制器 + *

+ * + * @author jinxia.hou + * @since 2022-02-11 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/dataDict/") +public class +SysDataDictController extends AbCrudController { + + private final SysDataDictManager sysDataDictManager; + + private final ICache iCache; + + public SysDataDictController(SysDataDictManager sysDataDictManager, ICache iCache) { + this.sysDataDictManager = sysDataDictManager; + this.iCache = iCache; + } + + /** + * 通过dicKey获取字典项,数据结构是list + */ + @RequestMapping("getDictNodeList") + public ApiResponse> getByDictKey(@RequestParam("dictKey") String dictKey) { + return ApiResponse.success(sysDataDictManager.getDictNodeList(dictKey, false)); + } + + + /** + * 通过dicKey获取字典项。若hasRoot则包含字典本身 + * + * @return + * @throws Exception + */ + @RequestMapping("getDictData") + public ApiResponse> getByDictKey(@RequestBody @Valid GetDictDTO getDictDTO) { + List nodeVoList = BeanCopierUtils.transformList(sysDataDictManager.getDictNodeList(getDictDTO.getDictKey(), getDictDTO.getHasRoot()), SysDataDictVO.class); + + //如果包含字典本身,则把字典名称做为字典项树的根节点 + if (CollUtil.isEmpty(nodeVoList) || getDictDTO.getHasRoot()) { + return ApiResponse.success(BeanConversionUtils.listToTree(nodeVoList)); + } + SysDataDictVO parent = null; + //自定义根节点 + if (StrUtil.isNotEmpty(getDictDTO.getRootName())) { + parent = new SysDataDictVO(); + parent.setParentId("0"); + parent.setName(getDictDTO.getRootName()); + } else { + //拿到字典本身作为Root + parent = CollUtil.findOne(nodeVoList, e -> SysDataDict.TYPE_DICT.equals(e.getDictType())); + } + + if (parent == null) { + return ApiResponse.success(BeanConversionUtils.listToTree(nodeVoList)); + } + + parent.setChildren(BeanConversionUtils.listToTree(nodeVoList)); + return ApiResponse.success(Arrays.asList(parent)); + } + + /** + * 获取所有的字典(不包含字典项) + * + * @param queryParamDTO + * @return + */ + @RequestMapping("getDictList") + public ApiResponse getDictList(@RequestBody QueryParamDTO queryParamDTO) { + AbQueryFilter filter = new DefaultAbQueryFilter(queryParamDTO); + filter.eqFilter("dictType", SysDataDict.TYPE_DICT); + return ApiResponse.success(sysDataDictManager.query(filter)); + } + + + @RequestMapping("getDictTypeTree") + public ApiResponse> getDictTree() { + return ApiResponse.success(sysDataDictManager.getDictTypeTree()); + } + + @RequestMapping("getDictTree") + public ApiResponse> getDictTree(@RequestBody QueryParamDTO queryParamDTO) throws Exception { + return ApiResponse.success(sysDataDictManager.getDictTree(queryParamDTO)); + } + + /** + * 保存实体数据 + * + * @param entity 实体 + * @return 接口响应-实体ID + */ + @RequestMapping(value = "save", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse save(@Valid @RequestBody SysDataDict entity) { + String desc; + if (StringUtils.hasLength(entity.getId())) { + desc = "更新%s成功"; + sysDataDictManager.update(entity); + } else { + desc = "添加%s成功"; + sysDataDictManager.create(entity); + } + //把字典项放到缓存中 + putDictNodeInCache(entity.getDictKey()); + return ApiResponse.success(entity.getId()).withMessage(String.format(desc, getEntityDesc())); + } + + private void putDictNodeInCache(String dictKey) { + List dictNodeList = sysDataDictManager.getDictNodeList(dictKey, false); + if (CollUtil.isNotEmpty(dictNodeList)) { + List data = BeanCopierUtils.transformList(dictNodeList, DataDictDTO.class); + iCache.put(AbCacheRegionConstant.DICT_CACHE_REGION, SysCackeKeyConstant.GET_DICT_NODE_LIST + dictKey, data); + } + } + + + /** + * 批量保存实体数据 + * + * @param entityList 实体集合 + * @return 接口响应-实体ID + */ + @RequestMapping(value = "saveBatch", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse saveBatch(@Valid @RequestBody List entityList) { + entityList.forEach(entity->{ + if (StringUtils.hasLength(entity.getId())) { + sysDataDictManager.update(entity); + } else { + sysDataDictManager.create(entity); + } + }); + //把字典项放到缓存中 + if (CollUtil.isNotEmpty(entityList)){ + Set dictKeys= entityList.stream().map(SysDataDict::getDictKey).collect(Collectors.toSet()); + dictKeys.forEach(dictKey->{ + putDictNodeInCache(dictKey); + }); + } + return ApiResponse.success().withMessage("批量操作成功"); + } + + + /** + * 实体批量删除 + * + * @param id 实体ID,多个,分隔 + * @return 接口响应 + */ + @RequestMapping(value = "remove", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse remove(@RequestParam(name = "id") String id) { + List sysDataDicts = sysDataDictManager.selectByIds(StrUtil.split(id, CharUtil.COMMA)); + sysDataDicts.forEach(e -> { + iCache.invalidate(AbCacheRegionConstant.DICT_CACHE_REGION, SysCackeKeyConstant.GET_DICT_NODE_LIST + e.getDictKey()); + }); + sysDataDictManager.removeByIds(StrUtil.split(id, CharUtil.COMMA)); + final String message = String.format("删除%s成功", getEntityDesc()); + return ApiResponse.success().withMessage(message); + } + + @Override + protected String getEntityDesc() { + return "数据字典"; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataSourceController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataSourceController.java new file mode 100644 index 00000000..44aed165 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysDataSourceController.java @@ -0,0 +1,43 @@ +package com.dstz.sys.rest.controller; + +import com.dstz.base.api.dto.PageListDTO; +import com.dstz.base.api.dto.QueryParamDTO; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.query.impl.DefaultAbQueryFilter; +import com.dstz.sys.core.manager.SysDataSourceManager; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.validation.Valid; + +/** + * 系统数据源控制器 + * + * @author wacxhs + */ +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/dataSource") +@RestController +public class SysDataSourceController { + + private final SysDataSourceManager sysDataSourceManager; + + public SysDataSourceController(SysDataSourceManager sysDataSourceManager) { + this.sysDataSourceManager = sysDataSourceManager; + } + + /** + * 数据分页列表 + * + * @param queryParamDTO 查询参数 + * @return 接口处理结果-数据源分页 + */ + @PostMapping(value = "/listJson", produces = MediaType.APPLICATION_JSON_VALUE, consumes = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse listJson(@Valid @RequestBody QueryParamDTO queryParamDTO) { + PageListDTO pageListDTO = sysDataSourceManager.query(new DefaultAbQueryFilter(queryParamDTO)); + return ApiResponse.success(pageListDTO); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysFileController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysFileController.java new file mode 100644 index 00000000..05fb52e5 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysFileController.java @@ -0,0 +1,252 @@ +package com.dstz.sys.rest.controller; + + +import cn.hutool.core.date.DatePattern; +import cn.hutool.core.date.DateTime; +import cn.hutool.core.io.FileUtil; +import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.CharUtil; +import cn.hutool.core.util.RandomUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.core.util.ZipUtil; +import cn.hutool.extra.servlet.ServletUtil; +import cn.hutool.http.HttpResponse; +import cn.hutool.http.HttpUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.constats.StrPool; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.utils.AbRequestUtils; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.core.entity.SysFile; +import com.dstz.sys.core.manager.SysFileManager; +import com.dstz.sys.rest.model.dto.OnlineDocParam; +import com.dstz.sys.rest.model.dto.OnlineDocParamDTO; +import com.dstz.sys.rest.model.dto.OperateOnlineDocDTO; +import com.dstz.sys.rest.model.dto.UpdateFileDTO; +import com.dstz.sys.rest.model.vo.CreateAndOpenVO; +import com.dstz.sys.rest.model.vo.OnlineDocApiVO; +import org.apache.commons.io.FileUtils; +import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.time.DateUtils; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import javax.servlet.http.Part; +import javax.validation.Valid; +import java.io.*; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.util.Date; +import java.util.List; +import java.util.Objects; +import java.util.concurrent.TimeUnit; + +/** + *

+ * 系统附件 前端控制器 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/sysFile") +public class SysFileController extends AbCrudController { + + private static final Integer FILE_NAME_MAX_LENGTH = 60; + //文件上传的默认字典分类 + private static final String DEFAULT_DIC_CODE = "mrfl"; + + private final SysFileManager sysFileManager; + + public SysFileController(SysFileManager sysFileManager) { + + this.sysFileManager = sysFileManager; + } + + /** + *
+     * 
+ * + * @param file 文件本身 + * @param dicCode 文件字典分类编码 + */ + @RequestMapping(value = "upload", method = RequestMethod.POST) + public ApiResponse upload(@RequestParam("file") MultipartFile file, String dicCode) throws IOException { + if (file.getOriginalFilename().length() > FILE_NAME_MAX_LENGTH) { + throw new BusinessMessage(SysApiCodes.FILE_NAME_LENGTH_ERROR); + } + if (StrUtil.isEmpty(dicCode)) { + dicCode = DEFAULT_DIC_CODE; + } + SysFile sysFile = sysFileManager.upload(file.getInputStream(), file.getOriginalFilename(), dicCode); + return ApiResponse.success(sysFile.getId()).withMessage("上传成功"); + } + + /** + * 浏览文件,支持图片 + * + * @param fileId 文件ID + * @param response 响应 + */ + @GetMapping(value = "/view/{fileId}") + public void viewFile(@PathVariable("fileId") String fileId, HttpServletResponse response) { + SysFile sysFile = sysFileManager.getById(fileId); + String mineType; + if (Objects.isNull(sysFile) || StrUtil.isEmpty(mineType = FileUtil.getMimeType(sysFile.getName()))) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } + InputStream inputStream = sysFileManager.download(fileId); + if (inputStream == null) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + } + final Date nowDateTime = new Date(); + final Date expireDateTime = DateUtils.addYears(nowDateTime, 1); + response.addHeader(HttpHeaders.EXPIRES, DatePattern.JDK_DATETIME_FORMAT.format(expireDateTime)); + response.addHeader(HttpHeaders.CACHE_CONTROL, String.format("max-age=%d", TimeUnit.MICROSECONDS.toSeconds(expireDateTime.getTime() - nowDateTime.getTime()))); + ServletUtil.write(response, inputStream, mineType); + } + + /** + *
+     * 
+ * + * @param fileId 文件名 + * @return + * @throws Exception + */ + @RequestMapping(value = "download") + public void download(@RequestParam("fileId") String fileId, HttpServletResponse response) throws Exception { + SysFile sysFile = sysFileManager.getById(fileId); + if (Objects.isNull(sysFile)) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + return; + } + InputStream inputStream = sysFileManager.download(fileId); + if (inputStream == null) { + response.setStatus(HttpStatus.NOT_FOUND.value()); + } +// String mimeType = StrUtil.nullToDefault(FileUtil.getMimeType(sysFile.getName()), MediaType.APPLICATION_OCTET_STREAM_VALUE); + ServletUtil.write(response, inputStream, MediaType.APPLICATION_OCTET_STREAM_VALUE, sysFile.getName()); + } + + @RequestMapping(value = "zip", method = RequestMethod.GET) + public ResponseEntity zip(@RequestParam("fileIds") String fileIds) throws Exception { + File zipDirectory = new File(StrUtil.join(FileUtils.getTempDirectoryPath(), File.separator, RandomUtil.randomString(6))); + File zipFile = null; + try { + if (!zipDirectory.mkdir()) { + throw new BusinessMessage(SysApiCodes.FILE_CREATE_DIR_ERROR.formatDefaultMessage(zipDirectory.getPath())); + } + downloadToDirectory(StrUtil.split(fileIds, CharUtil.COMMA), zipDirectory); + zipFile = ZipUtil.zip(zipDirectory); + final String zipName = DatePattern.PURE_DATETIME_FORMAT.format(new DateTime()) + ".zip"; + HttpHeaders headers = new HttpHeaders(); + headers.setContentDispositionFormData("attachment", URLEncoder.encode(zipName, StandardCharsets.UTF_8.displayName())); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + return new ResponseEntity<>(Files.readAllBytes(zipFile.toPath()), headers, HttpStatus.OK); + } finally { + if (zipFile != null) { + FileUtils.deleteQuietly(zipFile); + } + FileUtils.deleteDirectory(zipDirectory); + } + } + + /** + * 下载到指定目录 + * + * @param fileIds 文件编号集 + * @param directory 下载目录 + * @throws IOException 文件不存在或读取写入问题 + */ + private void downloadToDirectory(List fileIds, File directory) throws IOException { + for (String id : fileIds) { + SysFile sysFile = sysFileManager.getById(id); + if (sysFile == null) { + throw new BusinessMessage(SysApiCodes.FILE_NOT_FOUND_ERROR.formatDefaultMessage(id)); + } + try (InputStream downloadStream = sysFileManager.download(id)) { + try (OutputStream fos = new FileOutputStream(new File(directory, sysFile.getName()))) { + IOUtils.copyLarge(downloadStream, fos); + } + } + } + } + + @RequestMapping(value = "del") + public ApiResponse del(@RequestParam("fileId") String fileId) throws Exception { + sysFileManager.delete(fileId); + return ApiResponse.success("删除成功"); + } + + @RequestMapping(value = "update") + public ApiResponse update(HttpServletRequest request, HttpServletResponse response) throws Exception { + String fileId = request.getParameter("fileId"); + String userId = request.getParameter("userId"); + Part file = request.getPart("file"); + + UpdateFileDTO updateFileDTO = new UpdateFileDTO(); + updateFileDTO.setFileId(fileId); + updateFileDTO.setFileStream(file.getInputStream()); + //todo 使用当前用户名 + updateFileDTO.setUserName("test"); + updateFileDTO.setUserId(userId); + int result = sysFileManager.update(updateFileDTO); + + return ApiResponse.success(result).withMessage("更新成功"+result+"条"); + } + + @Override + protected String getEntityDesc() { + return "系统附件"; + } + + @RequestMapping(value = "open") + public ApiResponse openFile(@RequestBody @Valid OperateOnlineDocDTO operateDTO){ + HttpServletRequest request = AbRequestUtils.getHttpServletRequest(); + String requestUrl =StrUtil.removeSuffix(request.getRequestURL(),request.getServletPath()); + + return ApiResponse.success(sysFileManager.openFile(operateDTO,requestUrl)); + } + + @RequestMapping(value = "close") + public ApiResponse closeFile(@RequestBody @Valid OperateOnlineDocDTO operateDTO){ + return ApiResponse.success(sysFileManager.closeFile(operateDTO)); + } + + + /** + * 创建并打开新文件 + * @param operateDTO + * @return + * @throws IOException + */ + @RequestMapping(value = "createAndOpen", method = RequestMethod.POST) + public ApiResponse createAndOpen(@RequestBody OperateOnlineDocDTO operateDTO) { + if (StrUtil.isEmpty(operateDTO.getFileName())){ + operateDTO.setFileName("新建文件.docx"); + } + //新建文件 + SysFile sysFile = sysFileManager.upload( IoUtil.toUtf8Stream(""), operateDTO.getFileName(), DEFAULT_DIC_CODE); + //打开文件 + if (sysFile !=null && StrUtil.isNotEmpty(sysFile.getId())){ + operateDTO.setFileId(sysFile.getId()); + HttpServletRequest request = AbRequestUtils.getHttpServletRequest(); + String requestUrl =StrUtil.removeSuffix(request.getRequestURL(),request.getServletPath()); + return ApiResponse.success(new CreateAndOpenVO(sysFileManager.openFile(operateDTO,requestUrl),sysFile.getId())); + } + return ApiResponse.fail(SysApiCodes.FILE_CREATE_FILE_ERROR.getCode(),SysApiCodes.FILE_CREATE_FILE_ERROR.getMessage()); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysLogErrController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysLogErrController.java new file mode 100644 index 00000000..6ddd5363 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysLogErrController.java @@ -0,0 +1,27 @@ +package com.dstz.sys.rest.controller; + + +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.core.entity.SysLogErr; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + *

+ * 系统异常日志 前端控制器 + *

+ * + * @author jinxia.hou + * @since 2022-02-17 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/logErr") + +public class SysLogErrController extends AbCrudController { + + @Override + protected String getEntityDesc() { + return "系统异常日志"; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysPropertiesController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysPropertiesController.java new file mode 100644 index 00000000..67752d5b --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysPropertiesController.java @@ -0,0 +1,81 @@ +package com.dstz.sys.rest.controller; + +import cn.hutool.core.lang.Assert; +import cn.hutool.core.util.StrUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.encrypt.EncryptUtil; +import com.dstz.base.common.exceptions.BusinessMessage; +import com.dstz.base.common.property.SysPropertyService; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.sys.api.constant.SysApiCodes; +import com.dstz.sys.core.entity.SysProperties; +import com.dstz.sys.core.manager.SysPropertiesManager; +import org.springframework.web.bind.annotation.*; + +/** + * @author jinxia.hou + * @Name SysPropertiesController + * @description: 系统属性 + * @date 2022/2/1610:41 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/properties") +public class SysPropertiesController extends AbCrudController { + + private final SysPropertiesManager sysPropertiesManager; + + private final SysPropertyService sysPropertyService; + + public SysPropertiesController(SysPropertiesManager sysPropertiesManager, SysPropertyService sysPropertyService) { + this.sysPropertiesManager = sysPropertiesManager; + this.sysPropertyService = sysPropertyService; + } + + /** + * 保存系统属性信息 + * + * @param sysProperties + * @throws Exception void + * @throws + */ + @RequestMapping(value = "save", method = RequestMethod.POST) + @Override + public ApiResponse save(@RequestBody SysProperties sysProperties) { + Assert.isFalse(sysPropertiesManager.isExist(sysProperties), () -> new BusinessMessage(SysApiCodes.KEY_WORD_DUPLICATE.formatDefaultMessage(SysApiCodes.KEY_WORD_DUPLICATE.getMessage(), "别名" + sysProperties.getCode()))); + + if (sysProperties.getEncrypt() != null && sysProperties.getEncrypt() == 1) { + sysProperties.setValue(EncryptUtil.encrypt(sysProperties.getValue())); + } + + if (StrUtil.isEmpty(sysProperties.getId())) { + sysPropertiesManager.create(sysProperties); + sysPropertiesManager.reloadProperty(); + return ApiResponse.success("添加系统属性成功"); + } + + sysPropertiesManager.update(sysProperties); + sysPropertiesManager.reloadProperty(); + return ApiResponse.success("更新系统属性成功"); + + } + + /** + * 保存系统属性信息 + * + * @param code + * @throws Exception void + * @throws + */ + @RequestMapping(value = "getByCode") + public ApiResponse getByCode(@RequestParam("code") String code) { + String result = sysPropertyService.getValByCode(code); + return ApiResponse.success(result); + } + + + @Override + protected String getEntityDesc() { + return "系统属性"; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysScriptController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysScriptController.java new file mode 100644 index 00000000..346d25d7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysScriptController.java @@ -0,0 +1,73 @@ +package com.dstz.sys.rest.controller; + + +import cn.hutool.core.collection.CollUtil; +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.web.controller.AbCrudController; +import com.dstz.groovy.script.api.IGroovyScriptEngine; +import com.dstz.sys.core.entity.SysScript; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.MediaType; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import java.util.HashMap; +import java.util.Set; + +/** + *

+ * 常用脚本 前端控制器 + *

+ * + * @author wacxhs + * @since 2022-01-25 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/script") +public class SysScriptController extends AbCrudController { + + private static final Logger logger = LoggerFactory.getLogger(SysScriptController.class); + + @Autowired + private IGroovyScriptEngine groovyScriptEngine; + + @Override + protected String getEntityDesc() { + return "常用脚本"; + } + + /** + * 允许 name,alias 作为入参传入列表页 + */ + private Set accessQueryFilters = CollUtil.newHashSet("name","typeCode"); + + @Override + public Set getAccessQueryFilters() { + return accessQueryFilters; + } + + /** + * 执行脚本 + * + * @param name 脚本名称 + * @param script 脚本 + * @return 接口响应-执行结果 + */ + @RequestMapping(value = "executeScript", produces = MediaType.APPLICATION_JSON_VALUE) + public ApiResponse executeScript(@RequestParam("key") String name, @RequestParam("script") String script) { + checkIsDemoEnvironment(); + Object retVal; + try { + // 执行结果 + retVal = groovyScriptEngine.evaluate(script, new HashMap<>(0)); + } catch (Exception e) { + logger.warn("执行脚本出错, 脚本名称:{}, 脚本: {}", name, script, e); + retVal = String.format("执行脚本出错, %s", e.getMessage()); + } + return ApiResponse.success(retVal); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysToolsController.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysToolsController.java new file mode 100644 index 00000000..fb40c351 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/controller/SysToolsController.java @@ -0,0 +1,169 @@ +package com.dstz.sys.rest.controller; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +import javax.servlet.http.HttpServletResponse; + +import org.springframework.util.ClassUtils; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import com.dstz.base.api.vo.ApiResponse; +import com.dstz.base.common.constats.AbAppRestConstant; +import com.dstz.base.common.enums.GlobalApiCodes; +import com.dstz.base.common.exceptions.BusinessException; +import com.dstz.base.common.utils.ConstantUtil; +import com.dstz.base.common.utils.EnumUtil; +import com.dstz.base.common.utils.HanYuPinyinUtil; +import com.dstz.base.common.utils.JsonUtils; +import com.dstz.base.web.controller.AbBaseController; +import com.fasterxml.jackson.databind.JsonNode; + +import cn.hutool.core.collection.CollUtil; +import cn.hutool.core.date.DateUtil; +import cn.hutool.core.util.StrUtil; +import cn.hutool.extra.spring.SpringUtil; +import net.sourceforge.pinyin4j.format.HanyuPinyinCaseType; + +/** + * @author jinxia.hou + * @Name SysToolsController + * @description: 系统工具类 + * @date 2022/2/1410:09 + */ +@RestController +@RequestMapping(AbAppRestConstant.SYS_SERVICE_PREFIX + "/tools") +public class SysToolsController extends AbBaseController { + /** + *
+     * 根据一个枚举类的路径获取这个枚举的json形式,供前端使用
+     * 注意!!如果枚举在类中间,那么路径如下:com.dstz.base.db.model.Column$TYPE
+     * 
+ * + * @param path + * @param listMode + * @param key :在非list模式下,以枚举中的指定key来构建map + * @return + * @throws Exception + */ + @RequestMapping("getEnum") + public ApiResponse getEnum(@RequestParam("path") String path, @RequestParam("listMode") boolean listMode,@RequestParam(name="key",required = false) String key) throws Exception { + if (listMode) { + return ApiResponse.success(EnumUtil.toJSONArray(path)); + } + JsonNode jsonNode = EnumUtil.toJSON(path); + if(StrUtil.isEmpty(key)) { + return ApiResponse.success(jsonNode); + } + Map map = new HashMap<>(jsonNode.size()); + JsonUtils.parseObject(jsonNode, Map.class).forEach((k,v)->{ + Map m = (Map) v; + map.put(m.get(key).toString(), v); + }); + + return ApiResponse.success(map); + } + + /** + *
+     * 根据path(类路径)获取key(字段名)的常量
+     * ps:如果key为空,会把path类的全部static final的静态变量获取出来
+     * 
+ * + * @param path + * @param key + * @return + * @throws Exception + */ + @RequestMapping("getConstant") + public Object getConstant(@RequestParam("path") String path, @RequestParam("key") String key) throws Exception { + if (StrUtil.isEmpty(key)) { + return ConstantUtil.get(path); + } + return ConstantUtil.get(path, key); + } + + @RequestMapping("getInterFaceImpls") + public Object getInterFaceImpls(@RequestParam("classPath") String classPath) throws Exception { + Class clazz = Class.forName(classPath); + Map map = SpringUtil.getBeansOfType(clazz); + return map.values(); + } + + @RequestMapping("getInterFaceImplsResult") + public ApiResponse getInterFaceImplsResult(@RequestParam("classPath") String classPath) throws Exception { + Class clazz = Class.forName(classPath); + Map map = SpringUtil.getBeansOfType(clazz); + return ApiResponse.success(map.values()); + } + + /** + * @param chinese + * @param type 1:全拼 0:首字母拼音 + * @throws Exception + */ + @PostMapping("pinyin") + public void pinyin(@RequestBody Map map, HttpServletResponse response) throws Exception { + String chinese = (String) map.get("chinese"); + Integer type = (Integer) map.get("type"); + String result = ""; + if (type == 1) { + result = HanYuPinyinUtil.getPinyinString(chinese); + } else { + result = HanYuPinyinUtil.getFirstLetters(chinese, HanyuPinyinCaseType.LOWERCASE); + } + //todo oracle默认映射都大写 + /*if (DbType.ORACLE.equalsWithKey(DbContextHolder.getDbType())) { + result = result.toUpperCase();// oracle默认映射都大写 + }*/ + response.getWriter().write(result); + } + + /** + * 获取当前日期 + * + * @param format 格式化 + * @return 响应消息 + */ + @RequestMapping("getCurrentTime") + public ApiResponse getCurrentTime(@RequestParam(value = "format") String format) { + return ApiResponse.success(DateUtil.format(new Date(), format)); + } + + @RequestMapping("getResultEnum") + public Object getResultEnum(@RequestParam String path, @RequestParam(required = false, defaultValue = "false") Boolean listMode) throws Exception { + if (listMode) { + return ApiResponse.success(EnumUtil.toJSONArray(path)); + } + + return ApiResponse.success(EnumUtil.toJSON(path)); + } + + /** + * class present + * + * @param classes class name + * @return is present + */ + @RequestMapping(value = "/clsPresent", method = RequestMethod.POST) + public ApiResponse> classPresent(@RequestBody List classes) { + if (CollUtil.isEmpty(classes) || classes.size() > 5) { + return ApiResponse.fail(GlobalApiCodes.PARAMETER_INVALID.getCode(), GlobalApiCodes.PARAMETER_INVALID.getMessage()); + } + List resultList = classes.stream().map(item -> ClassUtils.isPresent(item, this.getClass().getClassLoader())).collect(Collectors.toList()); + return ApiResponse.success(resultList); + } + + @RequestMapping("trespass") + public ApiResponse trespass(@RequestParam String desc) { + throw new BusinessException(GlobalApiCodes.REMOTE_CALL_ERROR.formatDefaultMessage("非法的访问,访问信息已经被统计!")); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AuthorizationDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AuthorizationDTO.java new file mode 100644 index 00000000..995679de --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AuthorizationDTO.java @@ -0,0 +1,57 @@ +package com.dstz.sys.rest.model.dto; + +import com.dstz.sys.api.constant.RightsObjectConstants; +import com.dstz.sys.core.entity.SysAuthorization; + +import java.io.Serializable; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SaveAuthorization + * @description: + * @date 2022/2/2214:22 + */ +public class AuthorizationDTO implements Serializable { + + private static final long serialVersionUID = 2808337286344024676L; + + + private String rightsTarget; + private RightsObjectConstants rightsObject; + private List authorizationList; + + + public String getRightsTarget() { + return rightsTarget; + } + + public void setRightsTarget(String rightsTarget) { + this.rightsTarget = rightsTarget; + } + + public RightsObjectConstants getRightsObject() { + return rightsObject; + } + + public void setRightsObject(RightsObjectConstants rightsObject) { + this.rightsObject = rightsObject; + } + + public List getAuthorizationList() { + return authorizationList; + } + + public void setAuthorizationList(List authorizationList) { + this.authorizationList = authorizationList; + } + + public AuthorizationDTO() { + } + + public AuthorizationDTO(String rightsTarget, RightsObjectConstants rightsObject, List authorizationList) { + this.rightsTarget = rightsTarget; + this.rightsObject = rightsObject; + this.authorizationList = authorizationList; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AutoTranslateDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AutoTranslateDTO.java new file mode 100644 index 00000000..8991c43b --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/AutoTranslateDTO.java @@ -0,0 +1,45 @@ +package com.dstz.sys.rest.model.dto; + + +import javax.validation.constraints.NotEmpty; +import java.util.List; + +/** + * 自动翻译 + * + * @author wacxhs + */ +public class AutoTranslateDTO implements java.io.Serializable { + + private static final long serialVersionUID = -1866516373715389892L; + + /** + * 中文文本 + */ + @NotEmpty(message = "翻译文本不能为空") + private String chineseText; + + /** + * 翻译为目标语言 + */ + @NotEmpty(message = "翻译目标语言不能为空") + private List toLanguages; + + public String getChineseText() { + return chineseText; + } + + public void setChineseText(String chineseText) { + this.chineseText = chineseText; + } + + public List getToLanguages() { + return toLanguages; + } + + public void setToLanguages(List toLanguages) { + this.toLanguages = toLanguages; + } + + +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/ElementIconGetDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/ElementIconGetDTO.java new file mode 100644 index 00000000..e080121b --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/ElementIconGetDTO.java @@ -0,0 +1,42 @@ +package com.dstz.sys.rest.model.dto; + +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name ElementIconGetDTO + * @description: 获取icon 列表 + * @date 2022/3/3015:01 + */ +public class ElementIconGetDTO implements Serializable { + private static final long serialVersionUID = -295778810172188790L; + private Integer pageSize = 10; + private Integer pageNumber = 1; + private String name; + + public Integer getPageSize() { + return pageSize; + } + + public void setPageSize(Integer pageSize) { + this.pageSize = pageSize; + } + + public Integer getPageNumber() { + return pageNumber; + } + + public void setPageNumber(Integer pageNumber) { + this.pageNumber = pageNumber; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetDictDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetDictDTO.java new file mode 100644 index 00000000..08a6bad6 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetDictDTO.java @@ -0,0 +1,55 @@ +package com.dstz.sys.rest.model.dto; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name GetDictVO + * @description: 获取字典DTO + * @date 2022/8/29:34 + */ +public class GetDictDTO implements Serializable { + private static final long serialVersionUID = 3253146726465614563L; + + @NotNull(message = "dictKey 不能为空!") + private String dictKey; + private String rootName; + private Boolean hasRoot; + + public String getDictKey() { + return dictKey; + } + + public void setDictKey(String dictKey) { + this.dictKey = dictKey; + } + + public String getRootName() { + return rootName; + } + + public void setRootName(String rootName) { + this.rootName = rootName; + } + + public Boolean getHasRoot() { + if (hasRoot == null){ + return false; + } + return hasRoot; + } + + public void setHasRoot(Boolean hasRoot) { + this.hasRoot = hasRoot; + } + + @Override + public String toString() { + return "GetDictDTO{" + + "dictKey='" + dictKey + '\'' + + ", rootName='" + rootName + '\'' + + ", hasRoot=" + hasRoot + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetNextSerialNoDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetNextSerialNoDTO.java new file mode 100644 index 00000000..2b69dd82 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/GetNextSerialNoDTO.java @@ -0,0 +1,56 @@ +package com.dstz.sys.rest.model.dto; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Map; + +/** + * @author jinxia.hou + * @Name getNextSerialNoDTO + * @description: 获取下一个流水号DTO + * @date 2022/9/1611:10 + */ +public class GetNextSerialNoDTO implements Serializable { + private static final long serialVersionUID = -3420062216474976137L; + + @NotBlank + private String code; + + private String param; + + private Map paramMap; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getParam() { + return param; + } + + public void setParam(String param) { + this.param = param; + } + + public Map getParamMap() { + return paramMap; + } + + public void setParamMap(Map paramMap) { + this.paramMap = paramMap; + } + + @Override + public String toString() { + return "getNextSerialNoDTO{" + + "code='" + code + '\'' + + ", param='" + param + '\'' + + ", paramMap=" + paramMap + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParam.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParam.java new file mode 100644 index 00000000..07ae64ee --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParam.java @@ -0,0 +1,171 @@ +package com.dstz.sys.rest.model.dto; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name OnlineDocParam + * @description: 在线文档参数DTO + * @date 2023/5/249:59 + */ +public class OnlineDocParam implements Serializable { + /** + * 用户id + */ + private String userId; + /** + * 用户名称 + */ + private String userName; + /** + * 用户头像 + */ + private String userAvatar; + /** + * 文件id + */ + private String fileId; + /** + * 文件名称 + */ + private String fileName; + /** + * 文件path + */ + private String filePath; + /** + * 用权限 + */ + private Integer userRight; + /** + * 是否自动保存,默认是false + */ + private Boolean saveFlag; + /** + * 回调url + */ + private String callbackUrl; + /** + * 额外参数 + */ + private Object extraData; + /** + * 按钮权限 + */ + private String userMenuPermission; + + public OnlineDocParam(String fileId) { + this.fileId = fileId; + } + + public OnlineDocParam() { + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public String getUserAvatar() { + return userAvatar; + } + + public void setUserAvatar(String userAvatar) { + this.userAvatar = userAvatar; + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public Integer getUserRight() { + return userRight; + } + + public void setUserRight(Integer userRight) { + this.userRight = userRight; + } + + public Boolean getSaveFlag() { + if(saveFlag == null){ + return Boolean.FALSE; + } + return saveFlag; + } + + public void setSaveFlag(Boolean saveFlag) { + this.saveFlag = saveFlag; + } + + public String getCallbackUrl() { + return callbackUrl; + } + + public void setCallbackUrl(String callbackUrl) { + this.callbackUrl = callbackUrl; + } + + public Object getExtraData() { + return extraData; + } + + public void setExtraData(Object extraData) { + this.extraData = extraData; + } + + public String getUserMenuPermission() { + return userMenuPermission; + } + + public void setUserMenuPermission(String userMenuPermission) { + this.userMenuPermission = userMenuPermission; + } + + @Override + public String toString() { + return "OnlineDocParam{" + + "userId='" + userId + '\'' + + ", userName='" + userName + '\'' + + ", userAvatar='" + userAvatar + '\'' + + ", fileId='" + fileId + '\'' + + ", fileName='" + fileName + '\'' + + ", filePath='" + filePath + '\'' + + ", userRight=" + userRight + + ", saveFlag=" + saveFlag + + ", callbackUrl='" + callbackUrl + '\'' + + ", extraData=" + extraData + + ", userMenuPermission='" + userMenuPermission + '\'' + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParamDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParamDTO.java new file mode 100644 index 00000000..86631339 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OnlineDocParamDTO.java @@ -0,0 +1,49 @@ +package com.dstz.sys.rest.model.dto; + +import com.dstz.sys.api.constant.OnlineDocMethod; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name OnlinDocParamDTO + * @description: 文档操作参数DTO + * @date 2023/5/249:58 + */ +public class OnlineDocParamDTO implements Serializable { + + private Integer method; + private OnlineDocParam params; + + public Integer getMethod() { + return method; + } + + public void setMethod(OnlineDocMethod method) { + this.method = method.getKey(); + } + + public OnlineDocParam getParams() { + return params; + } + + public void setParams(OnlineDocParam params) { + this.params = params; + } + + public OnlineDocParamDTO() { + } + + public OnlineDocParamDTO(OnlineDocMethod method, OnlineDocParam params) { + this.method = method.getKey(); + this.params = params; + } + + @Override + public String toString() { + return "OnlineDocParamDTO{" + + "method=" + method + + ", params=" + params + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OperateOnlineDocDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OperateOnlineDocDTO.java new file mode 100644 index 00000000..18f766d0 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/OperateOnlineDocDTO.java @@ -0,0 +1,116 @@ +package com.dstz.sys.rest.model.dto; + +import javax.validation.constraints.NotNull; +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name OnlineDocJsonParamDTO + * @description: 在线文档请求接口DTO + * @date 2023/5/2410:31 + */ +public class OperateOnlineDocDTO implements Serializable { + /** + * 文件id + */ + @NotNull(message = "文件Id 不能为空") + private String fileId; + + /** + * 文件名,创建文件的时候必填 + */ + private String fileName; + + /** + * 用权限 + */ + private Integer userRight; + /** + * 是否自动保存,默认是false + */ + private Boolean saveFlag; + + /** + * 额外参数 + */ + private Object extraData; + /** + * 按钮权限 + */ + private String userMenuPermission; + + public OperateOnlineDocDTO(String fileId) { + this.fileId = fileId; + } + + public OperateOnlineDocDTO(String fileId, Integer userRight) { + this.fileId = fileId; + this.userRight = userRight; + } + + public OperateOnlineDocDTO() { + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public Integer getUserRight() { + return userRight; + } + + public void setUserRight(Integer userRight) { + this.userRight = userRight; + } + + public Boolean getSaveFlag() { + if (saveFlag == null){ + return Boolean.FALSE; + } + return saveFlag; + } + + public void setSaveFlag(Boolean saveFlag) { + this.saveFlag = saveFlag; + } + + public Object getExtraData() { + return extraData; + } + + public void setExtraData(Object extraData) { + this.extraData = extraData; + } + + public String getUserMenuPermission() { + return userMenuPermission; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public void setUserMenuPermission(String userMenuPermission) { + this.userMenuPermission = userMenuPermission; + } + + @Override + public String toString() { + return "OperateOnlineDocDTO{" + + "fileId='" + fileId + '\'' + + ", fileName='" + fileName + '\'' + + ", userRight=" + userRight + + ", saveFlag=" + saveFlag + + ", extraData=" + extraData + + ", userMenuPermission='" + userMenuPermission + '\'' + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/SysWorkHandoverEditDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/SysWorkHandoverEditDTO.java new file mode 100644 index 00000000..b49c186c --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/SysWorkHandoverEditDTO.java @@ -0,0 +1,40 @@ +package com.dstz.sys.rest.model.dto; + +import java.io.Serializable; +import java.util.Set; + +/** + * @author jinxia.hou + * @Name SysWorkHandoverEditDTO + * @description: + * @date 2022/2/2210:52 + */ +public class SysWorkHandoverEditDTO implements Serializable { + + private static final long serialVersionUID = 8974946297588709585L; + /** + * 用户编号 + */ + private String userId; + + /** + * 接收用户编号 + */ + private Set receiveUserId; + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public Set getReceiveUserId() { + return receiveUserId; + } + + public void setReceiveUserId(Set receiveUserId) { + this.receiveUserId = receiveUserId; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestExpressionDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestExpressionDTO.java new file mode 100644 index 00000000..ac197c0c --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestExpressionDTO.java @@ -0,0 +1,64 @@ +package com.dstz.sys.rest.model.dto; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name TestExpressionDTO + * @description: 测试日志表达式Dto + * @date 2022/3/310:41 + */ +public class TestExpressionDTO implements Serializable { + private static final long serialVersionUID = -5631456838690512653L; + + /** + * 请求参数 + */ + private String requestParam; + /** + * 请求体 + */ + private String requestBody; + + /** + * 响应体 + */ + private String responseBody; + + /** + * 表达式 + */ + private String expressionString; + + public String getRequestParam() { + return requestParam; + } + + public void setRequestParam(String requestParam) { + this.requestParam = requestParam; + } + + public String getRequestBody() { + return requestBody; + } + + public void setRequestBody(String requestBody) { + this.requestBody = requestBody; + } + + public String getResponseBody() { + return responseBody; + } + + public void setResponseBody(String responseBody) { + this.responseBody = responseBody; + } + + public String getExpressionString() { + return expressionString; + } + + public void setExpressionString(String expressionString) { + this.expressionString = expressionString; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestHolidayConfDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestHolidayConfDTO.java new file mode 100644 index 00000000..68151fa7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/TestHolidayConfDTO.java @@ -0,0 +1,39 @@ +package com.dstz.sys.rest.model.dto; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author jinxia.hou + * @Name TestHolidayConfDTO + * @description: + * @date 2022/3/2317:49 + */ +public class TestHolidayConfDTO implements Serializable { + private static final long serialVersionUID = -8791184722892342808L; + /** + * 开始时间 + */ + private Date startDay; + + /** + * n 天 + */ + private Integer day; + + public Date getStartDay() { + return startDay; + } + + public void setStartDay(Date startDay) { + this.startDay = startDay; + } + + public Integer getDay() { + return day; + } + + public void setDay(Integer day) { + this.day = day; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/UpdateFileDTO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/UpdateFileDTO.java new file mode 100644 index 00000000..05d335a5 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/dto/UpdateFileDTO.java @@ -0,0 +1,61 @@ +package com.dstz.sys.rest.model.dto; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.InputStream; +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name UpdateFileDTO + * @description: 更新文件dto + * @date 2023/5/2417:17 + */ +public class UpdateFileDTO implements Serializable { + private String fileId; + private String userId; + private String userName; + private InputStream fileStream; + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserName() { + return userName; + } + + public void setUserName(String userName) { + this.userName = userName; + } + + public InputStream getFileStream() { + return fileStream; + } + + public void setFileStream(InputStream fileStream) { + this.fileStream = fileStream; + } + + @Override + public String toString() { + return "UpdateFileDTO{" + + "fileId='" + fileId + '\'' + + ", userId='" + userId + '\'' + + ", userName='" + userName + '\'' + + ", file=" + fileStream + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/AutoTranslateVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/AutoTranslateVO.java new file mode 100644 index 00000000..7878b8dd --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/AutoTranslateVO.java @@ -0,0 +1,50 @@ +package com.dstz.sys.rest.model.vo; + +/** + * 自动翻译 VO + * + * @author wacxhs + */ +public class AutoTranslateVO implements java.io.Serializable { + + private static final long serialVersionUID = -1414897543240472067L; + + /** + * 失败文本 + */ + private String errorMsg; + + /** + * 语言 + */ + private String language; + + /** + * 翻译后的文本 + */ + private String dstText; + + public String getErrorMsg() { + return errorMsg; + } + + public void setErrorMsg(String errorMsg) { + this.errorMsg = errorMsg; + } + + public String getLanguage() { + return language; + } + + public void setLanguage(String language) { + this.language = language; + } + + public String getDstText() { + return dstText; + } + + public void setDstText(String dstText) { + this.dstText = dstText; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/CreateAndOpenVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/CreateAndOpenVO.java new file mode 100644 index 00000000..7c362863 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/CreateAndOpenVO.java @@ -0,0 +1,48 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name createAndOpenVO + * @description: 创建文档并打开文档结果VO + * @date 2023/6/215:50 + */ +public class CreateAndOpenVO implements Serializable { + + private String url; + private String fileId; + + public String getUrl() { + return url; + } + + public void setUrl(String url) { + this.url = url; + } + + public String getFileId() { + return fileId; + } + + public void setFileId(String fileId) { + this.fileId = fileId; + } + + public CreateAndOpenVO(String url, String fileId) { + this.url = url; + this.fileId = fileId; + } + + public CreateAndOpenVO() { + } + + @Override + public String + toString() { + return "createAndOpenVO{" + + "url='" + url + '\'' + + ", fileId='" + fileId + '\'' + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ElementIconVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ElementIconVO.java new file mode 100644 index 00000000..bf7e7e63 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ElementIconVO.java @@ -0,0 +1,2283 @@ +package com.dstz.sys.rest.model.vo; +import java.util.Arrays; +import java.util.List; + +/** + * @author jinxia.hou + * @Name ElementIconVO + * @description: Element UI 图标集合 + * @date 2022/3/3014:51 + */ +public interface ElementIconVO { + List iconList= Arrays.asList("24-hours-fill", + "24-hours-line", + "4k-fill", + "4k-line", + "a-b", + "account-box-fill", + "account-box-line", + "account-circle-fill", + "account-circle-line", + "account-pin-box-fill", + "account-pin-box-line", + "account-pin-circle-fill", + "account-pin-circle-line", + "add-box-fill", + "add-box-line", + "add-circle-fill", + "add-circle-line", + "add-fill", + "add-line", + "admin-fill", + "admin-line", + /* "advertisement-fill", + "advertisement-line", */ + "airplay-fill", + "airplay-line", + "alarm-fill", + "alarm-line", + "alarm-warning-fill", + "alarm-warning-line", + "album-fill", + "album-line", + "alert-fill", + "alert-line", + "aliens-fill", + "aliens-line", + "align-bottom", + "align-center", + "align-justify", + "align-left", + "align-right", + "align-top", + "align-vertically", + "alipay-fill", + "alipay-line", + "amazon-fill", + "amazon-line", + "anchor-fill", + "anchor-line", + "ancient-gate-fill", + "ancient-gate-line", + "ancient-pavilion-fill", + "ancient-pavilion-line", + "android-fill", + "android-line", + "angularjs-fill", + "angularjs-line", + "anticlockwise-2-fill", + "anticlockwise-2-line", + "anticlockwise-fill", + "anticlockwise-line", + "app-store-fill", + "app-store-line", + "apple-fill", + "apple-line", + "apps-2-fill", + "apps-2-line", + "apps-fill", + "apps-line", + "archive-drawer-fill", + "archive-drawer-line", + "archive-fill", + "archive-line", + "arrow-down-circle-fill", + "arrow-down-circle-line", + "arrow-down-fill", + "arrow-down-line", + "arrow-down-s-fill", + "arrow-down-s-line", + "arrow-drop-down-fill", + "arrow-drop-down-line", + "arrow-drop-left-fill", + "arrow-drop-left-line", + "arrow-drop-right-fill", + "arrow-drop-right-line", + "arrow-drop-up-fill", + "arrow-drop-up-line", + "arrow-go-back-fill", + "arrow-go-back-line", + "arrow-go-forward-fill", + "arrow-go-forward-line", + "arrow-left-circle-fill", + "arrow-left-circle-line", + "arrow-left-down-fill", + "arrow-left-down-line", + "arrow-left-fill", + "arrow-left-line", + "arrow-left-right-fill", + "arrow-left-right-line", + "arrow-left-s-fill", + "arrow-left-s-line", + "arrow-left-up-fill", + "arrow-left-up-line", + "arrow-right-circle-fill", + "arrow-right-circle-line", + "arrow-right-down-fill", + "arrow-right-down-line", + "arrow-right-fill", + "arrow-right-line", + "arrow-right-s-fill", + "arrow-right-s-line", + "arrow-right-up-fill", + "arrow-right-up-line", + "arrow-up-circle-fill", + "arrow-up-circle-line", + "arrow-up-down-fill", + "arrow-up-down-line", + "arrow-up-fill", + "arrow-up-line", + "arrow-up-s-fill", + "arrow-up-s-line", + "artboard-2-fill", + "artboard-2-line", + "artboard-fill", + "artboard-line", + "article-fill", + "article-line", + "aspect-ratio-fill", + "aspect-ratio-line", + "asterisk", + "at-fill", + "at-line", + "attachment-2", + "attachment-fill", + "attachment-line", + "auction-fill", + "auction-line", + "award-fill", + "award-line", + "baidu-fill", + "baidu-line", + "ball-pen-fill", + "ball-pen-line", + "bank-card-2-fill", + "bank-card-2-line", + "bank-card-fill", + "bank-card-line", + "bank-fill", + "bank-line", + "bar-chart-2-fill", + "bar-chart-2-line", + "bar-chart-box-fill", + "bar-chart-box-line", + "bar-chart-fill", + "bar-chart-grouped-fill", + "bar-chart-grouped-line", + "bar-chart-horizontal-fill", + "bar-chart-horizontal-line", + "bar-chart-line", + "barcode-box-fill", + "barcode-box-line", + "barcode-fill", + "barcode-line", + "barricade-fill", + "barricade-line", + "base-station-fill", + "base-station-line", + "basketball-fill", + "basketball-line", + "battery-2-charge-fill", + "battery-2-charge-line", + "battery-2-fill", + "battery-2-line", + "battery-charge-fill", + "battery-charge-line", + "battery-fill", + "battery-line", + "battery-low-fill", + "battery-low-line", + "battery-saver-fill", + "battery-saver-line", + "battery-share-fill", + "battery-share-line", + "bear-smile-fill", + "bear-smile-line", + "behance-fill", + "behance-line", + "bell-fill", + "bell-line", + "bike-fill", + "bike-line", + "bilibili-fill", + "bilibili-line", + "bill-fill", + "bill-line", + "billiards-fill", + "billiards-line", + "bit-coin-fill", + "bit-coin-line", + "blaze-fill", + "blaze-line", + "bluetooth-connect-fill", + "bluetooth-connect-line", + "bluetooth-fill", + "bluetooth-line", + "blur-off-fill", + "blur-off-line", + "body-scan-fill", + "body-scan-line", + "bold", + "book-2-fill", + "book-2-line", + "book-3-fill", + "book-3-line", + "book-fill", + "book-line", + "book-mark-fill", + "book-mark-line", + "book-open-fill", + "book-open-line", + "book-read-fill", + "book-read-line", + "booklet-fill", + "booklet-line", + "bookmark-2-fill", + "bookmark-2-line", + "bookmark-3-fill", + "bookmark-3-line", + "bookmark-fill", + "bookmark-line", + "boxing-fill", + "boxing-line", + "braces-fill", + "braces-line", + "brackets-fill", + "brackets-line", + "briefcase-2-fill", + "briefcase-2-line", + "briefcase-3-fill", + "briefcase-3-line", + "briefcase-4-fill", + "briefcase-4-line", + "briefcase-5-fill", + "briefcase-5-line", + "briefcase-fill", + "briefcase-line", + "bring-forward", + "bring-to-front", + "broadcast-fill", + "broadcast-line", + "brush-2-fill", + "brush-2-line", + "brush-3-fill", + "brush-3-line", + "brush-4-fill", + "brush-4-line", + "brush-fill", + "brush-line", + "bubble-chart-fill", + "bubble-chart-line", + "bug-2-fill", + "bug-2-line", + "bug-fill", + "bug-line", + "building-2-fill", + "building-2-line", + "building-3-fill", + "building-3-line", + "building-4-fill", + "building-4-line", + "building-fill", + "building-line", + "bus-2-fill", + "bus-2-line", + "bus-fill", + "bus-line", + "bus-wifi-fill", + "bus-wifi-line", + "cactus-fill", + "cactus-line", + "cake-2-fill", + "cake-2-line", + "cake-3-fill", + "cake-3-line", + "cake-fill", + "cake-line", + "calculator-fill", + "calculator-line", + "calendar-2-fill", + "calendar-2-line", + "calendar-check-fill", + "calendar-check-line", + "calendar-event-fill", + "calendar-event-line", + "calendar-fill", + "calendar-line", + "calendar-todo-fill", + "calendar-todo-line", + "camera-2-fill", + "camera-2-line", + "camera-3-fill", + "camera-3-line", + "camera-fill", + "camera-lens-fill", + "camera-lens-line", + "camera-line", + "camera-off-fill", + "camera-off-line", + "camera-switch-fill", + "camera-switch-line", + "capsule-fill", + "capsule-line", + "car-fill", + "car-line", + "car-washing-fill", + "car-washing-line", + "caravan-fill", + "caravan-line", + "cast-fill", + "cast-line", + "cellphone-fill", + "cellphone-line", + "celsius-fill", + "celsius-line", + "centos-fill", + "centos-line", + "character-recognition-fill", + "character-recognition-line", + "charging-pile-2-fill", + "charging-pile-2-line", + "charging-pile-fill", + "charging-pile-line", + "chat-1-fill", + "chat-1-line", + "chat-2-fill", + "chat-2-line", + "chat-3-fill", + "chat-3-line", + "chat-4-fill", + "chat-4-line", + "chat-check-fill", + "chat-check-line", + "chat-delete-fill", + "chat-delete-line", + "chat-download-fill", + "chat-download-line", + "chat-follow-up-fill", + "chat-follow-up-line", + "chat-forward-fill", + "chat-forward-line", + "chat-heart-fill", + "chat-heart-line", + "chat-history-fill", + "chat-history-line", + "chat-new-fill", + "chat-new-line", + "chat-off-fill", + "chat-off-line", + "chat-poll-fill", + "chat-poll-line", + "chat-private-fill", + "chat-private-line", + "chat-quote-fill", + "chat-quote-line", + "chat-settings-fill", + "chat-settings-line", + "chat-smile-2-fill", + "chat-smile-2-line", + "chat-smile-3-fill", + "chat-smile-3-line", + "chat-smile-fill", + "chat-smile-line", + "chat-upload-fill", + "chat-upload-line", + "chat-voice-fill", + "chat-voice-line", + "check-double-fill", + "check-double-line", + "check-fill", + "check-line", + "checkbox-blank-circle-fill", + "checkbox-blank-circle-line", + "checkbox-blank-fill", + "checkbox-blank-line", + "checkbox-circle-fill", + "checkbox-circle-line", + "checkbox-fill", + "checkbox-indeterminate-fill", + "checkbox-indeterminate-line", + "checkbox-line", + "checkbox-multiple-blank-fill", + "checkbox-multiple-blank-line", + "checkbox-multiple-fill", + "checkbox-multiple-line", + "china-railway-fill", + "china-railway-line", + "chrome-fill", + "chrome-line", + "clapperboard-fill", + "clapperboard-line", + "clipboard-fill", + "clipboard-line", + "clockwise-2-fill", + "clockwise-2-line", + "clockwise-fill", + "clockwise-line", + "close-circle-fill", + "close-circle-line", + "close-fill", + "close-line", + "closed-captioning-fill", + "closed-captioning-line", + "cloud-fill", + "cloud-line", + "cloud-off-fill", + "cloud-off-line", + "cloud-windy-fill", + "cloud-windy-line", + "cloudy-2-fill", + "cloudy-2-line", + "cloudy-fill", + "cloudy-line", + "code-box-fill", + "code-box-line", + "code-fill", + "code-line", + "code-s-fill", + "code-s-line", + "code-s-slash-fill", + "code-s-slash-line", + "code-view", + "codepen-fill", + "codepen-line", + "coin-fill", + "coin-line", + "coins-fill", + "coins-line", + "collage-fill", + "collage-line", + "command-fill", + "command-line", + "community-fill", + "community-line", + "compass-2-fill", + "compass-2-line", + "compass-3-fill", + "compass-3-line", + "compass-4-fill", + "compass-4-line", + "compass-discover-fill", + "compass-discover-line", + "compass-fill", + "compass-line", + "compasses-2-fill", + "compasses-2-line", + "compasses-fill", + "compasses-line", + "computer-fill", + "computer-line", + "contacts-book-2-fill", + "contacts-book-2-line", + "contacts-book-fill", + "contacts-book-line", + "contacts-book-upload-fill", + "contacts-book-upload-line", + "contacts-fill", + "contacts-line", + "contrast-2-fill", + "contrast-2-line", + "contrast-drop-2-fill", + "contrast-drop-2-line", + "contrast-drop-fill", + "contrast-drop-line", + "contrast-fill", + "contrast-line", + "copper-coin-fill", + "copper-coin-line", + "copper-diamond-fill", + "copper-diamond-line", + "copyleft-fill", + "copyleft-line", + "copyright-fill", + "copyright-line", + "coreos-fill", + "coreos-line", + "coupon-2-fill", + "coupon-2-line", + "coupon-3-fill", + "coupon-3-line", + "coupon-4-fill", + "coupon-4-line", + "coupon-5-fill", + "coupon-5-line", + "coupon-fill", + "coupon-line", + "cpu-fill", + "cpu-line", + "creative-commons-by-fill", + "creative-commons-by-line", + "creative-commons-fill", + "creative-commons-line", + "creative-commons-nc-fill", + "creative-commons-nc-line", + "creative-commons-nd-fill", + "creative-commons-nd-line", + "creative-commons-sa-fill", + "creative-commons-sa-line", + "creative-commons-zero-fill", + "creative-commons-zero-line", + "criminal-fill", + "criminal-line", + "crop-2-fill", + "crop-2-line", + "crop-fill", + "crop-line", + "css3-fill", + "css3-line", + "cup-fill", + "cup-line", + "currency-fill", + "currency-line", + "cursor-fill", + "cursor-line", + "customer-service-2-fill", + "customer-service-2-line", + "customer-service-fill", + "customer-service-line", + "dashboard-2-fill", + "dashboard-2-line", + "dashboard-3-fill", + "dashboard-3-line", + "dashboard-fill", + "dashboard-line", + "database-2-fill", + "database-2-line", + "database-fill", + "database-line", + "delete-back-2-fill", + "delete-back-2-line", + "delete-back-fill", + "delete-back-line", + "delete-bin-2-fill", + "delete-bin-2-line", + "delete-bin-3-fill", + "delete-bin-3-line", + "delete-bin-4-fill", + "delete-bin-4-line", + "delete-bin-5-fill", + "delete-bin-5-line", + "delete-bin-6-fill", + "delete-bin-6-line", + "delete-bin-7-fill", + "delete-bin-7-line", + "delete-bin-fill", + "delete-bin-line", + "delete-column", + "delete-row", + "device-fill", + "device-line", + "device-recover-fill", + "device-recover-line", + "dingding-fill", + "dingding-line", + "direction-fill", + "direction-line", + "disc-fill", + "disc-line", + "discord-fill", + "discord-line", + "discuss-fill", + "discuss-line", + "dislike-fill", + "dislike-line", + "disqus-fill", + "disqus-line", + "divide-fill", + "divide-line", + "donut-chart-fill", + "donut-chart-line", + "door-closed-fill", + "door-closed-line", + "door-fill", + "door-line", + "door-lock-box-fill", + "door-lock-box-line", + "door-lock-fill", + "door-lock-line", + "door-open-fill", + "door-open-line", + "dossier-fill", + "dossier-line", + "douban-fill", + "douban-line", + "double-quotes-l", + "double-quotes-r", + "download-2-fill", + "download-2-line", + "download-cloud-2-fill", + "download-cloud-2-line", + "download-cloud-fill", + "download-cloud-line", + "download-fill", + "download-line", + "draft-fill", + "draft-line", + "drag-drop-fill", + "drag-drop-line", + "drag-move-2-fill", + "drag-move-2-line", + "drag-move-fill", + "drag-move-line", + "dribbble-fill", + "dribbble-line", + "drive-fill", + "drive-line", + "drizzle-fill", + "drizzle-line", + "drop-fill", + "drop-line", + "dropbox-fill", + "dropbox-line", + "dual-sim-1-fill", + "dual-sim-1-line", + "dual-sim-2-fill", + "dual-sim-2-line", + "dv-fill", + "dv-line", + "dvd-fill", + "dvd-line", + "e-bike-2-fill", + "e-bike-2-line", + "e-bike-fill", + "e-bike-line", + "earth-fill", + "earth-line", + "earthquake-fill", + "earthquake-line", + "edge-fill", + "edge-line", + "edit-2-fill", + "edit-2-line", + "edit-box-fill", + "edit-box-line", + "edit-circle-fill", + "edit-circle-line", + "edit-fill", + "edit-line", + "eject-fill", + "eject-line", + "emotion-2-fill", + "emotion-2-line", + "emotion-fill", + "emotion-happy-fill", + "emotion-happy-line", + "emotion-laugh-fill", + "emotion-laugh-line", + "emotion-line", + "emotion-normal-fill", + "emotion-normal-line", + "emotion-sad-fill", + "emotion-sad-line", + "emotion-unhappy-fill", + "emotion-unhappy-line", + "empathize-fill", + "empathize-line", + "emphasis-cn", + "emphasis", + "english-input", + "equalizer-fill", + "equalizer-line", + "eraser-fill", + "eraser-line", + "error-warning-fill", + "error-warning-line", + "evernote-fill", + "evernote-line", + "exchange-box-fill", + "exchange-box-line", + "exchange-cny-fill", + "exchange-cny-line", + "exchange-dollar-fill", + "exchange-dollar-line", + "exchange-fill", + "exchange-funds-fill", + "exchange-funds-line", + "exchange-line", + "external-link-fill", + "external-link-line", + "eye-2-fill", + "eye-2-line", + "eye-close-fill", + "eye-close-line", + "eye-fill", + "eye-line", + "eye-off-fill", + "eye-off-line", + "facebook-box-fill", + "facebook-box-line", + "facebook-circle-fill", + "facebook-circle-line", + "facebook-fill", + "facebook-line", + "fahrenheit-fill", + "fahrenheit-line", + "feedback-fill", + "feedback-line", + "file-2-fill", + "file-2-line", + "file-3-fill", + "file-3-line", + "file-4-fill", + "file-4-line", + "file-add-fill", + "file-add-line", + "file-chart-2-fill", + "file-chart-2-line", + "file-chart-fill", + "file-chart-line", + "file-cloud-fill", + "file-cloud-line", + "file-code-fill", + "file-code-line", + "file-copy-2-fill", + "file-copy-2-line", + "file-copy-fill", + "file-copy-line", + "file-damage-fill", + "file-damage-line", + "file-download-fill", + "file-download-line", + "file-edit-fill", + "file-edit-line", + "file-excel-2-fill", + "file-excel-2-line", + "file-excel-fill", + "file-excel-line", + "file-fill", + "file-forbid-fill", + "file-forbid-line", + "file-gif-fill", + "file-gif-line", + "file-history-fill", + "file-history-line", + "file-hwp-fill", + "file-hwp-line", + "file-info-fill", + "file-info-line", + "file-line", + "file-list-2-fill", + "file-list-2-line", + "file-list-3-fill", + "file-list-3-line", + "file-list-fill", + "file-list-line", + "file-lock-fill", + "file-lock-line", + "file-mark-fill", + "file-mark-line", + "file-music-fill", + "file-music-line", + "file-paper-2-fill", + "file-paper-2-line", + "file-paper-fill", + "file-paper-line", + "file-pdf-fill", + "file-pdf-line", + "file-ppt-2-fill", + "file-ppt-2-line", + "file-ppt-fill", + "file-ppt-line", + "file-reduce-fill", + "file-reduce-line", + "file-search-fill", + "file-search-line", + "file-settings-fill", + "file-settings-line", + "file-shield-2-fill", + "file-shield-2-line", + "file-shield-fill", + "file-shield-line", + "file-shred-fill", + "file-shred-line", + "file-text-fill", + "file-text-line", + "file-transfer-fill", + "file-transfer-line", + "file-unknow-fill", + "file-unknow-line", + "file-upload-fill", + "file-upload-line", + "file-user-fill", + "file-user-line", + "file-warning-fill", + "file-warning-line", + "file-word-2-fill", + "file-word-2-line", + "file-word-fill", + "file-word-line", + "file-zip-fill", + "file-zip-line", + "film-fill", + "film-line", + "filter-2-fill", + "filter-2-line", + "filter-3-fill", + "filter-3-line", + "filter-fill", + "filter-line", + "filter-off-fill", + "filter-off-line", + "find-replace-fill", + "find-replace-line", + "finder-fill", + "finder-line", + "fingerprint-2-fill", + "fingerprint-2-line", + "fingerprint-fill", + "fingerprint-line", + "fire-fill", + "fire-line", + "firefox-fill", + "firefox-line", + "first-aid-kit-fill", + "first-aid-kit-line", + "flag-2-fill", + "flag-2-line", + "flag-fill", + "flag-line", + "flashlight-fill", + "flashlight-line", + "flask-fill", + "flask-line", + "flight-land-fill", + "flight-land-line", + "flight-takeoff-fill", + "flight-takeoff-line", + "flood-fill", + "flood-line", + "flow-chart", + "flutter-fill", + "flutter-line", + "focus-2-fill", + "focus-2-line", + "focus-3-fill", + "focus-3-line", + "focus-fill", + "focus-line", + "foggy-fill", + "foggy-line", + "folder-2-fill", + "folder-2-line", + "folder-3-fill", + "folder-3-line", + "folder-4-fill", + "folder-4-line", + "folder-5-fill", + "folder-5-line", + "folder-add-fill", + "folder-add-line", + "folder-chart-2-fill", + "folder-chart-2-line", + "folder-chart-fill", + "folder-chart-line", + "folder-download-fill", + "folder-download-line", + "folder-fill", + "folder-forbid-fill", + "folder-forbid-line", + "folder-history-fill", + "folder-history-line", + "folder-info-fill", + "folder-info-line", + "folder-keyhole-fill", + "folder-keyhole-line", + "folder-line", + "folder-lock-fill", + "folder-lock-line", + "folder-music-fill", + "folder-music-line", + "folder-open-fill", + "folder-open-line", + "folder-received-fill", + "folder-received-line", + "folder-reduce-fill", + "folder-reduce-line", + "folder-settings-fill", + "folder-settings-line", + "folder-shared-fill", + "folder-shared-line", + "folder-shield-2-fill", + "folder-shield-2-line", + "folder-shield-fill", + "folder-shield-line", + "folder-transfer-fill", + "folder-transfer-line", + "folder-unknow-fill", + "folder-unknow-line", + "folder-upload-fill", + "folder-upload-line", + "folder-user-fill", + "folder-user-line", + "folder-warning-fill", + "folder-warning-line", + "folder-zip-fill", + "folder-zip-line", + "folders-fill", + "folders-line", + "font-color", + "font-size-2", + "font-size", + "football-fill", + "football-line", + "footprint-fill", + "footprint-line", + "forbid-2-fill", + "forbid-2-line", + "forbid-fill", + "forbid-line", + "format-clear", + "fridge-fill", + "fridge-line", + "fullscreen-exit-fill", + "fullscreen-exit-line", + "fullscreen-fill", + "fullscreen-line", + "function-fill", + "function-line", + "functions", + "funds-box-fill", + "funds-box-line", + "funds-fill", + "funds-line", + "gallery-fill", + "gallery-line", + "gallery-upload-fill", + "gallery-upload-line", + "game-fill", + "game-line", + "gamepad-fill", + "gamepad-line", + "gas-station-fill", + "gas-station-line", + "gatsby-fill", + "gatsby-line", + "genderless-fill", + "genderless-line", + "ghost-2-fill", + "ghost-2-line", + "ghost-fill", + "ghost-line", + "ghost-smile-fill", + "ghost-smile-line", + "gift-2-fill", + "gift-2-line", + "gift-fill", + "gift-line", + "git-branch-fill", + "git-branch-line", + "git-commit-fill", + "git-commit-line", + "git-merge-fill", + "git-merge-line", + "git-pull-request-fill", + "git-pull-request-line", + "git-repository-commits-fill", + "git-repository-commits-line", + "git-repository-fill", + "git-repository-line", + "git-repository-private-fill", + "git-repository-private-line", + "github-fill", + "github-line", + "gitlab-fill", + "gitlab-line", + "global-fill", + "global-line", + "globe-fill", + "globe-line", + "goblet-fill", + "goblet-line", + "google-fill", + "google-line", + "google-play-fill", + "google-play-line", + "government-fill", + "government-line", + "gps-fill", + "gps-line", + "gradienter-fill", + "gradienter-line", + "grid-fill", + "grid-line", + "group-2-fill", + "group-2-line", + "group-fill", + "group-line", + "guide-fill", + "guide-line", + "h-1", + "h-2", + "h-3", + "h-4", + "h-5", + "h-6", + "hail-fill", + "hail-line", + "hammer-fill", + "hammer-line", + "hand-coin-fill", + "hand-coin-line", + "hand-heart-fill", + "hand-heart-line", + "hand-sanitizer-fill", + "hand-sanitizer-line", + "handbag-fill", + "handbag-line", + "hard-drive-2-fill", + "hard-drive-2-line", + "hard-drive-fill", + "hard-drive-line", + "hashtag", + "haze-2-fill", + "haze-2-line", + "haze-fill", + "haze-line", + "hd-fill", + "hd-line", + "heading", + "headphone-fill", + "headphone-line", + "health-book-fill", + "health-book-line", + "heart-2-fill", + "heart-2-line", + "heart-3-fill", + "heart-3-line", + "heart-add-fill", + "heart-add-line", + "heart-fill", + "heart-line", + "heart-pulse-fill", + "heart-pulse-line", + "hearts-fill", + "hearts-line", + "heavy-showers-fill", + "heavy-showers-line", + "history-fill", + "history-line", + "home-2-fill", + "home-2-line", + "home-3-fill", + "home-3-line", + "home-4-fill", + "home-4-line", + "home-5-fill", + "home-5-line", + "home-6-fill", + "home-6-line", + "home-7-fill", + "home-7-line", + "home-8-fill", + "home-8-line", + "home-fill", + "home-gear-fill", + "home-gear-line", + "home-heart-fill", + "home-heart-line", + "home-line", + "home-smile-2-fill", + "home-smile-2-line", + "home-smile-fill", + "home-smile-line", + "home-wifi-fill", + "home-wifi-line", + "honor-of-kings-fill", + "honor-of-kings-line", + "honour-fill", + "honour-line", + "hospital-fill", + "hospital-line", + "hotel-bed-fill", + "hotel-bed-line", + "hotel-fill", + "hotel-line", + "hotspot-fill", + "hotspot-line", + "hq-fill", + "hq-line", + "html5-fill", + "html5-line", + "ie-fill", + "ie-line", + "image-2-fill", + "image-2-line", + "image-add-fill", + "image-add-line", + "image-edit-fill", + "image-edit-line", + "image-fill", + "image-line", + "inbox-archive-fill", + "inbox-archive-line", + "inbox-fill", + "inbox-line", + "inbox-unarchive-fill", + "inbox-unarchive-line", + "increase-decrease-fill", + "increase-decrease-line", + "indent-decrease", + "indent-increase", + "indeterminate-circle-fill", + "indeterminate-circle-line", + "information-fill", + "information-line", + "infrared-thermometer-fill", + "infrared-thermometer-line", + "ink-bottle-fill", + "ink-bottle-line", + "input-cursor-move", + "input-method-fill", + "input-method-line", + "insert-column-left", + "insert-column-right", + "insert-row-bottom", + "insert-row-top", + "instagram-fill", + "instagram-line", + "install-fill", + "install-line", + "invision-fill", + "invision-line", + "italic", + "kakao-talk-fill", + "kakao-talk-line", + "key-2-fill", + "key-2-line", + "key-fill", + "key-line", + "keyboard-box-fill", + "keyboard-box-line", + "keyboard-fill", + "keyboard-line", + "keynote-fill", + "keynote-line", + "knife-blood-fill", + "knife-blood-line", + "knife-fill", + "knife-line", + "landscape-fill", + "landscape-line", + "layout-2-fill", + "layout-2-line", + "layout-3-fill", + "layout-3-line", + "layout-4-fill", + "layout-4-line", + "layout-5-fill", + "layout-5-line", + "layout-6-fill", + "layout-6-line", + "layout-bottom-2-fill", + "layout-bottom-2-line", + "layout-bottom-fill", + "layout-bottom-line", + "layout-column-fill", + "layout-column-line", + "layout-fill", + "layout-grid-fill", + "layout-grid-line", + "layout-left-2-fill", + "layout-left-2-line", + "layout-left-fill", + "layout-left-line", + "layout-line", + "layout-masonry-fill", + "layout-masonry-line", + "layout-right-2-fill", + "layout-right-2-line", + "layout-right-fill", + "layout-right-line", + "layout-row-fill", + "layout-row-line", + "layout-top-2-fill", + "layout-top-2-line", + "layout-top-fill", + "layout-top-line", + "leaf-fill", + "leaf-line", + "lifebuoy-fill", + "lifebuoy-line", + "lightbulb-fill", + "lightbulb-flash-fill", + "lightbulb-flash-line", + "lightbulb-line", + "line-chart-fill", + "line-chart-line", + "line-fill", + "line-height", + "line-line", + "link-m", + "link-unlink-m", + "link-unlink", + "link", + "linkedin-box-fill", + "linkedin-box-line", + "linkedin-fill", + "linkedin-line", + "links-fill", + "links-line", + "list-check-2", + "list-check", + "list-ordered", + "list-settings-fill", + "list-settings-line", + "list-unordered", + "live-fill", + "live-line", + "loader-2-fill", + "loader-2-line", + "loader-3-fill", + "loader-3-line", + "loader-4-fill", + "loader-4-line", + "loader-5-fill", + "loader-5-line", + "loader-fill", + "loader-line", + "lock-2-fill", + "lock-2-line", + "lock-fill", + "lock-line", + "lock-password-fill", + "lock-password-line", + "lock-unlock-fill", + "lock-unlock-line", + "login-box-fill", + "login-box-line", + "login-circle-fill", + "login-circle-line", + "logout-box-fill", + "logout-box-line", + "logout-box-r-fill", + "logout-box-r-line", + "logout-circle-fill", + "logout-circle-line", + "logout-circle-r-fill", + "logout-circle-r-line", + "luggage-cart-fill", + "luggage-cart-line", + "luggage-deposit-fill", + "luggage-deposit-line", + "lungs-fill", + "lungs-line", + "mac-fill", + "mac-line", + "macbook-fill", + "macbook-line", + "magic-fill", + "magic-line", + "mail-add-fill", + "mail-add-line", + "mail-check-fill", + "mail-check-line", + "mail-close-fill", + "mail-close-line", + "mail-download-fill", + "mail-download-line", + "mail-fill", + "mail-forbid-fill", + "mail-forbid-line", + "mail-line", + "mail-lock-fill", + "mail-lock-line", + "mail-open-fill", + "mail-open-line", + "mail-send-fill", + "mail-send-line", + "mail-settings-fill", + "mail-settings-line", + "mail-star-fill", + "mail-star-line", + "mail-unread-fill", + "mail-unread-line", + "mail-volume-fill", + "mail-volume-line", + "map-2-fill", + "map-2-line", + "map-fill", + "map-line", + "map-pin-2-fill", + "map-pin-2-line", + "map-pin-3-fill", + "map-pin-3-line", + "map-pin-4-fill", + "map-pin-4-line", + "map-pin-5-fill", + "map-pin-5-line", + "map-pin-add-fill", + "map-pin-add-line", + "map-pin-fill", + "map-pin-line", + "map-pin-range-fill", + "map-pin-range-line", + "map-pin-time-fill", + "map-pin-time-line", + "map-pin-user-fill", + "map-pin-user-line", + "mark-pen-fill", + "mark-pen-line", + "markdown-fill", + "markdown-line", + "markup-fill", + "markup-line", + "mastercard-fill", + "mastercard-line", + "mastodon-fill", + "mastodon-line", + "medal-2-fill", + "medal-2-line", + "medal-fill", + "medal-line", + "medicine-bottle-fill", + "medicine-bottle-line", + "medium-fill", + "medium-line", + "men-fill", + "men-line", + "mental-health-fill", + "mental-health-line", + "menu-2-fill", + "menu-2-line", + "menu-3-fill", + "menu-3-line", + "menu-4-fill", + "menu-4-line", + "menu-5-fill", + "menu-5-line", + "menu-add-fill", + "menu-add-line", + "menu-fill", + "menu-fold-fill", + "menu-fold-line", + "menu-line", + "menu-unfold-fill", + "menu-unfold-line", + "merge-cells-horizontal", + "merge-cells-vertical", + "message-2-fill", + "message-2-line", + "message-3-fill", + "message-3-line", + "message-fill", + "message-line", + "messenger-fill", + "messenger-line", + "meteor-fill", + "meteor-line", + "mic-2-fill", + "mic-2-line", + "mic-fill", + "mic-line", + "mic-off-fill", + "mic-off-line", + "mickey-fill", + "mickey-line", + "microscope-fill", + "microscope-line", + "microsoft-fill", + "microsoft-line", + "mind-map", + "mini-program-fill", + "mini-program-line", + "mist-fill", + "mist-line", + "money-cny-box-fill", + "money-cny-box-line", + "money-cny-circle-fill", + "money-cny-circle-line", + "money-dollar-box-fill", + "money-dollar-box-line", + "money-dollar-circle-fill", + "money-dollar-circle-line", + "money-euro-box-fill", + "money-euro-box-line", + "money-euro-circle-fill", + "money-euro-circle-line", + "money-pound-box-fill", + "money-pound-box-line", + "money-pound-circle-fill", + "money-pound-circle-line", + "moon-clear-fill", + "moon-clear-line", + "moon-cloudy-fill", + "moon-cloudy-line", + "moon-fill", + "moon-foggy-fill", + "moon-foggy-line", + "moon-line", + "more-2-fill", + "more-2-line", + "more-fill", + "more-line", + "motorbike-fill", + "motorbike-line", + "mouse-fill", + "mouse-line", + "movie-2-fill", + "movie-2-line", + "movie-fill", + "movie-line", + "music-2-fill", + "music-2-line", + "music-fill", + "music-line", + "mv-fill", + "mv-line", + "navigation-fill", + "navigation-line", + "netease-cloud-music-fill", + "netease-cloud-music-line", + "netflix-fill", + "netflix-line", + "newspaper-fill", + "newspaper-line", + "node-tree", + "notification-2-fill", + "notification-2-line", + "notification-3-fill", + "notification-3-line", + "notification-4-fill", + "notification-4-line", + "notification-badge-fill", + "notification-badge-line", + "notification-fill", + "notification-line", + "notification-off-fill", + "notification-off-line", + "npmjs-fill", + "npmjs-line", + "number-0", + "number-1", + "number-2", + "number-3", + "number-4", + "number-5", + "number-6", + "number-7", + "number-8", + "number-9", + "numbers-fill", + "numbers-line", + "nurse-fill", + "nurse-line", + "oil-fill", + "oil-line", + "omega", + "open-arm-fill", + "open-arm-line", + "open-source-fill", + "open-source-line", + "opera-fill", + "opera-line", + "order-play-fill", + "order-play-line", + "organization-chart", + "outlet-2-fill", + "outlet-2-line", + "outlet-fill", + "outlet-line", + "page-separator", + "pages-fill", + "pages-line", + "paint-brush-fill", + "paint-brush-line", + "paint-fill", + "paint-line", + "palette-fill", + "palette-line", + "pantone-fill", + "pantone-line", + "paragraph", + "parent-fill", + "parent-line", + "parentheses-fill", + "parentheses-line", + "parking-box-fill", + "parking-box-line", + "parking-fill", + "parking-line", + "passport-fill", + "passport-line", + "patreon-fill", + "patreon-line", + "pause-circle-fill", + "pause-circle-line", + "pause-fill", + "pause-line", + "pause-mini-fill", + "pause-mini-line", + "paypal-fill", + "paypal-line", + "pen-nib-fill", + "pen-nib-line", + "pencil-fill", + "pencil-line", + "pencil-ruler-2-fill", + "pencil-ruler-2-line", + "pencil-ruler-fill", + "pencil-ruler-line", + "percent-fill", + "percent-line", + "phone-camera-fill", + "phone-camera-line", + "phone-fill", + "phone-find-fill", + "phone-find-line", + "phone-line", + "phone-lock-fill", + "phone-lock-line", + "picture-in-picture-2-fill", + "picture-in-picture-2-line", + "picture-in-picture-exit-fill", + "picture-in-picture-exit-line", + "picture-in-picture-fill", + "picture-in-picture-line", + "pie-chart-2-fill", + "pie-chart-2-line", + "pie-chart-box-fill", + "pie-chart-box-line", + "pie-chart-fill", + "pie-chart-line", + "pin-distance-fill", + "pin-distance-line", + "ping-pong-fill", + "ping-pong-line", + "pinterest-fill", + "pinterest-line", + "pinyin-input", + "pixelfed-fill", + "pixelfed-line", + "plane-fill", + "plane-line", + "plant-fill", + "plant-line", + "play-circle-fill", + "play-circle-line", + "play-fill", + "play-line", + "play-list-2-fill", + "play-list-2-line", + "play-list-add-fill", + "play-list-add-line", + "play-list-fill", + "play-list-line", + "play-mini-fill", + "play-mini-line", + "playstation-fill", + "playstation-line", + "plug-2-fill", + "plug-2-line", + "plug-fill", + "plug-line", + "polaroid-2-fill", + "polaroid-2-line", + "polaroid-fill", + "polaroid-line", + "police-car-fill", + "police-car-line", + "price-tag-2-fill", + "price-tag-2-line", + "price-tag-3-fill", + "price-tag-3-line", + "price-tag-fill", + "price-tag-line", + "printer-cloud-fill", + "printer-cloud-line", + "printer-fill", + "printer-line", + "product-hunt-fill", + "product-hunt-line", + "profile-fill", + "profile-line", + "projector-2-fill", + "projector-2-line", + "projector-fill", + "projector-line", + "psychotherapy-fill", + "psychotherapy-line", + "pulse-fill", + "pulse-line", + "pushpin-2-fill", + "pushpin-2-line", + "pushpin-fill", + "pushpin-line", + "qq-fill", + "qq-line", + "qr-code-fill", + "qr-code-line", + "qr-scan-2-fill", + "qr-scan-2-line", + "qr-scan-fill", + "qr-scan-line", + "question-answer-fill", + "question-answer-line", + "question-fill", + "question-line", + "question-mark", + "questionnaire-fill", + "questionnaire-line", + "quill-pen-fill", + "quill-pen-line", + "radar-fill", + "radar-line", + "radio-2-fill", + "radio-2-line", + "radio-button-fill", + "radio-button-line", + "radio-fill", + "radio-line", + "rainbow-fill", + "rainbow-line", + "rainy-fill", + "rainy-line", + "reactjs-fill", + "reactjs-line", + "record-circle-fill", + "record-circle-line", + "record-mail-fill", + "record-mail-line", + "recycle-fill", + "recycle-line", + "red-packet-fill", + "red-packet-line", + "reddit-fill", + "reddit-line", + "refresh-fill", + "refresh-line", + "refund-2-fill", + "refund-2-line", + "refund-fill", + "refund-line", + "registered-fill", + "registered-line", + "remixicon-fill", + "remixicon-line", + "remote-control-2-fill", + "remote-control-2-line", + "remote-control-fill", + "remote-control-line", + "repeat-2-fill", + "repeat-2-line", + "repeat-fill", + "repeat-line", + "repeat-one-fill", + "repeat-one-line", + "reply-all-fill", + "reply-all-line", + "reply-fill", + "reply-line", + "reserved-fill", + "reserved-line", + "rest-time-fill", + "rest-time-line", + "restart-fill", + "restart-line", + "restaurant-2-fill", + "restaurant-2-line", + "restaurant-fill", + "restaurant-line", + "rewind-fill", + "rewind-line", + "rewind-mini-fill", + "rewind-mini-line", + "rhythm-fill", + "rhythm-line", + "riding-fill", + "riding-line", + "road-map-fill", + "road-map-line", + "roadster-fill", + "roadster-line", + "robot-fill", + "robot-line", + "rocket-2-fill", + "rocket-2-line", + "rocket-fill", + "rocket-line", + "rotate-lock-fill", + "rotate-lock-line", + "rounded-corner", + "route-fill", + "route-line", + "router-fill", + "router-line", + "rss-fill", + "rss-line", + "ruler-2-fill", + "ruler-2-line", + "ruler-fill", + "ruler-line", + "run-fill", + "run-line", + "safari-fill", + "safari-line", + "safe-2-fill", + "safe-2-line", + "safe-fill", + "safe-line", + "sailboat-fill", + "sailboat-line", + "save-2-fill", + "save-2-line", + "save-3-fill", + "save-3-line", + "save-fill", + "save-line", + "scales-2-fill", + "scales-2-line", + "scales-3-fill", + "scales-3-line", + "scales-fill", + "scales-line", + "scan-2-fill", + "scan-2-line", + "scan-fill", + "scan-line", + "scissors-2-fill", + "scissors-2-line", + "scissors-cut-fill", + "scissors-cut-line", + "scissors-fill", + "scissors-line", + "screenshot-2-fill", + "screenshot-2-line", + "screenshot-fill", + "screenshot-line", + "sd-card-fill", + "sd-card-line", + "sd-card-mini-fill", + "sd-card-mini-line", + "search-2-fill", + "search-2-line", + "search-eye-fill", + "search-eye-line", + "search-fill", + "search-line", + "secure-payment-fill", + "secure-payment-line", + "seedling-fill", + "seedling-line", + "send-backward", + "send-plane-2-fill", + "send-plane-2-line", + "send-plane-fill", + "send-plane-line", + "send-to-back", + "sensor-fill", + "sensor-line", + "separator", + "server-fill", + "server-line", + "service-fill", + "service-line", + "settings-2-fill", + "settings-2-line", + "settings-3-fill", + "settings-3-line", + "settings-4-fill", + "settings-4-line", + "settings-5-fill", + "settings-5-line", + "settings-6-fill", + "settings-6-line", + "settings-fill", + "settings-line", + "shape-2-fill", + "shape-2-line", + "shape-fill", + "shape-line", + "share-box-fill", + "share-box-line", + "share-circle-fill", + "share-circle-line", + "share-fill", + "share-forward-2-fill", + "share-forward-2-line", + "share-forward-box-fill", + "share-forward-box-line", + "share-forward-fill", + "share-forward-line", + "share-line", + "shield-check-fill", + "shield-check-line", + "shield-cross-fill", + "shield-cross-line", + "shield-fill", + "shield-flash-fill", + "shield-flash-line", + "shield-keyhole-fill", + "shield-keyhole-line", + "shield-line", + "shield-star-fill", + "shield-star-line", + "shield-user-fill", + "shield-user-line", + "ship-2-fill", + "ship-2-line", + "ship-fill", + "ship-line", + "shirt-fill", + "shirt-line", + "shopping-bag-2-fill", + "shopping-bag-2-line", + "shopping-bag-3-fill", + "shopping-bag-3-line", + "shopping-bag-fill", + "shopping-bag-line", + "shopping-basket-2-fill", + "shopping-basket-2-line", + "shopping-basket-fill", + "shopping-basket-line", + "shopping-cart-2-fill", + "shopping-cart-2-line", + "shopping-cart-fill", + "shopping-cart-line", + "showers-fill", + "showers-line", + "shuffle-fill", + "shuffle-line", + "shut-down-fill", + "shut-down-line", + "side-bar-fill", + "side-bar-line", + "signal-tower-fill", + "signal-tower-line", + "signal-wifi-1-fill", + "signal-wifi-1-line", + "signal-wifi-2-fill", + "signal-wifi-2-line", + "signal-wifi-3-fill", + "signal-wifi-3-line", + "signal-wifi-error-fill", + "signal-wifi-error-line", + "signal-wifi-fill", + "signal-wifi-line", + "signal-wifi-off-fill", + "signal-wifi-off-line", + "sim-card-2-fill", + "sim-card-2-line", + "sim-card-fill", + "sim-card-line", + "single-quotes-l", + "single-quotes-r", + "sip-fill", + "sip-line", + "skip-back-fill", + "skip-back-line", + "skip-back-mini-fill", + "skip-back-mini-line", + "skip-forward-fill", + "skip-forward-line", + "skip-forward-mini-fill", + "skip-forward-mini-line", + "skull-2-fill", + "skull-2-line", + "skull-fill", + "skull-line", + "skype-fill", + "skype-line", + "slack-fill", + "slack-line", + "slice-fill", + "slice-line", + "slideshow-2-fill", + "slideshow-2-line", + "slideshow-3-fill", + "slideshow-3-line", + "slideshow-4-fill", + "slideshow-4-line", + "slideshow-fill", + "slideshow-line", + "smartphone-fill", + "smartphone-line", + "snapchat-fill", + "snapchat-line", + "snowy-fill", + "snowy-line", + "sort-asc", + "sort-desc", + "sound-module-fill", + "sound-module-line", + "soundcloud-fill", + "soundcloud-line", + "space-ship-fill", + "space-ship-line", + "space", + "spam-2-fill", + "spam-2-line", + "spam-3-fill", + "spam-3-line", + "spam-fill", + "spam-line", + "speaker-2-fill", + "speaker-2-line", + "speaker-3-fill", + "speaker-3-line", + "speaker-fill", + "speaker-line", + "spectrum-fill", + "spectrum-line", + "speed-fill", + "speed-line", + "speed-mini-fill", + "speed-mini-line", + "split-cells-horizontal", + "split-cells-vertical", + "spotify-fill", + "spotify-line", + "spy-fill", + "spy-line", + "stack-fill", + "stack-line", + "stack-overflow-fill", + "stack-overflow-line", + "stackshare-fill", + "stackshare-line", + "star-fill", + "star-half-fill", + "star-half-line", + "star-half-s-fill", + "star-half-s-line", + "star-line", + "star-s-fill", + "star-s-line", + "star-smile-fill", + "star-smile-line", + "steam-fill", + "steam-line", + "steering-2-fill", + "steering-2-line", + "steering-fill", + "steering-line", + "stethoscope-fill", + "stethoscope-line", + "sticky-note-2-fill", + "sticky-note-2-line", + "sticky-note-fill", + "sticky-note-line", + "stock-fill", + "stock-line", + "stop-circle-fill", + "stop-circle-line", + "stop-fill", + "stop-line", + "stop-mini-fill", + "stop-mini-line", + "store-2-fill", + "store-2-line", + "store-3-fill", + "store-3-line", + "store-fill", + "store-line", + "strikethrough-2", + "strikethrough", + "subscript-2", + "subscript", + "subtract-fill", + "subtract-line", + "subway-fill", + "subway-line", + "subway-wifi-fill", + "subway-wifi-line", + "suitcase-2-fill", + "suitcase-2-line", + "suitcase-3-fill", + "suitcase-3-line", + "suitcase-fill", + "suitcase-line", + "sun-cloudy-fill", + "sun-cloudy-line", + "sun-fill", + "sun-foggy-fill", + "sun-foggy-line", + "sun-line", + "superscript-2", + "superscript", + "surgical-mask-fill", + "surgical-mask-line", + "surround-sound-fill", + "surround-sound-line", + "survey-fill", + "survey-line", + "swap-box-fill", + "swap-box-line", + "swap-fill", + "swap-line", + "switch-fill", + "switch-line", + "sword-fill", + "sword-line", + "syringe-fill", + "syringe-line", + "t-box-fill", + "t-box-line", + "t-shirt-2-fill", + "t-shirt-2-line", + "t-shirt-air-fill", + "t-shirt-air-line", + "t-shirt-fill", + "t-shirt-line", + "table-2", + "table-alt-fill", + "table-alt-line", + "table-fill", + "table-line", + "tablet-fill", + "tablet-line", + "takeaway-fill", + "takeaway-line", + "taobao-fill", + "taobao-line", + "tape-fill", + "tape-line", + "task-fill", + "task-line", + "taxi-fill", + "taxi-line", + "taxi-wifi-fill", + "taxi-wifi-line", + "team-fill", + "team-line", + "telegram-fill", + "telegram-line", + "temp-cold-fill", + "temp-cold-line", + "temp-hot-fill", + "temp-hot-line", + "terminal-box-fill", + "terminal-box-line", + "terminal-fill", + "terminal-line", + "terminal-window-fill", + "terminal-window-line", + "test-tube-fill", + "test-tube-line", + "text-direction-l", + "text-direction-r", + "text-spacing", + "text-wrap", + "text", + "thermometer-fill", + "thermometer-line", + "thumb-down-fill", + "thumb-down-line", + "thumb-up-fill", + "thumb-up-line", + "thunderstorms-fill", + "thunderstorms-line", + "ticket-2-fill", + "ticket-2-line", + "ticket-fill", + "ticket-line", + "time-fill", + "time-line", + "timer-2-fill", + "timer-2-line", + "timer-fill", + "timer-flash-fill", + "timer-flash-line", + "timer-line", + "todo-fill", + "todo-line", + "toggle-fill", + "toggle-line", + "tools-fill", + "tools-line", + "tornado-fill", + "tornado-line", + "trademark-fill", + "trademark-line", + "traffic-light-fill", + "traffic-light-line", + "train-fill", + "train-line", + "train-wifi-fill", + "train-wifi-line", + "translate-2", + "translate", + "travesti-fill", + "travesti-line", + "treasure-map-fill", + "treasure-map-line", + "trello-fill", + "trello-line", + "trophy-fill", + "trophy-line", + "truck-fill", + "truck-line", + "tumblr-fill", + "tumblr-line", + "tv-2-fill", + "tv-2-line", + "tv-fill", + "tv-line", + "twitch-fill", + "twitch-line", + "twitter-fill", + "twitter-line", + "typhoon-fill", + "typhoon-line", + "u-disk-fill", + "u-disk-line", + "ubuntu-fill", + "ubuntu-line", + "umbrella-fill", + "umbrella-line", + "underline", + "uninstall-fill", + "uninstall-line", + "unsplash-fill", + "unsplash-line", + "upload-2-fill", + "upload-2-line", + "upload-cloud-2-fill", + "upload-cloud-2-line", + "upload-cloud-fill", + "upload-cloud-line", + "upload-fill", + "upload-line", + "usb-fill", + "usb-line", + "user-2-fill", + "user-2-line", + "user-3-fill", + "user-3-line", + "user-4-fill", + "user-4-line", + "user-5-fill", + "user-5-line", + "user-6-fill", + "user-6-line", + "user-add-fill", + "user-add-line", + "user-fill", + "user-follow-fill", + "user-follow-line", + "user-heart-fill", + "user-heart-line", + "user-line", + "user-location-fill", + "user-location-line", + "user-received-2-fill", + "user-received-2-line", + "user-received-fill", + "user-received-line", + "user-search-fill", + "user-search-line", + "user-settings-fill", + "user-settings-line", + "user-shared-2-fill", + "user-shared-2-line", + "user-shared-fill", + "user-shared-line", + "user-smile-fill", + "user-smile-line", + "user-star-fill", + "user-star-line", + "user-unfollow-fill", + "user-unfollow-line", + "user-voice-fill", + "user-voice-line", + "video-add-fill", + "video-add-line", + "video-chat-fill", + "video-chat-line", + "video-download-fill", + "video-download-line", + "video-fill", + "video-line", + "video-upload-fill", + "video-upload-line", + "vidicon-2-fill", + "vidicon-2-line", + "vidicon-fill", + "vidicon-line", + "vimeo-fill", + "vimeo-line", + "vip-crown-2-fill", + "vip-crown-2-line", + "vip-crown-fill", + "vip-crown-line", + "vip-diamond-fill", + "vip-diamond-line", + "vip-fill", + "vip-line", + "virus-fill", + "virus-line", + "visa-fill", + "visa-line", + "voice-recognition-fill", + "voice-recognition-line", + "voiceprint-fill", + "voiceprint-line", + "volume-down-fill", + "volume-down-line", + "volume-mute-fill", + "volume-mute-line", + "volume-off-vibrate-fill", + "volume-off-vibrate-line", + "volume-up-fill", + "volume-up-line", + "volume-vibrate-fill", + "volume-vibrate-line", + "vuejs-fill", + "vuejs-line", + "walk-fill", + "walk-line", + "wallet-2-fill", + "wallet-2-line", + "wallet-3-fill", + "wallet-3-line", + "wallet-fill", + "wallet-line", + "water-flash-fill", + "water-flash-line", + "webcam-fill", + "webcam-line", + "wechat-2-fill", + "wechat-2-line", + "wechat-fill", + "wechat-line", + "wechat-pay-fill", + "wechat-pay-line", + "weibo-fill", + "weibo-line", + "whatsapp-fill", + "whatsapp-line", + "wheelchair-fill", + "wheelchair-line", + "wifi-fill", + "wifi-line", + "wifi-off-fill", + "wifi-off-line", + "window-2-fill", + "window-2-line", + "window-fill", + "window-line", + "windows-fill", + "windows-line", + "windy-fill", + "windy-line", + "wireless-charging-fill", + "wireless-charging-line", + "women-fill", + "women-line", + "wubi-input", + "xbox-fill", + "xbox-line", + "xing-fill", + "xing-line", + "youtube-fill", + "youtube-line", + "zcool-fill", + "zcool-line", + "zhihu-fill", + "zhihu-line", + "zoom-in-fill", + "zoom-in-line", + "zoom-out-fill", + "zoom-out-line", + "zzz-fill", + "zzz-line"); +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/OnlineDocApiVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/OnlineDocApiVO.java new file mode 100644 index 00000000..de8e38f9 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/OnlineDocApiVO.java @@ -0,0 +1,49 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; +import java.util.Map; + +/** + * @author jinxia.hou + * @Name OnlineDocApiVO + * @description: 在线文档调用返回结果 + * @date 2023/5/2415:01 + */ +public class OnlineDocApiVO implements Serializable { + private String errorMessage; + private String errorCode; + private Object result; + + public String getErrorMessage() { + return errorMessage; + } + + public void setErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getErrorCode() { + return errorCode; + } + + public void setErrorCode(String errorCode) { + this.errorCode = errorCode; + } + + public Object getResult() { + return result; + } + + public void setResult(Object result) { + this.result = result; + } + + @Override + public String toString() { + return "OnlineDocApiVO{" + + "errorMessage='" + errorMessage + '\'' + + ", errorCode='" + errorCode + '\'' + + ", result=" + result + + '}'; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ParticipantScheduleVo.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ParticipantScheduleVo.java new file mode 100644 index 00000000..f9254a26 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/ParticipantScheduleVo.java @@ -0,0 +1,367 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; +import java.util.Date; + +/** + * @author jinxia.hou + * @Name ParticipantScheduleVo + * @description: + * @date 2022/2/1816:05 + */ +public class ParticipantScheduleVo implements Serializable { + + private static final long serialVersionUID = -6592562315091674625L; + /** + * ID + */ + protected String id; + /** + * 业务关联id。此id用在回调时 + */ + protected String bizId; + + /** + * 标题 + */ + protected String title; + + /** + * 描述 + */ + protected String remark; + + /** + * 任务连接 + */ + protected String taskUrl; + + /** + * 类型 + */ + protected String type; + + /** + * 任务打开方式 + */ + protected String openType; + + /** + * 所属人 + */ + protected String owner; + + /** + * 所属人 + */ + protected String ownerName; + + /** + * 参与者 + */ + protected String participantNames; + + /** + * 开始日期 + */ + protected java.util.Date startTime; + + /** + * 结束日期 + */ + protected java.util.Date endTime; + + /** + * 实际开始日期 + */ + protected java.util.Date actualStartTime; + + /** + * 完成时间 + */ + protected java.util.Date completeTime; + + /** + * 进度 + */ + protected Integer rateProgress; + + /** + * 提交人 + */ + protected String submitter; + + /** + * 提交人 + */ + protected String submitterName; + + /** + * isLock + */ + protected String isLock; + + /** + * 创建时间 + */ + protected java.util.Date createTime; + + /** + * 创建人 + */ + protected String createBy; + + /** + * 更新时间 + */ + protected java.util.Date updateTime; + + /** + * 更新人 + */ + protected String updateBy; + + /** + * 删除标记 + */ + protected String deleteFlag; + + + /** + * 日程ID + */ + protected String scheduleId; + + /** + * 参与者名字 + */ + protected String participantorName; + + /** + * 参与者 + */ + protected String participantor; + + + /** + * ilka提交注释 + */ + protected String submitComment; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getBizId() { + return bizId; + } + + public void setBizId(String bizId) { + this.bizId = bizId; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public String getRemark() { + return remark; + } + + public void setRemark(String remark) { + this.remark = remark; + } + + public String getTaskUrl() { + return taskUrl; + } + + public void setTaskUrl(String taskUrl) { + this.taskUrl = taskUrl; + } + + public String getType() { + return type; + } + + public void setType(String type) { + this.type = type; + } + + public String getOpenType() { + return openType; + } + + public void setOpenType(String openType) { + this.openType = openType; + } + + public String getOwner() { + return owner; + } + + public void setOwner(String owner) { + this.owner = owner; + } + + public String getOwnerName() { + return ownerName; + } + + public void setOwnerName(String ownerName) { + this.ownerName = ownerName; + } + + public String getParticipantNames() { + return participantNames; + } + + public void setParticipantNames(String participantNames) { + this.participantNames = participantNames; + } + + public Date getStartTime() { + return startTime; + } + + public void setStartTime(Date startTime) { + this.startTime = startTime; + } + + public Date getEndTime() { + return endTime; + } + + public void setEndTime(Date endTime) { + this.endTime = endTime; + } + + public Date getActualStartTime() { + return actualStartTime; + } + + public void setActualStartTime(Date actualStartTime) { + this.actualStartTime = actualStartTime; + } + + public Date getCompleteTime() { + return completeTime; + } + + public void setCompleteTime(Date completeTime) { + this.completeTime = completeTime; + } + + public Integer getRateProgress() { + return rateProgress; + } + + public void setRateProgress(Integer rateProgress) { + this.rateProgress = rateProgress; + } + + public String getSubmitter() { + return submitter; + } + + public void setSubmitter(String submitter) { + this.submitter = submitter; + } + + public String getSubmitterName() { + return submitterName; + } + + public void setSubmitterName(String submitterName) { + this.submitterName = submitterName; + } + + public String getIsLock() { + return isLock; + } + + public void setIsLock(String isLock) { + this.isLock = isLock; + } + + public Date getCreateTime() { + return createTime; + } + + public void setCreateTime(Date createTime) { + this.createTime = createTime; + } + + public String getCreateBy() { + return createBy; + } + + public void setCreateBy(String createBy) { + this.createBy = createBy; + } + + public Date getUpdateTime() { + return updateTime; + } + + public void setUpdateTime(Date updateTime) { + this.updateTime = updateTime; + } + + public String getUpdateBy() { + return updateBy; + } + + public void setUpdateBy(String updateBy) { + this.updateBy = updateBy; + } + + public String getDeleteFlag() { + return deleteFlag; + } + + public void setDeleteFlag(String deleteFlag) { + this.deleteFlag = deleteFlag; + } + + public String getScheduleId() { + return scheduleId; + } + + public void setScheduleId(String scheduleId) { + this.scheduleId = scheduleId; + } + + public String getParticipantorName() { + return participantorName; + } + + public void setParticipantorName(String participantorName) { + this.participantorName = participantorName; + } + + public String getParticipantor() { + return participantor; + } + + public void setParticipantor(String participantor) { + this.participantor = participantor; + } + + public String getSubmitComment() { + return submitComment; + } + + public void setSubmitComment(String submitComment) { + this.submitComment = submitComment; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogMetadataCacheVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogMetadataCacheVO.java new file mode 100644 index 00000000..60ca2e02 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogMetadataCacheVO.java @@ -0,0 +1,79 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name SysAuditLogMetadataCacheVO + * @description: + * @date 2022/2/2315:40 + */ +public class SysAuditLogMetadataCacheVO implements Serializable { + + private static final long serialVersionUID = -1961365464945900536L; + /** + * 元数据主键编号 + */ + private String id; + + /** + * 判断条件表达式 + */ + private String predicateExpr; + + + /** + * 业务主键获取表达式 + */ + private String bizIdExpr; + + /** + * 记录数据获取表达式 + */ + private String dataExpr; + + /** + * 日志描述模板,支持SPEL表达式 + */ + private String descriptionTpl; + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getPredicateExpr() { + return predicateExpr; + } + + public void setPredicateExpr(String predicateExpr) { + this.predicateExpr = predicateExpr; + } + + public String getBizIdExpr() { + return bizIdExpr; + } + + public void setBizIdExpr(String bizIdExpr) { + this.bizIdExpr = bizIdExpr; + } + + public String getDataExpr() { + return dataExpr; + } + + public void setDataExpr(String dataExpr) { + this.dataExpr = dataExpr; + } + + public String getDescriptionTpl() { + return descriptionTpl; + } + + public void setDescriptionTpl(String descriptionTpl) { + this.descriptionTpl = descriptionTpl; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogTreeVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogTreeVO.java new file mode 100644 index 00000000..40104781 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysAuditLogTreeVO.java @@ -0,0 +1,88 @@ +package com.dstz.sys.rest.model.vo; + +import com.dstz.base.api.model.Tree; + +import java.io.Serializable; +import java.util.List; + +/** + * @author niuniu + * @description: 日志模块左侧树的VO类 + * @date 2022/2/2315:40 + */ +public class SysAuditLogTreeVO implements Tree, Serializable { + + /** + * 唯一标识 + */ + private String id; + /** + * 仅作list转树筛选用, 无其他意义 + */ + private String code; + /** + * 展示名称 + */ + private String name; + + /** + * 上级id + */ + private String parentId; + + /** + * 子类集合 + */ + private List children; + + + @Override + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getParentId() { + return parentId; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + @Override + public List getChildren() { + return children; + } + + @Override + public void setChildren(List children) { + this.children = children; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public SysAuditLogTreeVO(String id, String name, String parentId) { + this.id = id; + this.name = name; + this.parentId = parentId; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysDataDictVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysDataDictVO.java new file mode 100644 index 00000000..b3525678 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysDataDictVO.java @@ -0,0 +1,174 @@ +package com.dstz.sys.rest.model.vo; + +import com.dstz.base.api.model.Tree; + +import java.io.Serializable; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysDataDictVO + * @description: 数据字典 + * @date 2022/3/2411:54 + */ +public class SysDataDictVO implements Tree, Serializable { + + private static final long serialVersionUID = -1706296576459495977L; + /** + * ID + */ + private String id; + + /** + * 上级id + */ + private String parentId; + + /** + * 编码 + */ + private String code; + + /** + * name + */ + private String name; + + /** + * 字典key + */ + private String dictKey; + + /** + * 分组字典编码 + */ + private String typeCode; + + /** + * 排序 + */ + private Integer sn; + + /** + * dict/node字典项 + */ + private String dictType; + + /** + * 扩展字段1 + */ + private String extend1; + + /** + * 扩展字段2 + */ + private String extend2; + + /** + * 是否系统内置 + */ + private Integer isSystem; + + private List children; + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getDictKey() { + return dictKey; + } + + public void setDictKey(String dictKey) { + this.dictKey = dictKey; + } + + public String getTypeCode() { + return typeCode; + } + + public void setTypeCode(String typeCode) { + this.typeCode = typeCode; + } + + public Integer getSn() { + return sn; + } + + public void setSn(Integer sn) { + this.sn = sn; + } + + public String getDictType() { + return dictType; + } + + public void setDictType(String dictType) { + this.dictType = dictType; + } + + public String getExtend1() { + return extend1; + } + + public void setExtend1(String extend1) { + this.extend1 = extend1; + } + + public String getExtend2() { + return extend2; + } + + public void setExtend2(String extend2) { + this.extend2 = extend2; + } + + public Integer getIsSystem() { + return isSystem; + } + + public void setIsSystem(Integer isSystem) { + this.isSystem = isSystem; + } + + public void setId(String id) { + this.id = id; + } + + public void setParentId(String parentId) { + this.parentId = parentId; + } + + + @Override + public String getId() { + return id; + } + + @Override + public String getParentId() { + return parentId; + } + + @Override + public List getChildren() { + return children; + } + + @Override + public void setChildren(List list) { + this.children = list; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysPropertiesVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysPropertiesVO.java new file mode 100644 index 00000000..e3c311ae --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysPropertiesVO.java @@ -0,0 +1,108 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; +import java.util.ArrayList; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysPropertiesVo + * @description: + * @date 2022/2/1611:09 + */ +public class SysPropertiesVO implements Serializable { + + private static final long serialVersionUID = -6671077622293070668L; + private String id; + + private String code; + + private String name; + + private String value; + + private String desc; + + private String encrypt; + + private String environment; + + private String group; + + /** + * 分类使用逗号进行分割。 + */ + protected List categorys = new ArrayList(); + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getValue() { + return value; + } + + public void setValue(String value) { + this.value = value; + } + + public String getDesc() { + return desc; + } + + public void setDesc(String desc) { + this.desc = desc; + } + + public String getEncrypt() { + return encrypt; + } + + public void setEncrypt(String encrypt) { + this.encrypt = encrypt; + } + + public String getEnvironment() { + return environment; + } + + public void setEnvironment(String environment) { + this.environment = environment; + } + + public String getGroup() { + return group; + } + + public void setGroup(String group) { + this.group = group; + } + + public List getCategorys() { + return categorys; + } + + public void setCategorys(List categorys) { + this.categorys = categorys; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysSerialNoVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysSerialNoVO.java new file mode 100644 index 00000000..5b0e766a --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysSerialNoVO.java @@ -0,0 +1,150 @@ +package com.dstz.sys.rest.model.vo; + +import java.io.Serializable; + +/** + * @author jinxia.hou + * @Name SysSerialNoDto + * @description: 流水号Vo + * @date 2022/2/1616:41 + */ +public class SysSerialNoVO implements Serializable { + + + private static final long serialVersionUID = -174309595049453931L; + private String id; + + + private String name; + + + private String code; + + + private String rule; + + + private Integer type; + + + private Integer noLength; + + + private Integer stepLength; + + + private String nowDate; + + + private Integer initialValue; + + + private Integer nowValue; + + /** + * 新的流水号。 + */ + protected Integer newNowValue = 0; + + /** + * 预览流水号。 + */ + protected String overViewTenValue = ""; + + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } + + public String getCode() { + return code; + } + + public void setCode(String code) { + this.code = code; + } + + public String getRule() { + return rule; + } + + public void setRule(String rule) { + this.rule = rule; + } + + public Integer getType() { + return type; + } + + public void setType(Integer type) { + this.type = type; + } + + public Integer getNoLength() { + return noLength; + } + + public void setNoLength(Integer noLength) { + this.noLength = noLength; + } + + public Integer getStepLength() { + return stepLength; + } + + public void setStepLength(Integer stepLength) { + this.stepLength = stepLength; + } + + public String getNowDate() { + return nowDate; + } + + public void setNowDate(String nowDate) { + this.nowDate = nowDate; + } + + public Integer getInitialValue() { + return initialValue; + } + + public void setInitialValue(Integer initialValue) { + this.initialValue = initialValue; + } + + public Integer getNowValue() { + return nowValue; + } + + public void setNowValue(Integer nowValue) { + this.nowValue = nowValue; + } + + public Integer getNewNowValue() { + return newNowValue; + } + + public void setNewNowValue(Integer newNowValue) { + this.newNowValue = newNowValue; + } + + public String getOverViewTenValue() { + return overViewTenValue; + } + + public void setOverViewTenValue(String overViewTenValue) { + this.overViewTenValue = overViewTenValue; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysWorkHandoverPersonalVO.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysWorkHandoverPersonalVO.java new file mode 100644 index 00000000..3e859d87 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/rest/model/vo/SysWorkHandoverPersonalVO.java @@ -0,0 +1,82 @@ +package com.dstz.sys.rest.model.vo; + +import com.dstz.org.api.model.IUser; + +import java.io.Serializable; +import java.util.List; + +/** + * @author jinxia.hou + * @Name SysWorkHandoverPersonalVO + * @description: TODO + * @date 2022/2/2210:54 + */ +public class SysWorkHandoverPersonalVO implements Serializable { + private static final long serialVersionUID = -128902638543933299L; + private String userId; + + private String userAccount; + + private String userFullName; + + private List handoverUsers; + + private List receiveUsers; + + + /** + * new instance + * + * @param user user + * @return SysWorkHandoverPersonalVO + */ + public static SysWorkHandoverPersonalVO newInstance(IUser user) { + SysWorkHandoverPersonalVO o = new SysWorkHandoverPersonalVO(); + if (user != null) { + o.userId = user.getUserId(); + o.userAccount = user.getUsername(); + o.userFullName = user.getFullName(); + } + return o; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getUserAccount() { + return userAccount; + } + + public void setUserAccount(String userAccount) { + this.userAccount = userAccount; + } + + public String getUserFullName() { + return userFullName; + } + + public void setUserFullName(String userFullName) { + this.userFullName = userFullName; + } + + public List getHandoverUsers() { + return handoverUsers; + } + + public void setHandoverUsers(List handoverUsers) { + this.handoverUsers = handoverUsers; + } + + public List getReceiveUsers() { + return receiveUsers; + } + + public void setReceiveUsers(List receiveUsers) { + this.receiveUsers = receiveUsers; + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbDictValueMapLoader.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbDictValueMapLoader.java new file mode 100644 index 00000000..007881cd --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbDictValueMapLoader.java @@ -0,0 +1,49 @@ +package com.dstz.sys.valueMapLoader; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.sys.core.manager.SysDataDictManager; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author jinxia.hou + * @Name AbDictValueMapLoader + * @description: 数据字典值加载器 + * @date 2022/5/2316:07 + */ +@Component("abDictValueMapLoader") +public class AbDictValueMapLoader implements AbValueMapLoader { + + private final SysDataDictManager sysDataDictManager; + + public AbDictValueMapLoader(SysDataDictManager sysDataDictManager) { + this.sysDataDictManager = sysDataDictManager; + AbValueMapLoaderProvider.register(AbValueMapType.DICT, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + final String dictKey = abValueMap.fixValue(); + if (CollUtil.isEmpty(mapKeys)) { + return Collections.emptyMap(); + } + List sysDataDictList = sysDataDictManager.selectByWrapper(Wrappers.lambdaQuery(SysDataDict.class) + .eq(SysDataDict::getTypeCode, SysDataDict.SYSTEM_DEFAULT_TYPE) + .eq(SysDataDict::getDictKey, dictKey) + .in(SysDataDict::getCode, mapKeys) + .eq(SysDataDict::getDictType, SysDataDict.TYPE_NODE)); + return sysDataDictList.stream().collect(Collectors.toMap(SysDataDict::getCode, Function.identity())); + } +} diff --git a/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbTypeValueMapLoader.java b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbTypeValueMapLoader.java new file mode 100644 index 00000000..2ca7ede9 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/java/com/dstz/sys/valueMapLoader/AbTypeValueMapLoader.java @@ -0,0 +1,48 @@ +package com.dstz.sys.valueMapLoader; + +import cn.hutool.core.collection.CollUtil; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.dstz.base.common.valuemap.AbValueMap; +import com.dstz.base.common.valuemap.AbValueMapLoader; +import com.dstz.base.common.valuemap.AbValueMapLoaderProvider; +import com.dstz.base.common.valuemap.AbValueMapType; +import com.dstz.sys.core.entity.SysDataDict; +import com.dstz.sys.core.manager.SysDataDictManager; +import org.springframework.stereotype.Component; + +import java.util.Collection; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.function.Function; +import java.util.stream.Collectors; + +/** + * @author jinxia.hou + * @Name AbTypeValueMapLoader + * @description: 分类加载器 + * @date 2022/5/2316:07 + */ +@Component("abTypeValueMapLoader") +public class AbTypeValueMapLoader implements AbValueMapLoader { + + private final SysDataDictManager sysDataDictManager; + + public AbTypeValueMapLoader(SysDataDictManager sysDataDictManager) { + this.sysDataDictManager = sysDataDictManager; + AbValueMapLoaderProvider.register(AbValueMapType.TYPE, this); + } + + @Override + public Map loading(AbValueMap abValueMap, Collection mapKeys) { + final String typeCode = abValueMap.fixValue(); + if (CollUtil.isEmpty(mapKeys)) { + return Collections.emptyMap(); + } + List sysDataDictList = sysDataDictManager.selectByWrapper(Wrappers.lambdaQuery(SysDataDict.class) + .eq(SysDataDict::getDictKey, typeCode) + .in(SysDataDict::getCode, mapKeys) + ); + return sysDataDictList.stream().collect(Collectors.toMap(SysDataDict::getCode, Function.identity())); + } +} diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/HolidayConfMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/HolidayConfMapper.xml new file mode 100644 index 00000000..69d2f053 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/HolidayConfMapper.xml @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMapper.xml new file mode 100644 index 00000000..55e4a29d --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMapper.xml @@ -0,0 +1,64 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMetadataMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMetadataMapper.xml new file mode 100644 index 00000000..008acbe8 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuditLogMetadataMapper.xml @@ -0,0 +1,66 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE sys_audit_log_metadata SET + enabled_=#{model.enabled,jdbcType=NUMERIC}, + update_by_=#{model.updateBy,jdbcType=VARCHAR}, + update_time_=#{model.updateTime,jdbcType=TIMESTAMP} + WHERE + id_ IN + + #{id,jdbcType=VARCHAR} + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuthorizationMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuthorizationMapper.xml new file mode 100644 index 00000000..4fdc8084 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysAuthorizationMapper.xml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + DELETE + FROM sys_authorization + WHERE rights_target_ = #{rightsTarget} + and rights_object_ = #{rightsObject} + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysConfigurationMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysConfigurationMapper.xml new file mode 100644 index 00000000..98f830f7 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysConfigurationMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.xml new file mode 100644 index 00000000..8f136635 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDailyPhrasesMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDataDictMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDataDictMapper.xml new file mode 100644 index 00000000..1ea8dd73 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysDataDictMapper.xml @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysLogErrMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysLogErrMapper.xml new file mode 100644 index 00000000..22354895 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysLogErrMapper.xml @@ -0,0 +1,19 @@ + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysPropertiesMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysPropertiesMapper.xml new file mode 100644 index 00000000..3a6b87ee --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysPropertiesMapper.xml @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoConfigMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoConfigMapper.xml new file mode 100644 index 00000000..84c725d2 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoConfigMapper.xml @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoMapper.xml new file mode 100644 index 00000000..1e007b00 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoMapper.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE sys_serialno + SET now_date_=#{nowDate,jdbcType=VARCHAR}, + now_value_=#{newNowValue,jdbcType=NUMERIC} + WHERE code_ = #{code,jdbcType=VARCHAR} + and now_value_ = #{nowValue,jdbcType=NUMERIC} + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoReviveMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoReviveMapper.xml new file mode 100644 index 00000000..5c320174 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysSerialNoReviveMapper.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkHandoverMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkHandoverMapper.xml new file mode 100644 index 00000000..e267616e --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkHandoverMapper.xml @@ -0,0 +1,56 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + DELETE FROM sys_work_handover WHERE id_ IN + #{id,jdbcType=VARCHAR} + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchLayoutMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchLayoutMapper.xml new file mode 100644 index 00000000..28c3b67a --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchLayoutMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + DELETE + FROM sys_workbench_layout + WHERE user_id_ = #{userId} + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchPanelMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchPanelMapper.xml new file mode 100644 index 00000000..600171b9 --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/SysWorkbenchPanelMapper.xml @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/WorkCalendarMapper.xml b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/WorkCalendarMapper.xml new file mode 100644 index 00000000..c09c47cc --- /dev/null +++ b/ab-sys/ab-sys-core/src/main/resources/com/dstz/sys/core/mapper/WorkCalendarMapper.xml @@ -0,0 +1,84 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + UPDATE c_work_calendar + SET is_work_day_ = #{isWorkDay,jdbcType=VARCHAR}, + type_=CONCAT(type, #{type,jdbcType=VARCHAR}) + WHERE day_ BETWEEN #{startDay,jdbcType=DATE} and #{endDay,jdbcType=DATE} + + + diff --git a/ab-sys/ab-sys-core/src/main/resources/ip2region/ip2region.xdb b/ab-sys/ab-sys-core/src/main/resources/ip2region/ip2region.xdb new file mode 100644 index 00000000..c78b7928 Binary files /dev/null and b/ab-sys/ab-sys-core/src/main/resources/ip2region/ip2region.xdb differ diff --git a/ab-sys/pom.xml b/ab-sys/pom.xml new file mode 100644 index 00000000..338c2caf --- /dev/null +++ b/ab-sys/pom.xml @@ -0,0 +1,19 @@ + + + + agile-bpm + com.dstz + 2.5.0 + + 4.0.0 + + ab-sys + pom + + + ab-sys-api + ab-sys-core + + \ No newline at end of file diff --git a/doc/change.sql b/doc/change.sql new file mode 100644 index 00000000..a59b2b93 --- /dev/null +++ b/doc/change.sql @@ -0,0 +1,2 @@ +## 当前版本 2.5.0 的 SQL 变更 +## 要保证有分号结尾,然后别报错, Create 语句不用带 Drop SQL diff --git a/doc/readme.md b/doc/readme.md new file mode 100644 index 00000000..393dcffc --- /dev/null +++ b/doc/readme.md @@ -0,0 +1,21 @@ + + + +- 第一次建议执行 full SQL 如 `sql/mysql/full/agilebpm_full.sql` + +- upgrade 为每个版本升级的SQL,请根据自己当下版本,执行至 升级后的版本 + +- changeSQL 是每个版本内 更新的SQL,用于开发环境,最终会整理成 upgradeSQL 。 + + + + +我们 s2 是最新的初始化SQL + +如:我新增了个内置字典,就加个create语句到changeSQL,修改了就加个update。 +如有批量数据修复的,就写相关修复SQL进去。 + +有的人本地开发,你加了个字段,他那里就要报错。所以要有 changeSQL 他们发现报错了,就会到changeSQL找 + + + \ No newline at end of file diff --git a/doc/sql/mysql/README.md b/doc/sql/mysql/README.md new file mode 100644 index 00000000..1ca77966 --- /dev/null +++ b/doc/sql/mysql/README.md @@ -0,0 +1 @@ +请暂时使用 full 目录下的SQL \ No newline at end of file diff --git a/doc/sql/mysql/full/agilebpm_full.sql b/doc/sql/mysql/full/agilebpm_full.sql new file mode 100644 index 00000000..6ccacfb1 --- /dev/null +++ b/doc/sql/mysql/full/agilebpm_full.sql @@ -0,0 +1,2034 @@ +/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; +/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; +/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; +/*!50503 SET NAMES utf8mb4 */; +/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; +/*!40103 SET TIME_ZONE='+00:00' */; +/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; +/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; +/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; +/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; + +-- +-- Table structure for table `ab_message_template` +-- + +DROP TABLE IF EXISTS `ab_message_template`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ab_message_template` ( + `id_` varchar(64) NOT NULL COMMENT '模板id', + `code_` varchar(60) DEFAULT NULL COMMENT '模板编码', + `type_` tinyint DEFAULT NULL COMMENT '模板类型', + `name_` varchar(50) DEFAULT NULL COMMENT '模板名称', + `desc_` varchar(500) DEFAULT NULL COMMENT '模板描述', + `html_template_` longtext COMMENT 'html模板配置', + `card_template_` varchar(1500) DEFAULT NULL COMMENT '卡片模板配置', + `app_template_` varchar(1500) DEFAULT NULL COMMENT '应用模板配置', + `template_param_` varchar(800) DEFAULT NULL COMMENT '模板参数', + `enabled_` tinyint DEFAULT NULL COMMENT '是否启用', + `is_delete_` tinyint DEFAULT NULL COMMENT '是否删除', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ab_message_template` +-- + +LOCK TABLES `ab_message_template` WRITE; +/*!40000 ALTER TABLE `ab_message_template` DISABLE KEYS */; +INSERT INTO `ab_message_template` VALUES ('1602918114232172642','copy_template',NULL,'抄送模板','系统默认抄送模板','

​您好,请有新的流程抄送:${title}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\",\"htmlTemplate\":\"undefined${subject}${subject}\"}','','[{\"key\":\"title\",\"value\":\"${bpmInstance.title}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172647','node_message_template',NULL,'流程节点通知消息模板','流程节点通知消息模板','

流程消息:${title}

','{\"cardTitle\":\"代办任务:${title} !\",\"cardUrl\":\"${appUrl}/bpm/bpmTaskComplate?id=${taskId}\",\"cardContent\":\"代办任务:${title} !\",\"htmlTemplate\":\"undefined${subject}${subject}${subject}\"}','','[{\"key\":\"title\",\"value\":\"${bpmInstance.title}\"},{\"key\":\"appUrl\",\"value\":\"http://osscqkead4.a5.tongzhouyun.com/#\"},{\"key\":\"taskId\",\"value\":\"${bpmTask.id}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2023-05-19 17:12:28','系统管理员','1602918114232172634'),('1602918114232172653','reminder_template',NULL,'催办消息模板','流程默认催办消息模板','

​您好,请您尽快处理任务:${title}

','{\"cardTitle\":\"催办任务:${title} !\",\"cardUrl\":\"${appUrl}/bpm/bpmTaskComplate?id=${taskId}\",\"cardContent\":\"催办任务:${title} !\"}','','[{\"key\":\"title\",\"value\":\"${bpmInstance.title} \"},{\"key\":\"appUrl\",\"value\":\"http://osscqkead4.a5.tongzhouyun.com/#\"},{\"key\":\"taskId\",\"value\":\"${bpmTask.id}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2023-05-19 17:19:19','系统管理员','1602918114232172634'),('1602918114232172657','taskTurnMsg',NULL,'任务转办消息','流程任务转办默认消息模板','

您有一条新的转办任务,请及时办理。 转办备注为:${转办备注}。

流程标题为:${流程标题}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"转办备注\",\"value\":\"${opinion}\"},{\"key\":\"流程标题\",\"value\":\"${bpmInstance.title}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172668','news_message_template',NULL,'新闻消息模板','新闻模块的消息模板','

您收到一条${type}通知:${subject} ,望查阅。

发送人:${senderName}

发送时间 :${sendTime}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"title\",\"value\":\"新闻-${title}\"},{\"key\":\"subject\",\"value\":\"${subject}\"},{\"key\":\"senderName\",\"value\":\"${senderName} \"},{\"key\":\"sendTime\",\"value\":\"${sendTime}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172672','wf_carboncopy_template',NULL,'流程传阅消息模板','流程传阅消息默认模板','

${传阅备注}

','{\"cardTitle\":\"传阅消息:${传阅备注}\",\"cardUrl\":\"${appUrl}/bpm/receiveList\",\"cardContent\":\"传阅消息:${传阅备注}\"}','','[{\"key\":\"传阅备注\",\"value\":\"${opinion}\"},{\"key\":\"appUrl\",\"value\":\"http://osscqkead4.a5.tongzhouyun.com/#\"}]',1,NULL,'2022-12-14 14:47:09','1','','2023-05-19 17:43:19','系统管理员','1602918114232172634'),('1602918114232172678','task_recall_template',NULL,'流程任务撤回消息模板','流程任务撤回消息默认模板','

${撤回备注}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"撤回备注\",\"value\":\"${opinion}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172684','task_discuss_template',NULL,'流程任务评论模板','流程任务评论默认消息模板','

${评论内容}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"评论内容\",\"value\":\"${content}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172689','notify_message_template',NULL,'公告消息模板','公告模块的消息模板','

您收到一条${type}通知:${subject} ,望查阅。

发送人:${senderName}

发送时间 :${sendTime}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"title\",\"value\":\"公告-${title}\"},{\"key\":\"subject\",\"value\":\"${subject}\"},{\"key\":\"senderName\",\"value\":\"${senderName}\"},{\"key\":\"sendTime\",\"value\":\"${sendTime}\"}]',1,NULL,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1622541149667930112','process_completed_template',NULL,'流程结束通知发起人','流程结束时通知发起人','

您申请的流程已经审批完结:${title}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"title\",\"value\":\"${bpmInstance.title}\"}]',1,NULL,'2023-02-06 18:22:10','1602918114232172634','1602918114232172544','2023-02-06 18:22:10','系统管理员','1602918114232172634'),('1623983605290708992','forgotPasswordValidation',NULL,'AgileBPM 找回密码验证','AgileBPM 找回密码验证','

验证码:${verifyCode}

','{\"cardTitle\":\"\",\"cardUrl\":\"\",\"cardContent\":\"\"}','','[{\"key\":\"verifyCode\",\"value\":\"${verifyCode}\"}]',1,NULL,'2023-02-10 17:53:58','1','410054569125740545','2023-02-10 17:53:58','管理员','1'),('1672860345298386944','sys_error_msg_template',NULL,'系统异常消息模板','系统异常消息推送模板','

用户:${用户名}(${用户账号}),系统操作异常。

异常信息为:${错误信息}

出现时间:${时间}

ip归属地:${ip归属地}(${ip})

请关注:${接口路径}




','{\"cardTitle\":\"系统异常告警\",\"cardUrl\":\"${appUrl}/sys/logErr/listJson\",\"cardContent\":\"用户:${用户名}(${用户账号}),系统操作异常。\\n异常信息为:${错误信息}\\n出现时间:${时间}\\nip归属地:${ip归属地}(${ip})\\n请关注:${接口路径}\"}','','[{\"key\":\"用户名\",\"value\":\"${fullName}\"},{\"key\":\"用户账号\",\"value\":\"${account}\"},{\"key\":\"错误信息\",\"value\":\"${content}\"},{\"key\":\"时间\",\"value\":\"${createTime}\"},{\"key\":\"ip归属地\",\"value\":\"${ipAddress}\"},{\"key\":\"ip\",\"value\":\"${ip}\"},{\"key\":\"接口路径\",\"value\":\"${path}\"},{\"key\":\"appUrl\",\"value\":\"http://test.a5.tongzhouyun.com/app/#\"}]',1,NULL,'2023-06-25 14:52:41','1602918114232172634','1639155804422721536','2023-06-25 16:09:33','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `ab_message_template` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ACT_EVT_LOG` +-- + +DROP TABLE IF EXISTS `ACT_EVT_LOG`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_EVT_LOG` ( + `LOG_NR_` bigint NOT NULL AUTO_INCREMENT, + `TYPE_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `TASK_ID_` varchar(64) DEFAULT NULL, + `TIME_STAMP_` timestamp(3) NOT NULL, + `USER_ID_` varchar(255) DEFAULT NULL, + `DATA_` longblob, + `LOCK_OWNER_` varchar(255) DEFAULT NULL, + `LOCK_TIME_` timestamp(3) NULL DEFAULT NULL, + `IS_PROCESSED_` tinyint DEFAULT '0', + PRIMARY KEY (`LOG_NR_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_GE_BYTEARRAY` +-- + +DROP TABLE IF EXISTS `ACT_GE_BYTEARRAY`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_GE_BYTEARRAY` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `NAME_` varchar(255) DEFAULT NULL, + `DEPLOYMENT_ID_` varchar(64) DEFAULT NULL, + `BYTES_` longblob, + `GENERATED_` tinyint DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_FK_BYTEARR_DEPL` (`DEPLOYMENT_ID_`), + CONSTRAINT `ACT_FK_BYTEARR_DEPL` FOREIGN KEY (`DEPLOYMENT_ID_`) REFERENCES `ACT_RE_DEPLOYMENT` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_GE_PROPERTY` +-- + +DROP TABLE IF EXISTS `ACT_GE_PROPERTY`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_GE_PROPERTY` ( + `NAME_` varchar(64) NOT NULL, + `VALUE_` varchar(300) DEFAULT NULL, + `REV_` int DEFAULT NULL, + PRIMARY KEY (`NAME_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `ACT_GE_PROPERTY` +-- + +LOCK TABLES `ACT_GE_PROPERTY` WRITE; +/*!40000 ALTER TABLE `ACT_GE_PROPERTY` DISABLE KEYS */; +INSERT INTO `ACT_GE_PROPERTY` VALUES ('cfg.execution-related-entities-count','false',1),('next.dbid','1',1),('schema.history','create(7.1.0-M6)',1),('schema.version','7.1.0-M6',1); +/*!40000 ALTER TABLE `ACT_GE_PROPERTY` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `ACT_PROCDEF_INFO` +-- + +DROP TABLE IF EXISTS `ACT_PROCDEF_INFO`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_PROCDEF_INFO` ( + `ID_` varchar(64) NOT NULL, + `PROC_DEF_ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `INFO_JSON_ID_` varchar(64) DEFAULT NULL, + PRIMARY KEY (`ID_`), + UNIQUE KEY `ACT_UNIQ_INFO_PROCDEF` (`PROC_DEF_ID_`), + KEY `ACT_IDX_INFO_PROCDEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_INFO_JSON_BA` (`INFO_JSON_ID_`), + CONSTRAINT `ACT_FK_INFO_JSON_BA` FOREIGN KEY (`INFO_JSON_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_INFO_PROCDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RE_DEPLOYMENT` +-- + +DROP TABLE IF EXISTS `ACT_RE_DEPLOYMENT`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RE_DEPLOYMENT` ( + `ID_` varchar(64) NOT NULL, + `NAME_` varchar(255) DEFAULT NULL, + `CATEGORY_` varchar(255) DEFAULT NULL, + `KEY_` varchar(255) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + `DEPLOY_TIME_` timestamp(3) NULL DEFAULT NULL, + `ENGINE_VERSION_` varchar(255) DEFAULT NULL, + `VERSION_` int DEFAULT '1', + `PROJECT_RELEASE_VERSION_` varchar(255) DEFAULT NULL, + PRIMARY KEY (`ID_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RE_MODEL` +-- + +DROP TABLE IF EXISTS `ACT_RE_MODEL`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RE_MODEL` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `NAME_` varchar(255) DEFAULT NULL, + `KEY_` varchar(255) DEFAULT NULL, + `CATEGORY_` varchar(255) DEFAULT NULL, + `CREATE_TIME_` timestamp(3) NULL DEFAULT NULL, + `LAST_UPDATE_TIME_` timestamp(3) NULL DEFAULT NULL, + `VERSION_` int DEFAULT NULL, + `META_INFO_` varchar(4000) DEFAULT NULL, + `DEPLOYMENT_ID_` varchar(64) DEFAULT NULL, + `EDITOR_SOURCE_VALUE_ID_` varchar(64) DEFAULT NULL, + `EDITOR_SOURCE_EXTRA_VALUE_ID_` varchar(64) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_FK_MODEL_SOURCE` (`EDITOR_SOURCE_VALUE_ID_`), + KEY `ACT_FK_MODEL_SOURCE_EXTRA` (`EDITOR_SOURCE_EXTRA_VALUE_ID_`), + KEY `ACT_FK_MODEL_DEPLOYMENT` (`DEPLOYMENT_ID_`), + CONSTRAINT `ACT_FK_MODEL_DEPLOYMENT` FOREIGN KEY (`DEPLOYMENT_ID_`) REFERENCES `ACT_RE_DEPLOYMENT` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_MODEL_SOURCE` FOREIGN KEY (`EDITOR_SOURCE_VALUE_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_MODEL_SOURCE_EXTRA` FOREIGN KEY (`EDITOR_SOURCE_EXTRA_VALUE_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RE_PROCDEF` +-- + +DROP TABLE IF EXISTS `ACT_RE_PROCDEF`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RE_PROCDEF` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `CATEGORY_` varchar(255) DEFAULT NULL, + `NAME_` varchar(255) DEFAULT NULL, + `KEY_` varchar(255) NOT NULL, + `VERSION_` int NOT NULL, + `DEPLOYMENT_ID_` varchar(64) DEFAULT NULL, + `RESOURCE_NAME_` varchar(4000) DEFAULT NULL, + `DGRM_RESOURCE_NAME_` varchar(4000) DEFAULT NULL, + `DESCRIPTION_` varchar(4000) DEFAULT NULL, + `HAS_START_FORM_KEY_` tinyint DEFAULT NULL, + `HAS_GRAPHICAL_NOTATION_` tinyint DEFAULT NULL, + `SUSPENSION_STATE_` int DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + `ENGINE_VERSION_` varchar(255) DEFAULT NULL, + `APP_VERSION_` int DEFAULT NULL, + PRIMARY KEY (`ID_`), + UNIQUE KEY `ACT_UNIQ_PROCDEF` (`KEY_`,`VERSION_`,`TENANT_ID_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_DEADLETTER_JOB` +-- + +DROP TABLE IF EXISTS `ACT_RU_DEADLETTER_JOB`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_DEADLETTER_JOB` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `TYPE_` varchar(255) NOT NULL, + `EXCLUSIVE_` tinyint(1) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROCESS_INSTANCE_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `EXCEPTION_STACK_ID_` varchar(64) DEFAULT NULL, + `EXCEPTION_MSG_` varchar(4000) DEFAULT NULL, + `DUEDATE_` timestamp(3) NULL DEFAULT NULL, + `REPEAT_` varchar(255) DEFAULT NULL, + `HANDLER_TYPE_` varchar(255) DEFAULT NULL, + `HANDLER_CFG_` varchar(4000) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_FK_DEADLETTER_JOB_EXECUTION` (`EXECUTION_ID_`), + KEY `ACT_FK_DEADLETTER_JOB_PROCESS_INSTANCE` (`PROCESS_INSTANCE_ID_`), + KEY `ACT_FK_DEADLETTER_JOB_PROC_DEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_DEADLETTER_JOB_EXCEPTION` (`EXCEPTION_STACK_ID_`), + CONSTRAINT `ACT_FK_DEADLETTER_JOB_EXCEPTION` FOREIGN KEY (`EXCEPTION_STACK_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_DEADLETTER_JOB_EXECUTION` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_DEADLETTER_JOB_PROC_DEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_DEADLETTER_JOB_PROCESS_INSTANCE` FOREIGN KEY (`PROCESS_INSTANCE_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_EVENT_SUBSCR` +-- + +DROP TABLE IF EXISTS `ACT_RU_EVENT_SUBSCR`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_EVENT_SUBSCR` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `EVENT_TYPE_` varchar(255) NOT NULL, + `EVENT_NAME_` varchar(255) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `ACTIVITY_ID_` varchar(64) DEFAULT NULL, + `CONFIGURATION_` varchar(255) DEFAULT NULL, + `CREATED_` timestamp(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3), + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_IDX_EVENT_SUBSCR_CONFIG_` (`CONFIGURATION_`), + KEY `ACT_FK_EVENT_EXEC` (`EXECUTION_ID_`), + CONSTRAINT `ACT_FK_EVENT_EXEC` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_EXECUTION` +-- + +DROP TABLE IF EXISTS `ACT_RU_EXECUTION`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_EXECUTION` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `BUSINESS_KEY_` varchar(255) DEFAULT NULL, + `PARENT_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `SUPER_EXEC_` varchar(64) DEFAULT NULL, + `ROOT_PROC_INST_ID_` varchar(64) DEFAULT NULL, + `ACT_ID_` varchar(255) DEFAULT NULL, + `IS_ACTIVE_` tinyint DEFAULT NULL, + `IS_CONCURRENT_` tinyint DEFAULT NULL, + `IS_SCOPE_` tinyint DEFAULT NULL, + `IS_EVENT_SCOPE_` tinyint DEFAULT NULL, + `IS_MI_ROOT_` tinyint DEFAULT NULL, + `SUSPENSION_STATE_` int DEFAULT NULL, + `CACHED_ENT_STATE_` int DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + `NAME_` varchar(255) DEFAULT NULL, + `START_TIME_` datetime(3) DEFAULT NULL, + `START_USER_ID_` varchar(255) DEFAULT NULL, + `LOCK_TIME_` timestamp(3) NULL DEFAULT NULL, + `IS_COUNT_ENABLED_` tinyint DEFAULT NULL, + `EVT_SUBSCR_COUNT_` int DEFAULT NULL, + `TASK_COUNT_` int DEFAULT NULL, + `JOB_COUNT_` int DEFAULT NULL, + `TIMER_JOB_COUNT_` int DEFAULT NULL, + `SUSP_JOB_COUNT_` int DEFAULT NULL, + `DEADLETTER_JOB_COUNT_` int DEFAULT NULL, + `VAR_COUNT_` int DEFAULT NULL, + `ID_LINK_COUNT_` int DEFAULT NULL, + `APP_VERSION_` int DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_IDX_EXEC_BUSKEY` (`BUSINESS_KEY_`), + KEY `ACT_IDC_EXEC_ROOT` (`ROOT_PROC_INST_ID_`), + KEY `ACT_FK_EXE_PROCINST` (`PROC_INST_ID_`), + KEY `ACT_FK_EXE_PARENT` (`PARENT_ID_`), + KEY `ACT_FK_EXE_SUPER` (`SUPER_EXEC_`), + KEY `ACT_FK_EXE_PROCDEF` (`PROC_DEF_ID_`), + CONSTRAINT `ACT_FK_EXE_PARENT` FOREIGN KEY (`PARENT_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_EXE_PROCDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_EXE_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE CASCADE ON UPDATE CASCADE, + CONSTRAINT `ACT_FK_EXE_SUPER` FOREIGN KEY (`SUPER_EXEC_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE CASCADE ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_IDENTITYLINK` +-- + +DROP TABLE IF EXISTS `ACT_RU_IDENTITYLINK`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_IDENTITYLINK` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `GROUP_ID_` varchar(255) DEFAULT NULL, + `TYPE_` varchar(255) DEFAULT NULL, + `USER_ID_` varchar(255) DEFAULT NULL, + `TASK_ID_` varchar(64) DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_IDX_IDENT_LNK_USER` (`USER_ID_`), + KEY `ACT_IDX_IDENT_LNK_GROUP` (`GROUP_ID_`), + KEY `ACT_IDX_ATHRZ_PROCEDEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_TSKASS_TASK` (`TASK_ID_`), + KEY `ACT_FK_IDL_PROCINST` (`PROC_INST_ID_`), + CONSTRAINT `ACT_FK_ATHRZ_PROCEDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_IDL_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TSKASS_TASK` FOREIGN KEY (`TASK_ID_`) REFERENCES `ACT_RU_TASK` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_INTEGRATION` +-- + +DROP TABLE IF EXISTS `ACT_RU_INTEGRATION`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_INTEGRATION` ( + `ID_` varchar(64) NOT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROCESS_INSTANCE_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `FLOW_NODE_ID_` varchar(64) DEFAULT NULL, + `CREATED_DATE_` timestamp(3) NULL DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_FK_INT_EXECUTION` (`EXECUTION_ID_`), + KEY `ACT_FK_INT_PROC_INST` (`PROCESS_INSTANCE_ID_`), + KEY `ACT_FK_INT_PROC_DEF` (`PROC_DEF_ID_`), + CONSTRAINT `ACT_FK_INT_EXECUTION` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE CASCADE ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_INT_PROC_DEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_INT_PROC_INST` FOREIGN KEY (`PROCESS_INSTANCE_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_JOB` +-- + +DROP TABLE IF EXISTS `ACT_RU_JOB`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_JOB` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `TYPE_` varchar(255) NOT NULL, + `LOCK_EXP_TIME_` timestamp(3) NULL DEFAULT NULL, + `LOCK_OWNER_` varchar(255) DEFAULT NULL, + `EXCLUSIVE_` tinyint(1) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROCESS_INSTANCE_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `RETRIES_` int DEFAULT NULL, + `EXCEPTION_STACK_ID_` varchar(64) DEFAULT NULL, + `EXCEPTION_MSG_` varchar(4000) DEFAULT NULL, + `DUEDATE_` timestamp(3) NULL DEFAULT NULL, + `REPEAT_` varchar(255) DEFAULT NULL, + `HANDLER_TYPE_` varchar(255) DEFAULT NULL, + `HANDLER_CFG_` varchar(4000) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_FK_JOB_EXECUTION` (`EXECUTION_ID_`), + KEY `ACT_FK_JOB_PROCESS_INSTANCE` (`PROCESS_INSTANCE_ID_`), + KEY `ACT_FK_JOB_PROC_DEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_JOB_EXCEPTION` (`EXCEPTION_STACK_ID_`), + CONSTRAINT `ACT_FK_JOB_EXCEPTION` FOREIGN KEY (`EXCEPTION_STACK_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_JOB_EXECUTION` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_JOB_PROC_DEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_JOB_PROCESS_INSTANCE` FOREIGN KEY (`PROCESS_INSTANCE_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_SUSPENDED_JOB` +-- + +DROP TABLE IF EXISTS `ACT_RU_SUSPENDED_JOB`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_SUSPENDED_JOB` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `TYPE_` varchar(255) NOT NULL, + `EXCLUSIVE_` tinyint(1) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROCESS_INSTANCE_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `RETRIES_` int DEFAULT NULL, + `EXCEPTION_STACK_ID_` varchar(64) DEFAULT NULL, + `EXCEPTION_MSG_` varchar(4000) DEFAULT NULL, + `DUEDATE_` timestamp(3) NULL DEFAULT NULL, + `REPEAT_` varchar(255) DEFAULT NULL, + `HANDLER_TYPE_` varchar(255) DEFAULT NULL, + `HANDLER_CFG_` varchar(4000) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_FK_SUSPENDED_JOB_EXECUTION` (`EXECUTION_ID_`), + KEY `ACT_FK_SUSPENDED_JOB_PROCESS_INSTANCE` (`PROCESS_INSTANCE_ID_`), + KEY `ACT_FK_SUSPENDED_JOB_PROC_DEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_SUSPENDED_JOB_EXCEPTION` (`EXCEPTION_STACK_ID_`), + CONSTRAINT `ACT_FK_SUSPENDED_JOB_EXCEPTION` FOREIGN KEY (`EXCEPTION_STACK_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_SUSPENDED_JOB_EXECUTION` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_SUSPENDED_JOB_PROC_DEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_SUSPENDED_JOB_PROCESS_INSTANCE` FOREIGN KEY (`PROCESS_INSTANCE_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_TASK` +-- + +DROP TABLE IF EXISTS `ACT_RU_TASK`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_TASK` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `NAME_` varchar(255) DEFAULT NULL, + `BUSINESS_KEY_` varchar(255) DEFAULT NULL, + `PARENT_TASK_ID_` varchar(64) DEFAULT NULL, + `DESCRIPTION_` varchar(4000) DEFAULT NULL, + `TASK_DEF_KEY_` varchar(255) DEFAULT NULL, + `OWNER_` varchar(255) DEFAULT NULL, + `ASSIGNEE_` varchar(255) DEFAULT NULL, + `DELEGATION_` varchar(64) DEFAULT NULL, + `PRIORITY_` int DEFAULT NULL, + `CREATE_TIME_` timestamp(3) NULL DEFAULT NULL, + `DUE_DATE_` datetime(3) DEFAULT NULL, + `CATEGORY_` varchar(255) DEFAULT NULL, + `SUSPENSION_STATE_` int DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + `FORM_KEY_` varchar(255) DEFAULT NULL, + `CLAIM_TIME_` datetime(3) DEFAULT NULL, + `APP_VERSION_` int DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_IDX_TASK_CREATE` (`CREATE_TIME_`), + KEY `ACT_FK_TASK_EXE` (`EXECUTION_ID_`), + KEY `ACT_FK_TASK_PROCINST` (`PROC_INST_ID_`), + KEY `ACT_FK_TASK_PROCDEF` (`PROC_DEF_ID_`), + CONSTRAINT `ACT_FK_TASK_EXE` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TASK_PROCDEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TASK_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_TIMER_JOB` +-- + +DROP TABLE IF EXISTS `ACT_RU_TIMER_JOB`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_TIMER_JOB` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `TYPE_` varchar(255) NOT NULL, + `LOCK_EXP_TIME_` timestamp(3) NULL DEFAULT NULL, + `LOCK_OWNER_` varchar(255) DEFAULT NULL, + `EXCLUSIVE_` tinyint(1) DEFAULT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROCESS_INSTANCE_ID_` varchar(64) DEFAULT NULL, + `PROC_DEF_ID_` varchar(64) DEFAULT NULL, + `RETRIES_` int DEFAULT NULL, + `EXCEPTION_STACK_ID_` varchar(64) DEFAULT NULL, + `EXCEPTION_MSG_` varchar(4000) DEFAULT NULL, + `DUEDATE_` timestamp(3) NULL DEFAULT NULL, + `REPEAT_` varchar(255) DEFAULT NULL, + `HANDLER_TYPE_` varchar(255) DEFAULT NULL, + `HANDLER_CFG_` varchar(4000) DEFAULT NULL, + `TENANT_ID_` varchar(255) DEFAULT '', + PRIMARY KEY (`ID_`), + KEY `ACT_FK_TIMER_JOB_EXECUTION` (`EXECUTION_ID_`), + KEY `ACT_FK_TIMER_JOB_PROCESS_INSTANCE` (`PROCESS_INSTANCE_ID_`), + KEY `ACT_FK_TIMER_JOB_PROC_DEF` (`PROC_DEF_ID_`), + KEY `ACT_FK_TIMER_JOB_EXCEPTION` (`EXCEPTION_STACK_ID_`), + CONSTRAINT `ACT_FK_TIMER_JOB_EXCEPTION` FOREIGN KEY (`EXCEPTION_STACK_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TIMER_JOB_EXECUTION` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TIMER_JOB_PROC_DEF` FOREIGN KEY (`PROC_DEF_ID_`) REFERENCES `ACT_RE_PROCDEF` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_TIMER_JOB_PROCESS_INSTANCE` FOREIGN KEY (`PROCESS_INSTANCE_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `ACT_RU_VARIABLE` +-- + +DROP TABLE IF EXISTS `ACT_RU_VARIABLE`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `ACT_RU_VARIABLE` ( + `ID_` varchar(64) NOT NULL, + `REV_` int DEFAULT NULL, + `TYPE_` varchar(255) NOT NULL, + `NAME_` varchar(255) NOT NULL, + `EXECUTION_ID_` varchar(64) DEFAULT NULL, + `PROC_INST_ID_` varchar(64) DEFAULT NULL, + `TASK_ID_` varchar(64) DEFAULT NULL, + `BYTEARRAY_ID_` varchar(64) DEFAULT NULL, + `DOUBLE_` double DEFAULT NULL, + `LONG_` bigint DEFAULT NULL, + `TEXT_` varchar(4000) DEFAULT NULL, + `TEXT2_` varchar(4000) DEFAULT NULL, + PRIMARY KEY (`ID_`), + KEY `ACT_IDX_VARIABLE_TASK_ID` (`TASK_ID_`), + KEY `ACT_FK_VAR_EXE` (`EXECUTION_ID_`), + KEY `ACT_FK_VAR_PROCINST` (`PROC_INST_ID_`), + KEY `ACT_FK_VAR_BYTEARRAY` (`BYTEARRAY_ID_`), + CONSTRAINT `ACT_FK_VAR_BYTEARRAY` FOREIGN KEY (`BYTEARRAY_ID_`) REFERENCES `ACT_GE_BYTEARRAY` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_VAR_EXE` FOREIGN KEY (`EXECUTION_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT, + CONSTRAINT `ACT_FK_VAR_PROCINST` FOREIGN KEY (`PROC_INST_ID_`) REFERENCES `ACT_RU_EXECUTION` (`ID_`) ON DELETE RESTRICT ON UPDATE RESTRICT +) ENGINE=InnoDB DEFAULT CHARSET=utf8; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_apply_order` +-- + +DROP TABLE IF EXISTS `biz_apply_order`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_apply_order` ( + `id_` varchar(50) NOT NULL COMMENT '主键', + `apply_no_` varchar(50) DEFAULT NULL COMMENT '订单编号', + `qdjl` varchar(50) DEFAULT NULL COMMENT '渠道经理', + `qdjl_id_` varchar(50) DEFAULT NULL COMMENT '渠道经理ID', + `org_id_` varchar(50) DEFAULT NULL COMMENT '组织ID', + `org_name_` varchar(50) DEFAULT NULL COMMENT '组织', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `remark` varchar(500) DEFAULT NULL COMMENT '备注', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '创建所属组织', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='订单信息'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_column` +-- + +DROP TABLE IF EXISTS `biz_column`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_column` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `table_id_` varchar(64) DEFAULT NULL COMMENT '表id', + `code_` varchar(64) DEFAULT NULL COMMENT '别名', + `name_` varchar(64) DEFAULT NULL COMMENT '名字', + `type_` varchar(64) DEFAULT NULL COMMENT '类型', + `length_` int DEFAULT NULL, + `decimal_` int DEFAULT NULL, + `required_` tinyint DEFAULT NULL, + `primary_` tinyint DEFAULT NULL, + `sn_` int DEFAULT NULL COMMENT '序号', + `default_value_` varchar(255) DEFAULT NULL, + `comment_` varchar(256) DEFAULT NULL, + `valid_rule_` varchar(255) DEFAULT NULL COMMENT '字段校验', + `setting_` varchar(255) DEFAULT NULL, + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务字段表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_cust_dialog` +-- + +DROP TABLE IF EXISTS `biz_cust_dialog`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_cust_dialog` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `code_` varchar(64) DEFAULT NULL COMMENT '编码', + `name_` varchar(128) NOT NULL COMMENT '名字', + `desc_` varchar(256) DEFAULT NULL COMMENT '描述', + `style_` varchar(32) DEFAULT NULL COMMENT '显示类型', + `ds_key_` varchar(64) DEFAULT NULL COMMENT '数据源别名', + `ds_name_` varchar(128) DEFAULT NULL COMMENT '数据源名字', + `obj_type_` varchar(255) DEFAULT NULL COMMENT '对象类型', + `obj_name_` varchar(64) NOT NULL COMMENT '对象名称', + `page_` tinyint DEFAULT NULL COMMENT '是否分页', + `page_size_` int DEFAULT NULL COMMENT '分页大小', + `width_` int DEFAULT NULL COMMENT '弹出框的宽度', + `height_` int DEFAULT NULL COMMENT '弹出框的高度', + `system_` tinyint DEFAULT NULL COMMENT '是否系统内置', + `multiple_` tinyint DEFAULT NULL COMMENT '是否多选', + `tree_config_json_` varchar(512) DEFAULT NULL COMMENT '树形的配置信息,json字段', + `display_fields_json_` text COMMENT '显示字段', + `condition_fields_json_` text COMMENT '条件字段的json', + `return_fields_json_` text COMMENT '返回字段json', + `sort_fields_json_` text COMMENT '排序字段', + `data_source_` varchar(64) DEFAULT NULL, + `left_tree_` tinyint DEFAULT NULL COMMENT '是否开启左侧树', + `left_tree_config_json_` text COMMENT '左侧树配置信息 json字段', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`), + UNIQUE KEY `idx_unqiue` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自定义对话框'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `biz_cust_dialog` +-- + +LOCK TABLES `biz_cust_dialog` WRITE; +/*!40000 ALTER TABLE `biz_cust_dialog` DISABLE KEYS */; +INSERT INTO `biz_cust_dialog` VALUES ('150290149177425921','dbTableSelector','数据库表选择器',NULL,'list','dataSourceDefault','本地数据源','view','v_sys_tables',1,10,800,800,1,0,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"table_name_\",\"showName\":\"表名\"},{\"columnName\":\"table_comment_\",\"showName\":\"描述\"}]','[{\"columnName\":\"table_name_\",\"dbType\":\"varchar\",\"showName\":\"表名\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\"}},{\"columnName\":\"table_comment_\",\"dbType\":\"clob\",\"showName\":\"描述\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\"}}]','[{\"columnName\":\"table_name_\",\"returnName\":\"tablename\",\"showName\":\"表名\"},{\"columnName\":\"table_comment_\",\"returnName\":\"tablecomment\",\"showName\":\"描述\"}]','[]','database',0,'{\"type\":\"sysTree\",\"rootName\":\"所有数据\"}',NULL,NULL,NULL,'2022-04-29 15:39:11','测试','1'),('150785834542301185','dataSourceSelector','数据源选择器','数据源选择器','list','dataSourceDefault','本地数据源','table','sysDataSourceManager.query',1,10,800,600,1,1,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"alias\",\"showName\":\"别名\",\"formatterType\":\"js\"},{\"columnName\":\"name\",\"showName\":\"名称\",\"formatterType\":\"js\"}]','[{\"columnName\":\"name\",\"showName\":\"名称\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}}]','[{\"columnName\":\"alias\",\"returnName\":\"alias\",\"showName\":\"别名\"},{\"columnName\":\"name\",\"returnName\":\"name\",\"showName\":\"名称\"}]','[]','interface',0,'{\"type\":\"sysTree\",\"rootName\":\"所有数据\"}',NULL,NULL,NULL,'2023-03-28 18:32:14','系统管理员','1602918114232172634'),('150894137384239105','czls','操作历史','操作历史','list','dataSourceDefault','本地数据源','table','sys_audit_log',1,10,80,80,1,0,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"operator_\",\"showName\":\"操作者\",\"formatterType\":\"js\"},{\"columnName\":\"description_\",\"showName\":\"操作描述\",\"formatterType\":\"js\"},{\"columnName\":\"create_time_\",\"showName\":\"操作时间\",\"formatterType\":\"date\",\"formatter\":\"yyyy-MM-dd HH:mm:ss\"}]','[{\"columnName\":\"biz_id_\",\"dbType\":\"varchar\",\"showName\":\"关联业务主键\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{}},{\"columnName\":\"operator_\",\"dbType\":\"varchar\",\"showName\":\"操作者\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\"}},{\"columnName\":\"create_time_\",\"dbType\":\"date\",\"showName\":\"操作时间\",\"condition\":\"BT\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputDate\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"主键\"},{\"columnName\":\"log_medata_id_\",\"returnName\":\"logmedataid\",\"showName\":\"日志元数据编号\"},{\"columnName\":\"account_\",\"returnName\":\"account\",\"showName\":\"操作账户\"},{\"columnName\":\"operator_\",\"returnName\":\"operator\",\"showName\":\"操作者\"},{\"columnName\":\"ip_\",\"returnName\":\"ip\",\"showName\":\"操作客户端IP\"},{\"columnName\":\"trace_id_\",\"returnName\":\"traceid\",\"showName\":\"日志跟踪编号\"},{\"columnName\":\"biz_id_\",\"returnName\":\"bizid\",\"showName\":\"关联业务主键\"},{\"columnName\":\"description_\",\"returnName\":\"description\",\"showName\":\"操作描述\"},{\"columnName\":\"data_\",\"returnName\":\"data\",\"showName\":\"记录数据\"},{\"columnName\":\"create_time_\",\"returnName\":\"createtime\",\"showName\":\"创建时间\"},{\"columnName\":\"create_by_\",\"returnName\":\"createby\",\"showName\":\"创建人\"}]','[{\"columnName\":\"create_time_\",\"sortType\":\"desc\"}]','database',0,'{\"type\":\"\",\"key\":\"property\",\"name\":\"property\",\"condField\":\"biz_id_\",\"rootName\":\"所有数据\"}',NULL,NULL,NULL,'2023-03-28 18:32:00','系统管理员','1602918114232172634'),('1510875112887717898','custDialog','自定义对话框列表','自定义对话框列表','list','dataSourceDefault','本地数据源','table','biz_cust_dialog',1,10,1300,800,1,1,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"名字\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"code_\",\"showName\":\"编码\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"style_\",\"showName\":\"显示类型\",\"formatterType\":\"json\",\"formatter\":\"[{\\\"value\\\":\\\"list\\\",\\\"key\\\":\\\"列表\\\",\\\"styleValue\\\":\\\"success\\\"},{\\\"value\\\":\\\"tree\\\",\\\"key\\\":\\\"树形\\\",\\\"styleValue\\\":\\\"warning\\\"}]\"},{\"columnName\":\"obj_type_\",\"showName\":\"对象类型\",\"formatterType\":\"json\",\"formatter\":\"[{\\\"value\\\":\\\"table\\\",\\\"key\\\":\\\"表\\\",\\\"styleValue\\\":\\\"success\\\"},{\\\"value\\\":\\\"view\\\",\\\"key\\\":\\\"视图\\\",\\\"styleValue\\\":\\\"warning\\\"}]\"}]','[{\"columnName\":\"code_\",\"showName\":\"编码\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"name_\",\"dbType\":\"varchar\",\"showName\":\"名字\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\"}}]','[{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"\"},{\"columnName\":\"name_\",\"returnName\":\"name\"}]','[{\"columnName\":\"id_\",\"sortType\":\"desc\"}]','database',0,'{\"type\":\"sysTree\",\"rootName\":\"所有数据\"}',NULL,NULL,NULL,'2023-03-28 18:35:19','系统管理员','1602918114232172634'),('1531208232518676480','bizTableSelector','业务实体列表','业务实体列表','list','dataSourceDefault','本地数据源','table','bizTableManager.query',0,10,1300,600,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name\",\"showName\":\"表名\",\"formatter\":\"\"},{\"columnName\":\"comment\",\"showName\":\"描述\",\"formatter\":\"\"},{\"columnName\":\"code\",\"showName\":\"编码\",\"formatter\":\"\"}]','[{\"columnName\":\"name\",\"showName\":\"表名\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"comment\",\"showName\":\"描述\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"code\",\"showName\":\"编码\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"typeCode\",\"showName\":\"分组编码\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"name\",\"returnName\":\"name\",\"showName\":\"表名\"},{\"columnName\":\"comment\",\"returnName\":\"comment\",\"showName\":\"描述\"},{\"columnName\":\"id\",\"returnName\":\"id\",\"showName\":\"主键\"},{\"columnName\":\"code\",\"returnName\":\"code\",\"showName\":\"编码\"},{\"columnName\":\"dsKey\",\"returnName\":\"dsKey\",\"showName\":\"数据源的别名\"},{\"columnName\":\"dsName\",\"returnName\":\"dsName\",\"showName\":\"数据源名称\"},{\"columnName\":\"typeCode\",\"returnName\":\"typeCode\",\"showName\":\"分组编码\"},{\"columnName\":\"external\",\"returnName\":\"external\",\"showName\":\"表类型\"},{\"columnName\":\"createdTable\",\"returnName\":\"createdTable\",\"showName\":\"生成物理表\"}]','[{\"columnName\":\"updateTime\",\"sortType\":\"desc\"}]','interface',1,'{\"type\":\"dataDict\",\"key\":\"biz\",\"name\":\"\",\"condField\":\"typeCode\",\"rootName\":\"所有数据\"}','2022-05-30 17:37:46','1','410054574497333249','2023-01-05 18:07:54','系统管理员11','1'),('1533724306577616896','formSelector','表单选择框','','list','dataSourceDefault','本地数据源','table','biz_form',1,10,1100,800,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"code_\",\"showName\":\"编码\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"名字\",\"formatter\":\"\"},{\"columnName\":\"design_code_\",\"showName\":\"设计编码\",\"formatter\":\"\"},{\"columnName\":\"bo_code_\",\"showName\":\"业务对象编码\",\"formatter\":\"\"},{\"columnName\":\"bo_name_\",\"showName\":\"业务对象名称\",\"formatter\":\"\"}]','[{\"columnName\":\"code_\",\"showName\":\"编码\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"name_\",\"showName\":\"名字\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"design_code_\",\"dbType\":\"varchar\",\"showName\":\"设计编码\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"type_\",\"dbType\":\"varchar\",\"showName\":\"分类(pc/mobile)\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}},{\"columnName\":\"bo_code_\",\"dbType\":\"varchar\",\"showName\":\"业务对象编码\",\"condition\":\"IN\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}},{\"columnName\":\"type_code_\",\"dbType\":\"varchar\",\"showName\":\"分类编码\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"名字\"},{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"type_\",\"returnName\":\"type\",\"showName\":\"分类\"},{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"CODE\"},{\"columnName\":\"type_code_\",\"returnName\":\"typeCode\",\"showName\":\"分类编码\"},{\"columnName\":\"bo_code_\",\"returnName\":\"boCode\",\"showName\":\"业务对象编码\"},{\"columnName\":\"bo_name_\",\"returnName\":\"boName\",\"showName\":\"业务对象名称\"}]','[{\"columnName\":\"create_time_\",\"sortType\":\"desc\"}]','database',1,'{\"type\":\"dataDict\",\"key\":\"biz\",\"name\":\"\",\"condField\":\"type_code_\",\"rootName\":\"所有数据\"}','2022-06-06 16:15:45','1','410054574497333249','2023-01-05 16:32:15','系统管理员11','1'),('1538829115810967552','lcdycx','流程定义查询','流程定义查询','list','dataSourceDefault','本地数据源','view','bpm_instance_view',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"流程名字\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"key_\",\"showName\":\"流程KEY\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"desc_\",\"showName\":\"流程描述\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"form_name_\",\"showName\":\"表单名称\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"key_\",\"showName\":\"流程KEY\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"name_\",\"dbType\":\"varchar\",\"showName\":\"名字\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"form_name_\",\"dbType\":\"varchar\",\"showName\":\"表单名称\",\"condition\":\"EQ\",\"valueSource\":\"inputText\",\"value\":{\"text\":\"\"}}]','[{\"columnName\":\"key_\",\"returnName\":\"key_\",\"showName\":\"流程KEY\"},{\"columnName\":\"name_\",\"returnName\":\"name_\",\"showName\":\"名字\"},{\"columnName\":\"desc_\",\"returnName\":\"desc_\",\"showName\":\"描述\"},{\"columnName\":\"target_id_\",\"returnName\":\"target_id_\",\"showName\":\"关联ID\"},{\"columnName\":\"form_name_\",\"returnName\":\"form_name_\",\"showName\":\"表单名称\"},{\"columnName\":\"id_\",\"returnName\":\"id_\",\"showName\":\"ID\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-06-20 18:20:26','1','410054574497333249','2023-06-19 17:11:02','系统管理员','1602918114232172634'),('1540232450675261440','resDialog','菜单选择','菜单选择','tree','dataSourceDefault','本地数据源','table','sys_resource',0,10,500,600,1,0,'{\"id\":\"id_\",\"pid\":\"parent_id_\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"name_\",\"sync\":true}','[]','[{\"columnName\":\"type_\",\"showName\":\"menu,button,API\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"menu\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id_\",\"showName\":\"ID\"},{\"columnName\":\"name_\",\"returnName\":\"name_\",\"showName\":\"名字\"},{\"columnName\":\"parent_id_\",\"returnName\":\"parent_id_\",\"showName\":\"父节点ID\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-06-24 15:16:47','1','410054574497333249','2023-01-05 18:07:41','系统管理员11','1'),('1541260543082799104','scriptSelector','常用脚本选择框','选择常用脚本','list','dataSourceDefault','本地数据源','table','sys_script',1,10,800,600,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"脚本名称\",\"formatter\":\"\"},{\"columnName\":\"desc_\",\"showName\":\"描述\",\"formatter\":\"\"},{\"columnName\":\"type_code_\",\"showName\":\"分类字典编码\",\"formatter\":\"\"}]','[{\"columnName\":\"name_\",\"showName\":\"脚本名称\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"type_code_\",\"dbType\":\"varchar\",\"showName\":\"分类字典编码\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"script_\",\"returnName\":\"script\",\"showName\":\"脚本\"}]','[]','database',1,'{\"type\":\"dataDict\",\"key\":\"script\",\"name\":\"\",\"condField\":\"type_code_\",\"rootName\":\"所有数据\"}','2022-06-27 11:22:03','1','410054574497333249','2023-01-05 15:37:55','系统管理员11','1'),('1541318385082339328','postSelector','岗位选择','','list','dataSourceDefault','本地数据源','table','orgDialogDataSourceComponent.queryPost',1,10,800,600,1,1,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name\",\"showName\":\"岗位名称\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"id\",\"dbType\":\"varchar\",\"showName\":\"岗位ID\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}},{\"columnName\":\"name\",\"showName\":\"岗位名称\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"status\",\"showName\":\"状态\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}}]','[{\"columnName\":\"groupId\",\"returnName\":\"groupId\",\"showName\":\"组织id\"},{\"columnName\":\"groupName\",\"returnName\":\"groupName\",\"showName\":\"组织名称\"},{\"columnName\":\"id\",\"returnName\":\"key\",\"showName\":\"岗位id\"},{\"columnName\":\"name\",\"returnName\":\"name\",\"showName\":\"岗位名称\"},{\"columnName\":\"roleId\",\"returnName\":\"roleId\",\"showName\":\"角色id\"},{\"columnName\":\"roleName\",\"returnName\":\"roleName\",\"showName\":\"角色名称\"},{\"columnName\":\"id\",\"returnName\":\"id\",\"showName\":\"岗位id\"}]','[]','interface',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-06-27 15:11:54','1','410054574497333249','2023-06-01 15:38:13','系统管理员','1602918114232172634'),('1555096586450096128','zdydhk','自定义列表对话框','查询所有自定义列表数据','list','dataSourceDefault','本地数据源','table','biz_cust_grid',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"名称\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"code_\",\"showName\":\"编码\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"grid_type_\",\"showName\":\"类型\",\"formatterType\":\"json\",\"formatter\":\"[{\\\"value\\\":\\\"app\\\",\\\"key\\\":\\\"应用\\\",\\\"styleValue\\\":\\\"\\\"},{\\\"value\\\":\\\"list\\\",\\\"key\\\":\\\"列表\\\",\\\"styleValue\\\":\\\"\\\"},{\\\"value\\\":\\\"report\\\",\\\"key\\\":\\\"报表\\\",\\\"styleValue\\\":\\\"\\\"}]\"},{\"columnName\":\"design_type_\",\"showName\":\"设计模式\",\"formatterType\":\"json\",\"formatter\":\"[{\\\"value\\\":\\\"simple\\\",\\\"key\\\":\\\"简单模式\\\",\\\"styleValue\\\":\\\"\\\"},{\\\"value\\\":\\\"advanced\\\",\\\"key\\\":\\\"高级模式\\\",\\\"styleValue\\\":\\\"\\\"}]\"}]','[{\"columnName\":\"status_\",\"showName\":\"状态 0:禁用,1:启用\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}},{\"columnName\":\"group_code_\",\"dbType\":\"varchar\",\"showName\":\"分类字典编码\",\"condition\":\"EQ\",\"valueSource\":\"dynamicParam\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"status_\",\"returnName\":\"status_\",\"showName\":\"状态 0:禁用,1:启用\"},{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"名称\"},{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"编码\"},{\"columnName\":\"grid_type_\",\"returnName\":\"grid_type_\",\"showName\":\"类型 app 应用 list 列表 report 报表\"},{\"columnName\":\"design_type_\",\"returnName\":\"design_type_\",\"showName\":\"simple 简单模式 advanced 复杂模式\"},{\"columnName\":\"group_code_\",\"returnName\":\"group_code_\",\"showName\":\"分类字典编码\"},{\"columnName\":\"ds_key_\",\"returnName\":\"ds_key_\",\"showName\":\"数据源别名\"},{\"columnName\":\"init_query_\",\"returnName\":\"init_query_\",\"showName\":\"是否默认查询\"},{\"columnName\":\"fix_tool\",\"returnName\":\"fix_tool\",\"showName\":\"是否固定操作栏\"},{\"columnName\":\"page_size_\",\"returnName\":\"page_size_\",\"showName\":\"分页数量\"},{\"columnName\":\"sql_content_\",\"returnName\":\"sql_content_\",\"showName\":\"SQL\"},{\"columnName\":\"conditions_\",\"returnName\":\"conditions_\",\"showName\":\"查询列表\"},{\"columnName\":\"sql_table_list_\",\"returnName\":\"sql_table_list_\",\"showName\":\"表列表\"},{\"columnName\":\"buttons_\",\"returnName\":\"buttons_\",\"showName\":\"按钮列表\"},{\"columnName\":\"choose_type_\",\"returnName\":\"choose_type_\",\"showName\":\"单选多选\"},{\"columnName\":\"show_rows_num_\",\"returnName\":\"show_rows_num_\",\"showName\":\"显示行号 0:否,1:是\"},{\"columnName\":\"table_name_\",\"returnName\":\"table_name_\",\"showName\":\"表名\"},{\"columnName\":\"pk_name_\",\"returnName\":\"pk_name_\",\"showName\":\"主键名称\"},{\"columnName\":\"left_tree_\",\"returnName\":\"left_tree_\",\"showName\":\"左树配置\"},{\"columnName\":\"app_conf_\",\"returnName\":\"app_conf_\",\"showName\":\"app配置\"},{\"columnName\":\"related_id_\",\"returnName\":\"related_id_\",\"showName\":\"表单KEY/流程定义id\"},{\"columnName\":\"related_name_\",\"returnName\":\"related_name_\",\"showName\":\"关联名称\"},{\"columnName\":\"create_time_\",\"returnName\":\"create_time_\",\"showName\":\"创建时间\"},{\"columnName\":\"create_by_\",\"returnName\":\"create_by_\",\"showName\":\"创建人ID\"},{\"columnName\":\"create_org_id_\",\"returnName\":\"create_org_id_\",\"showName\":\"所属组织\"},{\"columnName\":\"update_time_\",\"returnName\":\"update_time_\",\"showName\":\"更新时间\"},{\"columnName\":\"update_by_\",\"returnName\":\"update_by_\",\"showName\":\"更新人ID\"},{\"columnName\":\"rev_\",\"returnName\":\"rev_\",\"showName\":\"乐观锁版本号\"},{\"columnName\":\"enable_app_\",\"returnName\":\"enable_app_\",\"showName\":\"支持手机端 0:否,1:是\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"group_code_\",\"rootName\":\"所有数据\"}','2022-08-04 15:41:33','1','410054574497333249','2023-03-28 18:40:00','系统管理员','1602918114232172634'),('1556925420482752512','sysAppSelector','系统应用选择器','系统应用选择器','list','dataSourceDefault','本地数据源','table','sys_application',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"系统名称\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"code_\",\"showName\":\"系统编码\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"enabled_\",\"dbType\":\"number\",\"showName\":\"是否可用(0禁用 1启用)\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"应用名\"},{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"编码\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-08-09 16:48:41','1','410054574497333249','2023-03-28 18:37:12','系统管理员','1602918114232172634'),('1559361990154043392','orgTree','组织树含用户数量','','tree','','系统服务接口','table','orgDialogDataSourceComponent.queryOrg',0,10,800,750,1,0,'{\"id\":\"groupId\",\"pid\":\"parentId\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"groupName\",\"sync\":false}','[]','[{\"columnName\":\"userCount\",\"dbType\":\"varchar\",\"showName\":\"用户数量\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}}]','[{\"columnName\":\"groupId\",\"returnName\":\"id\",\"showName\":\"\"},{\"columnName\":\"parentId\",\"returnName\":\"parentId\",\"showName\":\"\"},{\"columnName\":\"groupName\",\"returnName\":\"name\",\"showName\":\"\"}]','[]','interface',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-08-16 10:10:45','1','410054574497333249','2023-02-20 09:38:16','系统管理员','1'),('1560094818385891328','bpmDefinitionSelector','流程定义选择器','流程定义选择器','list','dataSourceDefault','本地数据源','table','bpm_definition',1,10,860,750,1,1,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name_\",\"showName\":\"流程名称\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"key_\",\"showName\":\"流程编码\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"desc_\",\"showName\":\"描述\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"key_\",\"dbType\":\"varchar\",\"showName\":\"流程编码\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"name_\",\"dbType\":\"varchar\",\"showName\":\"流程名称\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"type_code_\",\"dbType\":\"varchar\",\"showName\":\"分类字典KEY\",\"condition\":\"EQ\",\"valueSource\":\"dynamicParam\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}},{\"columnName\":\"is_main_\",\"dbType\":\"number\",\"showName\":\"是否主版本\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}},{\"columnName\":\"is_main_\",\"dbType\":\"number\",\"showName\":\"是否主版本\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"Y\"}}]','[{\"columnName\":\"key_\",\"returnName\":\"key\",\"showName\":\"流程KEY\"},{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"名字\"},{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"icon_style_\",\"returnName\":\"icon\",\"showName\":\"ICON配置\"},{\"columnName\":\"desc_\",\"returnName\":\"desc_\",\"showName\":\"描述\"},{\"columnName\":\"is_main_\",\"returnName\":\"is_main_\",\"showName\":\"是否主版本\"}]','[]','database',1,'{\"type\":\"dataDict\",\"key\":\"flowType\",\"name\":\"\",\"condField\":\"type_code_\",\"rootName\":\"所有数据\"}','2022-08-18 10:42:45','1','','2023-03-28 18:40:42','系统管理员','1602918114232172634'),('1574309131272888320','boSelector','业务对象选择器','','list','dataSourceDefault','本地数据源','table','biz_object',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"code_\",\"showName\":\"编码\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"名字\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"desc_\",\"showName\":\"描述\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"code_\",\"dbType\":\"varchar\",\"showName\":\"编码\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"name_\",\"dbType\":\"varchar\",\"showName\":\"名字\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"type_code_\",\"dbType\":\"varchar\",\"showName\":\"分组编码\",\"condition\":\"EQ\",\"valueSource\":\"dynamicParam\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"编码\"},{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"名字\"},{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"主键\"},{\"columnName\":\"desc_\",\"returnName\":\"desc\",\"showName\":\"描述\"}]','[{\"columnName\":\"update_time_\",\"sortType\":\"desc\"}]','database',1,'{\"type\":\"dataDict\",\"key\":\"biz\",\"name\":\"\",\"condField\":\"type_code_\",\"rootName\":\"所有数据\"}','2022-09-26 16:05:21','1','','2023-03-07 09:17:37','系统管理员','1602918114232172634'),('1579383899762028544','areaQuery','区域查询选择','区域查询选择对话框','tree','dataSourceDefault','本地数据源','table','sys_region',0,10,800,750,1,0,'{\"id\":\"id\",\"pid\":\"parentId\",\"pidInitVal\":\"100000\",\"pidInitValScript\":false,\"showColumn\":\"name\",\"sync\":true}','[{\"columnName\":\"id\",\"showName\":\"区域主键\",\"formatter\":\"\"},{\"columnName\":\"name\",\"showName\":\"区域名称\",\"formatter\":\"\"},{\"columnName\":\"name\",\"showName\":\"区域名称\",\"formatter\":\"\"}]','[{\"columnName\":\"parentId\",\"dbType\":\"number\",\"showName\":\"区域上级标识\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}},{\"columnName\":\"level\",\"dbType\":\"number\",\"showName\":\"区域等级\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"parentId\",\"returnName\":\"parentId\",\"showName\":\"区域上级标识\"},{\"columnName\":\"level\",\"returnName\":\"level\",\"showName\":\"区域等级\"},{\"columnName\":\"id\",\"returnName\":\"id\",\"showName\":\"区域主键\"},{\"columnName\":\"name\",\"returnName\":\"name\",\"showName\":\"区域名称\"},{\"columnName\":\"sname\",\"returnName\":\"sname\",\"showName\":\"地名简称\"},{\"columnName\":\"citycode\",\"returnName\":\"citycode\",\"showName\":\"区域编码\"},{\"columnName\":\"yzcode\",\"returnName\":\"yzcode\",\"showName\":\"邮政编码\"},{\"columnName\":\"mername\",\"returnName\":\"mername\",\"showName\":\"组合名称\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"level\",\"rootName\":\"所有数据\"}','2022-10-10 16:10:40','1','','2023-02-01 18:40:15','系统管理员11','1'),('1602972341917237248','sjzdsjq','数据字典(设计器)','','list','dataSourceDefault','本地数据源','table','sys_data_dict',1,10,800,750,1,0,'{\"id\":\"id_\",\"pid\":\"parent_id_\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"name_\",\"sync\":false}','[{\"columnName\":\"code_\",\"showName\":\"编码\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"名称\",\"formatter\":\"\"}]','[{\"columnName\":\"dict_type_\",\"dbType\":\"varchar\",\"showName\":\"dict/node字典项\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"dict\"}},{\"columnName\":\"type_code_\",\"dbType\":\"varchar\",\"showName\":\"分组字典编码\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"code_\",\"returnName\":\"code\",\"showName\":\"编码\"},{\"columnName\":\"name_\",\"returnName\":\"name\",\"showName\":\"name\"}]','[{\"columnName\":\"id_\",\"sortType\":\"desc\"}]','database',1,'{\"type\":\"dataDict\",\"key\":\"dictType\",\"name\":\"数据字典左侧树\",\"condField\":\"type_code_\",\"rootName\":\"所有数据\"}','2022-12-14 18:22:43','1','1574336018731565056','2023-01-10 15:26:19','系统管理员','1602918114232172634'),('1606109734196805632','followerQuote','流程引用选择器','流程引用选择器','list','dataSourceDefault','本地数据源','table','bpmInstanceManager.getApplyList',1,10,800,750,1,1,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"title\",\"showName\":\"流程标题\",\"formatter\":\"\"},{\"columnName\":\"defName\",\"showName\":\"流程名称\",\"formatter\":\"\"}]','[{\"columnName\":\"title\",\"showName\":\"流程标题\",\"condition\":\"LK\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}}]','[{\"columnName\":\"title\",\"returnName\":\"title\",\"showName\":\"流程标题\"},{\"columnName\":\"defName\",\"returnName\":\"defName\",\"showName\":\"流程名称\"},{\"columnName\":\"id\",\"returnName\":\"id\",\"showName\":\"实例Id\"}]','[]','interface',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-12-23 10:09:35','1','1574336018731565056','2023-02-06 09:29:39','系统管理员11','1'),('1607211638599516160','xxmbxz','消息模板选择','选择消息模板对话框,勿删','list','dataSourceDefault','本地数据源','table','ab_message_template',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"code_\",\"showName\":\"模板编码\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"模板名称\",\"formatter\":\"\"},{\"columnName\":\"desc_\",\"showName\":\"模板描述\",\"formatter\":\"\"}]','[{\"columnName\":\"code_\",\"dbType\":\"varchar\",\"showName\":\"模板编码\",\"condition\":\"EQ\",\"valueSource\":\"param\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id_\",\"showName\":\"模板id\"},{\"columnName\":\"code_\",\"returnName\":\"code_\",\"showName\":\"模板编码\"},{\"columnName\":\"type_\",\"returnName\":\"type_\",\"showName\":\"模板类型\"},{\"columnName\":\"name_\",\"returnName\":\"name_\",\"showName\":\"模板名称\"},{\"columnName\":\"desc_\",\"returnName\":\"desc_\",\"showName\":\"模板描述\"},{\"columnName\":\"html_template_\",\"returnName\":\"html_template_\",\"showName\":\"html模板配置\"},{\"columnName\":\"card_template_\",\"returnName\":\"card_template_\",\"showName\":\"卡片模板配置\"},{\"columnName\":\"app_template_\",\"returnName\":\"app_template_\",\"showName\":\"应用模板配置\"},{\"columnName\":\"template_param_\",\"returnName\":\"template_param_\",\"showName\":\"模板参数\"},{\"columnName\":\"enabled_\",\"returnName\":\"enabled_\",\"showName\":\"是否启用\"},{\"columnName\":\"is_delete_\",\"returnName\":\"is_delete_\",\"showName\":\"是否删除\"},{\"columnName\":\"create_time_\",\"returnName\":\"create_time_\",\"showName\":\"创建时间\"},{\"columnName\":\"create_by_\",\"returnName\":\"create_by_\",\"showName\":\"创建人ID\"},{\"columnName\":\"create_org_id_\",\"returnName\":\"create_org_id_\",\"showName\":\"所属组织\"},{\"columnName\":\"update_time_\",\"returnName\":\"update_time_\",\"showName\":\"更新时间\"},{\"columnName\":\"updater_\",\"returnName\":\"updater_\",\"showName\":\"更新人\"},{\"columnName\":\"update_by_\",\"returnName\":\"update_by_\",\"showName\":\"更新人ID\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2022-12-26 11:08:10','1','','2023-01-05 18:01:58','CD-boss','1607298071825809408'),('1621444750016589824','sjzd','数据字典','','tree','dataSourceDefault','本地数据源','table','sys_data_dict',0,10,800,750,1,0,'{\"id\":\"id_\",\"pid\":\"parent_id_\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"name_\",\"sync\":false}','[{\"columnName\":\"id_\",\"showName\":\"ID\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"name\",\"formatter\":\"\"},{\"columnName\":\"id_\",\"showName\":\"ID\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"name\",\"formatter\":\"\"},{\"columnName\":\"id_\",\"showName\":\"ID\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"name\",\"formatter\":\"\"},{\"columnName\":\"id_\",\"showName\":\"ID\",\"formatter\":\"\"},{\"columnName\":\"name_\",\"showName\":\"name\",\"formatter\":\"\"}]','[{\"columnName\":\"dict_key_\",\"dbType\":\"varchar\",\"showName\":\"字典key\",\"condition\":\"NE\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"dictType\"}}]','[{\"columnName\":\"id_\",\"returnName\":\"id_\",\"showName\":\"ID\"},{\"columnName\":\"parent_id_\",\"returnName\":\"parent_id_\",\"showName\":\"上级id\"},{\"columnName\":\"code_\",\"returnName\":\"code_\",\"showName\":\"编码\"},{\"columnName\":\"name_\",\"returnName\":\"name_\",\"showName\":\"name\"}]','[]','database',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2023-02-03 17:45:28','1','1607311446916005888','2023-02-03 18:01:09','系统管理员11','1'),('1632675290280833024','custGrid','自定义列表','','list','dataSourceDefault','本地数据源','table','bizCustGridManager.query',1,10,800,750,1,0,'{\"id\":\"\",\"pid\":\"\",\"pidInitVal\":\"\",\"pidInitValScript\":false,\"showColumn\":\"\",\"sync\":false}','[{\"columnName\":\"name\",\"showName\":\"名称\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"code\",\"showName\":\"编码\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"name\",\"dbType\":\"varchar\",\"showName\":\"名称\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"text\":\"\"}},{\"columnName\":\"status\",\"dbType\":\"varchar\",\"showName\":\"状态\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}}]','[{\"columnName\":\"name\",\"returnName\":\"name\",\"showName\":\"\"},{\"columnName\":\"code\",\"returnName\":\"code\",\"showName\":\"\"}]','[]','interface',0,'{\"type\":\"\",\"key\":\"\",\"name\":\"\",\"condField\":\"\",\"rootName\":\"所有数据\"}','2023-03-06 17:31:38','1602918114232172634','1602918114232172544','2023-03-06 17:37:00','系统管理员','1602918114232172634'),('20000003130001','userSelector','用户选择','','list','dataSourceDefault','本地数据源','table','orgDialogDataSourceComponent.queryUser',1,10,1000,740,1,1,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"fullName\",\"showName\":\"姓名\",\"formatterType\":\"js\",\"formatter\":\"\"},{\"columnName\":\"username\",\"showName\":\"帐号\",\"formatterType\":\"js\",\"formatter\":\"\"}]','[{\"columnName\":\"account\",\"dbType\":\"varchar\",\"showName\":\"账户\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\"}},{\"columnName\":\"fullName\",\"dbType\":\"varchar\",\"showName\":\"姓名\",\"condition\":\"LK\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\"}},{\"columnName\":\"groupId\",\"showName\":\"组织ID\",\"condition\":\"EQ\",\"valueSource\":\"dynamicParam\",\"value\":{\"ctrlType\":\"\",\"text\":\"\"}},{\"columnName\":\"userId\",\"dbType\":\"varchar\",\"showName\":\"用户ID\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}},{\"columnName\":\"username\",\"dbType\":\"varchar\",\"showName\":\"帐号\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}}]','[{\"columnName\":\"userId\",\"returnName\":\"id\",\"showName\":\"用户ID\"},{\"columnName\":\"fullName\",\"returnName\":\"name\",\"showName\":\"名称\"},{\"columnName\":\"username\",\"returnName\":\"account\",\"showName\":\"帐号\"},{\"columnName\":\"email\",\"returnName\":\"email\",\"showName\":\"邮箱\"},{\"columnName\":\"mobile\",\"returnName\":\"mobile\",\"showName\":\"电话\"},{\"columnName\":\"weixin\",\"returnName\":\"weixin\",\"showName\":\"微信\"},{\"columnName\":\"address\",\"returnName\":\"address\",\"showName\":\"地址\"},{\"columnName\":\"photo\",\"returnName\":\"photo\",\"showName\":\"头像\"},{\"columnName\":\"sex\",\"returnName\":\"sex\",\"showName\":\"性别\"},{\"columnName\":\"signature\",\"returnName\":\"signature\",\"showName\":\"签名\"},{\"columnName\":\"from\",\"returnName\":\"from\",\"showName\":\"from\"},{\"columnName\":\"status\",\"returnName\":\"status\",\"showName\":\"状态\"},{\"columnName\":\"openid\",\"returnName\":\"openid\",\"showName\":\"openid\"}]','[]','interface',1,'{\"type\":\"bizCustDialog\",\"key\":\"orgTree\",\"name\":\"组织树含用户数量\",\"condField\":\"groupId\",\"rootName\":\"\"}',NULL,NULL,NULL,'2023-03-28 17:52:26','管理员','1'),('20000003160001','orgSelector','组织选择框','','tree','','系统服务接口','table','orgDialogDataSourceComponent.queryOrg',0,10,800,600,1,1,'{\"id\":\"groupId\",\"pid\":\"parentId\",\"pidInitVal\":\"0\",\"pidInitValScript\":false,\"showColumn\":\"groupName\",\"sync\":false}','[]','[{\"columnName\":\"groupId\",\"dbType\":\"varchar\",\"showName\":\"组织ID\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}},{\"columnName\":\"groupName\",\"dbType\":\"varchar\",\"showName\":\"组织名称\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}}]','[{\"columnName\":\"groupId\",\"returnName\":\"id\",\"showName\":\"组织ID\"},{\"columnName\":\"groupName\",\"returnName\":\"name\",\"showName\":\"组织名称\"},{\"columnName\":\"parentId\",\"returnName\":\"parentId\",\"showName\":\"上级组织ID\"},{\"columnName\":\"groupCode\",\"returnName\":\"key\",\"showName\":\"组织编码\"},{\"columnName\":\"type\",\"returnName\":\"type\"}]','[{\"columnName\":\"sn\",\"sortType\":\"desc\"}]','interface',NULL,NULL,NULL,NULL,NULL,'2023-01-15 10:56:44','管理员','1'),('20000003160002','roleSelector','角色对话','','list','dataSourceDefault','本地数据源','table','orgDialogDataSourceComponent.queryRole',1,10,900,600,1,1,'{\"pidInitValScript\":false,\"sync\":false}','[{\"columnName\":\"groupName\",\"showName\":\"名称\",\"formatterType\":\"js\"},{\"columnName\":\"groupCode\",\"showName\":\"编码\",\"formatterType\":\"js\"},{\"columnName\":\"level\",\"showName\":\"级别\",\"formatterType\":\"js\"}]','[{\"columnName\":\"groupId\",\"dbType\":\"varchar\",\"showName\":\"角色ID\",\"condition\":\"IN\",\"valueSource\":\"dynamicParam\",\"value\":{\"text\":\"\"}},{\"columnName\":\"typeCode\",\"dbType\":\"varchar\",\"showName\":\"分类编码\",\"condition\":\"EQ\",\"valueSource\":\"dynamicParam\",\"value\":{\"ctrlType\":\"\"}},{\"columnName\":\"groupName\",\"showName\":\"名称\",\"condition\":\"EQ\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"code\",\"showName\":\"编码\",\"condition\":\"EQ\",\"valueSource\":\"inputText\",\"value\":{\"ctrlType\":\"inputText\",\"text\":\"\"}},{\"columnName\":\"enabled\",\"showName\":\"0:禁用,1:启用\",\"condition\":\"EQ\",\"valueSource\":\"fixedValue\",\"value\":{\"text\":\"1\"}}]','[{\"columnName\":\"groupId\",\"returnName\":\"id\",\"showName\":\"ID\"},{\"columnName\":\"groupName\",\"returnName\":\"name\",\"showName\":\"名称\"},{\"columnName\":\"groupCode\",\"returnName\":\"key\",\"showName\":\"编码\"}]','[]','interface',1,'{\"type\":\"dataDict\",\"key\":\"jsfl\",\"name\":\"角色分类\",\"condField\":\"typeCode\",\"rootName\":\"所有数据\"}',NULL,NULL,NULL,'2023-03-28 18:33:00','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `biz_cust_dialog` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `biz_cust_grid` +-- + +DROP TABLE IF EXISTS `biz_cust_grid`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_cust_grid` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(100) NOT NULL COMMENT '名称', + `code_` varchar(36) NOT NULL COMMENT '编码', + `grid_type_` varchar(11) NOT NULL COMMENT '类型 app 应用 list 列表 report 报表', + `design_type_` varchar(10) DEFAULT NULL COMMENT 'simple 简单模式 advanced 复杂模式', + `group_code_` varchar(64) DEFAULT NULL COMMENT '分类字典编码', + `ds_key_` varchar(36) DEFAULT NULL COMMENT '数据源别名', + `init_query_` tinyint DEFAULT NULL COMMENT '是否默认查询', + `status_` tinyint DEFAULT NULL COMMENT '状态 0:禁用,1:启用', + `fix_tool` tinyint DEFAULT NULL COMMENT '是否固定操作栏', + `page_size_` int DEFAULT NULL COMMENT '分页数量', + `sql_content_` text NOT NULL COMMENT 'SQL', + `conditions_` text COMMENT '查询列表', + `sql_table_list_` varchar(500) DEFAULT NULL COMMENT '表列表', + `buttons_` text COMMENT '按钮列表', + `choose_type_` tinyint DEFAULT NULL COMMENT '单选多选', + `show_rows_num_` tinyint DEFAULT NULL COMMENT '显示行号 0:否,1:是', + `table_name_` varchar(50) DEFAULT NULL COMMENT '表名', + `pk_name_` varchar(50) DEFAULT NULL COMMENT '主键名称', + `left_tree_` text COMMENT '左树配置', + `extend_conf_` varchar(800) DEFAULT NULL COMMENT '扩展配置', + `app_conf_` text COMMENT 'app配置', + `related_id_` varchar(64) DEFAULT NULL COMMENT '表单KEY/流程定义id', + `max_export_rows_` int DEFAULT NULL COMMENT '最大导出行数', + `related_name_` varchar(255) DEFAULT NULL COMMENT '关联名称', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁版本号', + `enable_app_` tinyint DEFAULT NULL COMMENT '支持手机端 0:否,1:是', + `type_code_` varchar(50) DEFAULT NULL COMMENT '系统树编码', + PRIMARY KEY (`id_`), + KEY `index_code` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自定义列表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_cust_grid_field` +-- + +DROP TABLE IF EXISTS `biz_cust_grid_field`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_cust_grid_field` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `grid_id_` varchar(64) DEFAULT NULL COMMENT '表ID', + `name_` varchar(50) DEFAULT NULL COMMENT '数据库字段名称', + `field_name_` varchar(50) DEFAULT NULL COMMENT '字段AS名称', + `field_desc_` varchar(500) DEFAULT NULL COMMENT '字段备注', + `table_name_` varchar(50) DEFAULT NULL COMMENT '字段对应的表名', + `table_alias_` varchar(64) DEFAULT NULL COMMENT '字段对应的表别名', + `align_` varchar(10) DEFAULT NULL COMMENT '对齐方式', + `hidden_` tinyint DEFAULT NULL COMMENT '是否隐藏', + `unit_` varchar(500) DEFAULT NULL COMMENT '单位', + `width_` varchar(10) DEFAULT NULL COMMENT '列宽', + `sn_` tinyint DEFAULT NULL COMMENT '排序', + `format_type_` varchar(20) DEFAULT NULL COMMENT '格式化类型', + `format_conf_` varchar(2000) DEFAULT NULL COMMENT '格式化配置', + `data_type_` varchar(20) DEFAULT NULL COMMENT '数据类型', + `css_conf_` varchar(2000) DEFAULT NULL COMMENT '样式格式化配置', + `href_conf_` varchar(2000) DEFAULT NULL COMMENT 'href配置', + `addition_conf_` varchar(2000) DEFAULT NULL COMMENT '其他配置(行宽等)', + `sort_able_` tinyint DEFAULT NULL COMMENT '可排序', + `export_able_` tinyint DEFAULT NULL COMMENT '可导出', + `fuzz_` varchar(16) DEFAULT NULL COMMENT '脱敏 如3,4', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自定义列表字段'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_cust_grid_form_relation` +-- + +DROP TABLE IF EXISTS `biz_cust_grid_form_relation`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_cust_grid_form_relation` ( + `id_` varchar(64) NOT NULL COMMENT 'id', + `sql_id_` varchar(64) NOT NULL COMMENT '列表ID', + `form_code_` varchar(64) NOT NULL COMMENT '表单code', + `form_name_` varchar(126) DEFAULT NULL COMMENT '表单名', + `bo_code_` varchar(64) DEFAULT NULL COMMENT '业务对象code', + `pre_show_script_` varchar(2000) DEFAULT NULL COMMENT '展示前置Groovy脚本', + `pre_save_script_` varchar(2000) DEFAULT NULL COMMENT '保存前置Groovy脚本', + `after_save_script_` varchar(2000) DEFAULT NULL COMMENT '保存后置Groovy脚本', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁版本号', + PRIMARY KEY (`id_`), + KEY `cust_grid_form_idx` (`form_code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='自定义SQL列表与表单关联表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_form` +-- + +DROP TABLE IF EXISTS `biz_form`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_form` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `type_` varchar(64) DEFAULT NULL COMMENT '分类(pc/mobile)', + `code_` varchar(128) DEFAULT NULL COMMENT 'CODE', + `name_` varchar(256) DEFAULT NULL COMMENT '名字', + `type_code_` varchar(128) DEFAULT NULL COMMENT '分类编码', + `design_code_` varchar(128) DEFAULT NULL COMMENT '设计编码', + `template_code_` varchar(128) DEFAULT NULL COMMENT '表单模板编码', + `bo_code_` varchar(128) DEFAULT NULL COMMENT '业务对象编码', + `bo_name_` varchar(256) DEFAULT NULL COMMENT '业务对象名称', + `html_` longtext COMMENT 'html', + `js_` longtext COMMENT 'js', + `status_` varchar(8) DEFAULT NULL COMMENT '状态:按照设计生成、源码修改', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + PRIMARY KEY (`id_`), + UNIQUE KEY `key_unique_idx` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务表单'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_form_combination` +-- + +DROP TABLE IF EXISTS `biz_form_combination`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_form_combination` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `code_` varchar(128) NOT NULL COMMENT '编码', + `name_` varchar(256) DEFAULT NULL COMMENT '名字', + `desc_` varchar(500) DEFAULT NULL COMMENT '描述', + `type_code_` varchar(128) DEFAULT NULL COMMENT '分类编码', + `design_json_` longtext COMMENT '设计JSON', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + PRIMARY KEY (`id_`), + UNIQUE KEY `key_unique_idx` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='组合联动式表单'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_form_design` +-- + +DROP TABLE IF EXISTS `biz_form_design`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_form_design` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `type_code_` varchar(128) DEFAULT NULL COMMENT '分类编码', + `code_` varchar(128) DEFAULT NULL COMMENT '编码', + `name_` varchar(256) DEFAULT NULL COMMENT '名称', + `desc_` varchar(256) DEFAULT NULL COMMENT '描述', + `bo_code_` varchar(128) DEFAULT NULL COMMENT '业务对象编码', + `bo_name_` varchar(256) DEFAULT NULL COMMENT '业务对象名称', + `mode_` varchar(8) DEFAULT NULL COMMENT '模式:按照业务对象生成、自主设计业务对象', + `form_mode_` varchar(8) DEFAULT NULL COMMENT '表单模式:移动端和PC端兼容模式、PC端、移动端、移动端和PC端', + `pc_json_` longtext COMMENT 'pc配置json', + `mb_json_` longtext COMMENT 'mb配置json', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '创建人所属机构', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + `pc_template_code_` varchar(128) DEFAULT NULL COMMENT 'PC端表单模板编码', + `mb_template_code_` varchar(128) DEFAULT NULL COMMENT '移动端表单模板编码', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表单设计表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_form_template` +-- + +DROP TABLE IF EXISTS `biz_form_template`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_form_template` ( + `id_` varchar(64) NOT NULL COMMENT 'id', + `code_` varchar(64) DEFAULT NULL COMMENT '编码', + `name_` varchar(128) DEFAULT NULL COMMENT '名字', + `form_type_` varchar(64) DEFAULT NULL COMMENT '表单类型', + `html_` text COMMENT 'HTML模板', + `desc_` varchar(400) DEFAULT NULL COMMENT '描述', + `editable_` tinyint DEFAULT NULL COMMENT '是否可以编辑', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='表单生成模版'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `biz_form_template` +-- + +LOCK TABLES `biz_form_template` WRITE; +/*!40000 ALTER TABLE `biz_form_template` DISABLE KEYS */; +INSERT INTO `biz_form_template` VALUES ('1626105045947330562','vueJs','表单 JS 模板','','window.custFormComponentMixin = {\r\n data: function () {\r\n return {\"user\":{\"name\":\"张珊\"}};\r\n },\r\n created : function(){\r\n console.log(\"脚本将会混入自定义表单组件中...\");\r\n },\r\n methods:{\r\n testaaa : function(){\r\n alert(1)\r\n },\r\n custValid:function(){ \r\n // 执行业务校验,return false则终止提交 \r\n return true; \r\n }\r\n }\r\n }','',0,'2023-02-16 14:23:49','1602918114232172634','1602918114232172544','2023-02-16 14:23:49','系统管理员','1602918114232172634'),('1651503631602376704','elementFlowDesign','流式风格模板','pc','\r\n\r\n <#list json.columns as column>\r\n ${getColumn(column)}\r\n \r\n\r\n\r\n<#-- 主表的计算函数 -->\r\n<#if json.showTablePlugins>\r\n\r\n ${subTableParser.getTablePlugins(json)}\r\n\r\n\r\n\r\n<#-- 第三层以上子表 -->\r\n<#list json.columns as column>\r\n<#if column.abType==\'subtableDialog\'>\r\n\r\n ${getSubTable(column)}\r\n\r\n\r\n\r\n\r\n<#-- 统一处理字段 -->\r\n<#function getColumn column>\r\n<#assign rtn>\r\n<#-- 字段 -->\r\n<#if column.abType==\'column\'>\r\n\r\n ${columnParser.getColumn(column)}\r\n\r\n\r\n\r\n<#-- 标题 -->\r\n<#if column.abType==\'title\'>\r\n

${column.text}

\r\n\r\n\r\n<#-- 分割线 -->\r\n<#if column.abType==\'divider\'>\r\n${column.text}\r\n\r\n\r\n<#-- 提示 -->\r\n<#if column.abType==\'alert\'>\r\n\r\n\r\n\r\n<#-- 组 -->\r\n<#if column.abType==\'group\'>\r\n\r\n \r\n \r\n <#list column.columns as c>\r\n ${getColumn(c)}\r\n \r\n \r\n\r\n\r\n\r\n<#-- tabs -->\r\n<#if column.abType==\'tabs\'>\r\n\r\n <#list column.tabsColumn as pane>\r\n \r\n \r\n <#list pane.columns as c>\r\n ${getColumn(c)}\r\n \r\n \r\n \r\n \r\n\r\n\r\n\r\n<#-- 第二层子表 -->\r\n<#if column.abType==\'subtable\'>\r\n${getSubTable(column)}\r\n\r\n\r\n<#return rtn>\r\n\r\n\r\n<#-- 处理子表 -->\r\n<#function getSubTable column>\r\n<#assign rtn>\r\nstyle=\"margin-bottom: 15px;width: 100%\" shadow=\"hover\" body-style=\"overflow:auto\">\r\n \r\n \r\n <#-- 块子表 -->\r\n <#if column.type==\'group\'>\r\n \r\n <#list subTableParser.getDialogChildren(column,json) as t>\r\n ${t.label}\r\n \r\n <#if column.haveBtn(\'del\')>\r\n移除\r\n \r\n <#-- 子表的计算函数 -->\r\n <#if column.showTablePlugins>\r\n \r\n ${subTableParser.getTablePlugins(column)}\r\n \r\n \r\n \r\n <#list column.columns as c>\r\n ${getColumn(c)}\r\n \r\n \r\n \r\n \r\n <#-- 行子表 -->\r\n <#else>\r\n \r\n \r\n \r\n <#if column.sn>\r\n \r\n \r\n <#list column.columns as c>\r\n \r\n \r\n \r\n \r\n \r\n \r\n \r\n <#if column.sn>\r\n \r\n \r\n <#list column.columns as c>\r\n \r\n \r\n \r\n \r\n \r\n
序号${subTableParser.getThLabel(c)}操作
{{index+1}}\r\n ${columnParser.getColumn(c)}\r\n \r\n \r\n <#list subTableParser.getDialogChildren(column,json) as t>\r\n ${t.label}\r\n \r\n <#if column.haveBtn(\'del\')>\r\n 移除\r\n \r\n \r\n <#-- 子表的计算函数 -->\r\n <#if column.showTablePlugins>\r\n \r\n ${subTableParser.getTablePlugins(column)}\r\n \r\n \r\n
\r\n \r\n
\r\n\r\n<#return rtn>\r\n','',0,'2023-04-27 16:28:44','1','1607311446916005888','2023-04-27 16:28:44','管理员','1'); +/*!40000 ALTER TABLE `biz_form_template` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `biz_object` +-- + +DROP TABLE IF EXISTS `biz_object`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_object` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `code_` varchar(64) DEFAULT NULL COMMENT 'CODE', + `name_` varchar(128) DEFAULT NULL COMMENT '名字', + `desc_` varchar(256) DEFAULT NULL COMMENT '描述', + `relation_` text COMMENT '业务关系Json字段', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分组key', + `persistence_type_` varchar(64) DEFAULT NULL COMMENT '持久化类型', + `per_type_config_` varchar(1024) DEFAULT NULL COMMENT '持久化类型的配置内容', + `save_index_` tinyint DEFAULT NULL COMMENT '是否维护索引', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `table_codes_` varchar(512) DEFAULT NULL COMMENT '关联的表编码', + `interface_list_` text COMMENT '服务接口定义', + PRIMARY KEY (`id_`), + UNIQUE KEY `key_unique_idx` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务对象'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_pattern` +-- + +DROP TABLE IF EXISTS `biz_pattern`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_pattern` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人', + `code_` varchar(64) DEFAULT NULL COMMENT '模型Code', + `name_` varchar(128) DEFAULT NULL COMMENT '名称', + `message_` varchar(256) DEFAULT NULL COMMENT '校验器失败时的内容', + `java_pattern_` varchar(512) DEFAULT NULL, + `ui_pattern_` varchar(512) DEFAULT NULL, + PRIMARY KEY (`id_`), + UNIQUE KEY `key_unique_idx` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务正则表达式'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `biz_pattern` +-- + +LOCK TABLES `biz_pattern` WRITE; +/*!40000 ALTER TABLE `biz_pattern` DISABLE KEYS */; +INSERT INTO `biz_pattern` VALUES ('1602918114232172550','2022-12-14 14:47:09','1','2022-12-14 14:47:09','1','digits','整数','只能整数','^\\d+$','^\\d+$'),('1602918114232172564','2022-12-14 14:47:09','1','2022-12-14 14:47:09','1','varirule','以字母开头','只能以字母开头,允许字母、数字和下划线','^[a-zA-Z]\\w*$','^[a-zA-Z]\\w*$'),('1602918114232172568','2022-12-14 14:47:09','1','2022-12-14 14:47:09','1','required','必填','必填','\\S','\\S'),('1602918114232172578','2022-12-14 14:47:09','1','2022-12-14 14:47:09','1','chinese','中文','只能中文字符','[\\u4E00-\\u9FFF]','[\\u4E00-\\u9FFF]'),('1605572156937330688','2022-12-21 22:33:27','1','2022-12-21 22:33:27','1','bfb','百分比(数字0-100)','请输入1-100数字','^([0-9]|[0-9]\\d|100)$','^([0-9]|[0-9]\\d|100)$'),('1605575215788711936','2022-12-21 22:45:36','1','2022-12-21 22:45:36','1','zmhxhx','字母或下划线','只能是字母和下划线','^[A-Za-z_]*$','^[A-Za-z_]*$'),('1605756212840878080','2022-12-22 10:44:49','1','2023-01-10 17:33:35','1','zmhsz','账号 (首字符为字母后内容不得用_外的特殊字符)','首字符为字母,最大长度18','^[A-Za-z]{1}([a-zA-Z0-9_]{1,17})?$','^[A-Za-z]{1}([a-zA-Z0-9_]{1,17})?$'),('1605757136577613824','2022-12-22 10:48:30','1','2022-12-22 10:48:30','1','number','数字','只能输入数字','^-?((\\d{1,3}(,\\d{3})+?|\\d+)(\\.\\d{1,5})?)$','^-?((\\d{1,3}(,\\d{3})+?|\\d+)(\\.\\d{1,5})?)$'),('1605758133022609408','2022-12-22 10:52:27','1','2023-01-28 12:24:10','1','minlength','邮箱','请输入合法的邮箱地址','^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$','^[a-zA-Z0-9_-]+@[a-zA-Z0-9_-]+(\\.[a-zA-Z0-9_-]+)+$'),('1622532233006817280','2023-02-06 17:46:44','1','2023-02-06 17:46:44','1','hasChinese','包含汉字','内容不包含汉字','[\\u4E00-\\u9FA5\\uF900-\\uFA2D]','[\\u4E00-\\u9FA5\\uF900-\\uFA2D]'),('1622532920868478976','2023-02-06 17:49:28','1','2023-02-06 17:49:28','1','QQ','QQ号','请输入正确的QQ号','[1-9]*[1-9][0-9]*$','[1-9]*[1-9][0-9]*$'),('1622533616976142336','2023-02-06 17:52:14','1','2023-02-06 17:52:14','1','telephone','手机号码','请输入正确的手机号码','((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)','((\\d{11})|^((\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})|(\\d{4}|\\d{3})-(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1})|(\\d{7,8})-(\\d{4}|\\d{3}|\\d{2}|\\d{1}))$)'),('1622535544011366400','2023-02-06 17:59:54','1','2023-02-06 17:59:54','1','phone','带区号座机电话','请输入带区号的座机号码(以-分割)','\\d{3}-\\d{8}|\\d{4}-\\d{7}','\\d{3}-\\d{8}|\\d{4}-\\d{7}'),('1622536284809338880','2023-02-06 18:02:50','1','2023-02-06 18:02:50','1','postalCode','邮政编码','请输入六位邮政编码','[1-9]\\d{5}(?!\\d)','[1-9]\\d{5}(?!\\d)'),('1622538120928829440','2023-02-06 18:10:08','1','2023-02-06 18:10:08','1','IP','IP地址','请输入正确的IP地址','(^((2[0-4]\\d.)|(25[0-5].)|(1\\d{2}.)|(\\d{1,2}.))((2[0-5]{2}.)|(1\\d{2}.)|(\\d{1,2}.){2})((1\\d{2})|(2[0-5]{2})|(\\d{1,2})))','(^((2[0-4]\\d.)|(25[0-5].)|(1\\d{2}.)|(\\d{1,2}.))((2[0-5]{2}.)|(1\\d{2}.)|(\\d{1,2}.){2})((1\\d{2})|(2[0-5]{2})|(\\d{1,2})))'); +/*!40000 ALTER TABLE `biz_pattern` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `biz_permission` +-- + +DROP TABLE IF EXISTS `biz_permission`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_permission` ( + `id_` varchar(64) NOT NULL, + `type_` varchar(64) DEFAULT NULL COMMENT '配置这个权限的对象,可以是表单,流程,或流程节点', + `value_` varchar(128) DEFAULT NULL COMMENT '能获取到配置权限的对象的唯一值\r\n 通常是key 或 id \r\n 可以是自定义的\r\n 例如 某个流程的某个节点,可以是 流程key.nodeKey\r\n 这样的格式\r\n', + `config_` longtext COMMENT '权限配置的内容', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`), + UNIQUE KEY `obj_type_obj_val_unique_idx_` (`type_`,`value_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务权限'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_table` +-- + +DROP TABLE IF EXISTS `biz_table`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_table` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `code_` varchar(64) DEFAULT NULL COMMENT '模型Code', + `name_` varchar(64) DEFAULT NULL COMMENT '表名', + `comment_` varchar(256) DEFAULT NULL COMMENT '描述', + `ds_key_` varchar(64) DEFAULT NULL COMMENT '数据源的别名', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分组KEY', + `external_` tinyint DEFAULT NULL COMMENT '是否外部表', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人', + PRIMARY KEY (`id_`), + UNIQUE KEY `key_unique_idx` (`code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='业务模型'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `biz_word_template` +-- + +DROP TABLE IF EXISTS `biz_word_template`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `biz_word_template` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(128) DEFAULT NULL COMMENT '名字', + `code_` varchar(64) DEFAULT NULL COMMENT '别名/编码', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类字典编码', + `desc_` varchar(256) DEFAULT NULL COMMENT '描述', + `bo_code_` varchar(64) DEFAULT NULL COMMENT '业务对象code', + `bo_name_` varchar(128) DEFAULT NULL COMMENT '业务对象名称', + `template_text` longtext COMMENT '模板内容', + `is_main_` tinyint(1) DEFAULT NULL COMMENT '是否是主版本(0否 1是)', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `version_` int DEFAULT NULL COMMENT '版本号', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='world模板'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_biz_relation` +-- + +DROP TABLE IF EXISTS `bpm_biz_relation`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_biz_relation` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `def_id_` varchar(64) DEFAULT NULL COMMENT '定义ID', + `inst_id_` varchar(64) DEFAULT NULL COMMENT '实例ID', + `biz_id_` varchar(64) DEFAULT NULL COMMENT '业务ID', + `biz_object_code_` varchar(64) NOT NULL COMMENT '业务对象编码', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + PRIMARY KEY (`id_`,`biz_object_code_`), + KEY `link_inst_id_idx` (`inst_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流程实例与业务数据关系表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_bytearray` +-- + +DROP TABLE IF EXISTS `bpm_bytearray`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_bytearray` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `category_` varchar(50) NOT NULL COMMENT '类别', + `bytes_` longblob COMMENT '字节数据', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='BPM字节数组'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_definition` +-- + +DROP TABLE IF EXISTS `bpm_definition`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_definition` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(64) NOT NULL COMMENT '名字', + `key_` varchar(64) NOT NULL COMMENT '流程KEY', + `desc_` varchar(500) DEFAULT NULL COMMENT '描述', + `ACT_DEF_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti 定义ID', + `ACT_MODEL_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti 模型ID', + `ACT_DEPLOY_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti 部署ID', + `status_` varchar(64) DEFAULT NULL COMMENT '状态', + `version_` int DEFAULT NULL COMMENT '版本号', + `support_mobile_` tinyint DEFAULT '0' COMMENT '是否支持移动端', + `is_main_` tinyint DEFAULT NULL COMMENT '是否主版本', + `icon_style_` varchar(64) DEFAULT NULL COMMENT 'ICON配置', + `hidden_` tinyint DEFAULT '0' COMMENT '是否隐藏,默认否,隐藏后将不在个人办公-发起申请页面展示,但依然可以通过其他方式发起流程', + `sn_` int DEFAULT '10' COMMENT 'SN排序', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类字典KEY', + `process_editor_` varchar(64) DEFAULT NULL COMMENT '流程设计器', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + PRIMARY KEY (`id_`), + KEY `bpm_process_def_key` (`key_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='流程定义'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_instance` +-- + +DROP TABLE IF EXISTS `bpm_instance`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_instance` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `title_` varchar(255) NOT NULL COMMENT '标题', + `def_id_` varchar(64) NOT NULL COMMENT '定义ID', + `ACT_INST_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti实例ID', + `parent_id_` varchar(64) DEFAULT NULL COMMENT '父实例Id', + `def_key_` varchar(64) DEFAULT NULL COMMENT '流程Key', + `biz_id_` varchar(64) DEFAULT NULL COMMENT '业务ID', + `status_` varchar(64) DEFAULT NULL COMMENT '状态', + `duration_ms_` bigint DEFAULT NULL COMMENT '持续时间', + `support_mobile_` tinyint DEFAULT '0' COMMENT '是否支持移动端', + `suspension_state_` tinyint DEFAULT NULL COMMENT '挂起状态', + `super_task_def_key_` varchar(50) DEFAULT NULL COMMENT '父流程定义节点KEY', + `calling_convention_` varchar(255) DEFAULT NULL COMMENT '调用约定', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类字典KEY', + `summary_` varchar(500) DEFAULT NULL COMMENT '流程概要', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `end_time_` datetime DEFAULT NULL COMMENT '完成时间', + `creator_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `rev_` int DEFAULT NULL COMMENT '乐观锁', + PRIMARY KEY (`id_`), + KEY `idx_inst_ACT_INST_ID_` (`ACT_INST_ID_`), + KEY `idx_inst_parent_id_` (`parent_id_`), + KEY `idx_inst_biz_key_` (`biz_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='实例'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_task` +-- + +DROP TABLE IF EXISTS `bpm_task`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_task` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(64) NOT NULL COMMENT '名字', + `node_key_` varchar(64) DEFAULT NULL COMMENT '任务KEY', + `title_` varchar(255) NOT NULL COMMENT '标题-冗余', + `inst_id_` varchar(64) NOT NULL COMMENT '实例ID', + `ACT_TASK_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti 任务ID', + `ACT_EXECUTION_ID_` varchar(64) DEFAULT NULL COMMENT 'Activiti 执行id', + `def_id_` varchar(64) NOT NULL COMMENT '定义ID', + `status_arr_` varchar(256) NOT NULL COMMENT '状态列表', + `suspension_state_` tinyint DEFAULT NULL COMMENT '挂起状态', + `assignee_names_` varchar(500) DEFAULT NULL COMMENT '任务执行人说明', + `priority_` tinyint DEFAULT '50' COMMENT '紧急程度', + `support_mobile_` tinyint DEFAULT '0' COMMENT '是否支持移动端', + `back_node_` varchar(64) DEFAULT NULL COMMENT '驳回后返回节点', + `due_time_` datetime DEFAULT NULL COMMENT '到期时间', + `task_type_` varchar(64) DEFAULT NULL COMMENT '任务类型', + `parent_id_` varchar(64) DEFAULT NULL COMMENT '父任务ID', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类编码', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_time_` datetime(6) DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `create_time_` datetime(3) DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(3), + PRIMARY KEY (`id_`), + KEY `idx_task_instid` (`inst_id_`), + KEY `idx_task_parentid` (`parent_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_task_identitylink` +-- + +DROP TABLE IF EXISTS `bpm_task_identitylink`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_task_identitylink` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `task_id_` varchar(64) NOT NULL COMMENT '任务ID', + `inst_id_` varchar(64) NOT NULL COMMENT '实例ID', + `identity_type_` varchar(20) DEFAULT NULL COMMENT '候选人类型', + `identity_name_` varchar(64) DEFAULT NULL COMMENT '名字', + `identity_id_` varchar(64) DEFAULT NULL COMMENT '候选人ID', + `permission_code_` varchar(64) NOT NULL COMMENT '候选人编码', + `status_` varchar(64) DEFAULT 'default' COMMENT '状态(lock锁定,agency代理,default)lock状态', + PRIMARY KEY (`id_`), + KEY `idx_taskcandidate_taskid` (`task_id_`), + KEY `idx_identitylink_instid` (`inst_id_`), + KEY `idx_permission_code_` (`permission_code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务候选人'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_task_opinion` +-- + +DROP TABLE IF EXISTS `bpm_task_opinion`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_task_opinion` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `inst_id_` varchar(64) NOT NULL COMMENT '流程实例ID', + `ACT_EXECUTION_ID_` varchar(64) DEFAULT NULL COMMENT 'ACT执行栈', + `task_id_` varchar(64) DEFAULT NULL COMMENT '任务ID', + `task_def_key_` varchar(64) DEFAULT NULL COMMENT '任务定义Key', + `task_type_` varchar(64) DEFAULT NULL COMMENT '任务类型', + `task_name_` varchar(255) DEFAULT NULL COMMENT '任务名称', + `assign_info_` varchar(2000) DEFAULT NULL COMMENT '任务分配情况', + `approver_` varchar(64) DEFAULT NULL COMMENT '审批人', + `approver_org_id_` varchar(64) DEFAULT NULL COMMENT '审批人组织ID', + `approver_name_` varchar(64) DEFAULT NULL COMMENT '审批人名字', + `approve_time_` datetime(3) DEFAULT NULL COMMENT '审批时间', + `opinion_` varchar(2000) DEFAULT NULL COMMENT '意见', + `status_` varchar(64) NOT NULL COMMENT '状态', + `trace_` varchar(64) DEFAULT NULL COMMENT '意见线路', + `parent_task_id_` varchar(64) DEFAULT NULL COMMENT '父任务ID', + `attachment_` varchar(500) DEFAULT NULL COMMENT '附件信息', + `duration_ms_` bigint DEFAULT NULL COMMENT '持续时间', + `due_time_` datetime DEFAULT NULL COMMENT '到期时间', + `create_by_` varchar(255) DEFAULT NULL COMMENT '创建人', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + PRIMARY KEY (`id_`), + KEY `idx_opinion_task` (`task_id_`), + KEY `idx_opinion_approver` (`approver_`), + KEY `idx_opinion_instId` (`inst_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务审批记录'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `bpm_task_stack` +-- + +DROP TABLE IF EXISTS `bpm_task_stack`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `bpm_task_stack` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `task_id_` varchar(64) NOT NULL COMMENT '任务ID', + `inst_id_` varchar(64) DEFAULT NULL COMMENT '实例ID', + `parent_id_` varchar(64) DEFAULT NULL COMMENT 'ParentId', + `task_def_key_` varchar(64) NOT NULL COMMENT '任务key', + `task_name_` varchar(125) DEFAULT NULL COMMENT '任务名', + `node_type_` varchar(64) DEFAULT NULL COMMENT '节点类型', + `trace_` varchar(64) DEFAULT NULL COMMENT '线路', + `action_name_` varchar(255) DEFAULT NULL COMMENT '动作名', + PRIMARY KEY (`id_`), + KEY `idx_exestack_instid` (`inst_id_`), + KEY `idx_exestack_taskid` (`task_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务堆栈记录'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_comments` +-- + +DROP TABLE IF EXISTS `cms_comments`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_comments` ( + `id_` varchar(64) NOT NULL COMMENT '评论纪录主键', + `msg_id_` varchar(64) DEFAULT NULL COMMENT '公告或新闻纪录主键', + `comment_type_` tinyint DEFAULT NULL COMMENT '0:公告1:新闻', + `comment_content_` text COMMENT '评论内容', + `create_by_` varchar(64) DEFAULT NULL COMMENT '评论者主键', + `creator_` varchar(64) DEFAULT NULL COMMENT '评论人姓名', + `create_time_` datetime DEFAULT NULL COMMENT '评论发表时间', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='评论表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_fast_menu` +-- + +DROP TABLE IF EXISTS `cms_fast_menu`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_fast_menu` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `user_id_` varchar(64) NOT NULL COMMENT '归属人', + `resource_id_` varchar(64) NOT NULL COMMENT '资源id', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `mobile_` int DEFAULT NULL COMMENT '是否为移动端(0否 1是)', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='快捷菜单表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_frequent_used` +-- + +DROP TABLE IF EXISTS `cms_frequent_used`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_frequent_used` ( + `id_` varchar(64) NOT NULL COMMENT '常用流程id', + `def_id_` varchar(64) DEFAULT NULL COMMENT '流程id', + `user_id_` varchar(64) DEFAULT NULL COMMENT '用户id', + PRIMARY KEY (`id_`), + KEY `user_id` (`user_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='常用流程表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_news` +-- + +DROP TABLE IF EXISTS `cms_news`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_news` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `title_` varchar(100) NOT NULL COMMENT '新闻标题', + `content_` longtext COMMENT '新闻内容', + `images_` varchar(1000) DEFAULT NULL COMMENT '轮播图片文件信息', + `attachments_` varchar(1000) DEFAULT NULL COMMENT '附件信息', + `comments_num_` int DEFAULT '0' COMMENT '评论数量', + `visit_num_` int DEFAULT '0' COMMENT '访问数量', + `release_time_` datetime DEFAULT NULL COMMENT '发布时间', + `release_by_` varchar(64) DEFAULT NULL COMMENT '发布人或下架人ID', + `release_name_` varchar(64) DEFAULT NULL COMMENT '发布人或下架人名字', + `status_` tinyint DEFAULT '0' COMMENT '发布状态0未发布 1已发布', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='新闻表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_notify` +-- + +DROP TABLE IF EXISTS `cms_notify`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_notify` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `title_` varchar(100) NOT NULL COMMENT '公告标题', + `content_` text COMMENT '公告内容', + `type_id_` varchar(64) DEFAULT NULL COMMENT '公告类型主键', + `attachments_` varchar(1000) DEFAULT NULL COMMENT '附件json格式字符串信息', + `release_by_` varchar(64) DEFAULT NULL COMMENT '发布人或下架人ID根据状态来区分', + `release_name_` varchar(64) DEFAULT NULL COMMENT '发布人或下架人姓名', + `release_time_` datetime DEFAULT NULL COMMENT '发布时间或下架时间', + `status_` tinyint DEFAULT '0' COMMENT '发布状态0未发布1已发布2下架', + `comments_num_` int DEFAULT NULL COMMENT '评论数量', + `visit_num_` int DEFAULT '0' COMMENT '访问数量', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统公告表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_notify_share` +-- + +DROP TABLE IF EXISTS `cms_notify_share`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_notify_share` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `notify_id_` varchar(64) DEFAULT NULL COMMENT '公告id', + `group_id_` varchar(64) DEFAULT NULL COMMENT '部门id', + `group_name_` varchar(255) DEFAULT NULL COMMENT '部门名', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告发布对应部门表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `cms_notify_user` +-- + +DROP TABLE IF EXISTS `cms_notify_user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `cms_notify_user` ( + `id_` varchar(20) NOT NULL COMMENT '主键', + `notify_id_` varchar(64) DEFAULT NULL COMMENT '公告id', + `user_id_` varchar(255) DEFAULT NULL COMMENT '用户id', + `create_time_` datetime DEFAULT NULL COMMENT '(创建时间)阅读时间', + PRIMARY KEY (`id_`), + KEY `公告id` (`notify_id_`), + KEY `用户id` (`user_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公告用户关联表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `db_uploader` +-- + +DROP TABLE IF EXISTS `db_uploader`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `db_uploader` ( + `id_` varchar(64) NOT NULL, + `bytes_` longblob, + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `org_group` +-- + +DROP TABLE IF EXISTS `org_group`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `org_group` ( + `id_` varchar(64) NOT NULL COMMENT '主键', + `name_` varchar(64) NOT NULL COMMENT '名字', + `parent_id_` varchar(64) DEFAULT NULL COMMENT '父ID', + `sn_` int DEFAULT '100' COMMENT '排序', + `code_` varchar(64) NOT NULL, + `type_` varchar(64) DEFAULT NULL COMMENT '类型:0集团,1公司,3部门', + `desc_` varchar(500) DEFAULT NULL COMMENT '描述', + `path_` varchar(2000) DEFAULT NULL COMMENT '组织路径', + `path_name_` varchar(500) DEFAULT NULL COMMENT '组织路径', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`), + KEY `parent_id_` (`parent_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='组织架构'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `org_group` +-- + +LOCK TABLES `org_group` WRITE; +/*!40000 ALTER TABLE `org_group` DISABLE KEYS */; +INSERT INTO `org_group` VALUES ('1602918114232172544','AgileBPM','0',100,'agilebpm','0','AgileBPM','1602918114232172544','AgileBPM','2022-12-14 14:47:09','1',NULL,'2022-12-21 15:46:46','系统管理员','1602918114232172634'),('1605469760833257472','深圳分公司','1602918114232172544',1,'0755','1',NULL,'1602918114232172544.1605469760833257472','AgileBPM-深圳分公司','2022-12-21 15:46:34','1602918114232172634','','2023-07-04 15:35:51','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `org_group` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `org_relation` +-- + +DROP TABLE IF EXISTS `org_relation`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `org_relation` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `group_id_` varchar(64) DEFAULT NULL COMMENT '组ID', + `user_id_` varchar(64) DEFAULT NULL COMMENT '用户ID', + `is_master_` int DEFAULT '0' COMMENT '0:默认组织,1:从组织', + `role_id_` varchar(64) DEFAULT NULL COMMENT '角色ID', + `status_` int DEFAULT '1' COMMENT '状态:1启用,2禁用', + `type_` varchar(64) DEFAULT NULL COMMENT '类型:groupUser,groupRole,userRole,groupUserRole', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`), + KEY `FK_reference_21` (`user_id_`), + KEY `FK_reference_22` (`group_id_`), + KEY `FK_reference_23` (`role_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `org_relation` +-- + +LOCK TABLES `org_relation` WRITE; +/*!40000 ALTER TABLE `org_relation` DISABLE KEYS */; +INSERT INTO `org_relation` VALUES ('1605469760833257472_1605464158287495168','1605469760833257472',NULL,0,'1605464158287495168',1,'groupRole','2023-07-04 15:35:51','1602918114232172634','','2023-07-04 15:35:51','系统管理员','1602918114232172634'),('1610936016000094208','1602918114232172544','1602918114232172634',0,NULL,0,'groupUser','2023-01-09 09:31:56','1602918114232172634','','2023-07-04 14:39:44','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `org_relation` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `org_role` +-- + +DROP TABLE IF EXISTS `org_role`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `org_role` ( + `id_` varchar(64) NOT NULL, + `name_` varchar(64) NOT NULL COMMENT '角色名称', + `code_` varchar(64) NOT NULL COMMENT '编码', + `enabled_` int NOT NULL DEFAULT '1' COMMENT '0:禁用,1:启用', + `level_` int DEFAULT NULL COMMENT '角色等级', + `desc_` varchar(200) NOT NULL COMMENT '描述', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类字典编码', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='角色管理'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `org_role` +-- + +LOCK TABLES `org_role` WRITE; +/*!40000 ALTER TABLE `org_role` DISABLE KEYS */; +INSERT INTO `org_role` VALUES ('1605464158287495168','分级管理员','admin',1,0,'系统基础用户,仅能处理日常办公','mrjs','2022-12-21 15:24:18','1602918114232172634','','2022-12-21 15:50:32','系统管理员','1602918114232172634'),('1605464389708218368','普通用户','public',1,0,'','mrjs','2022-12-21 15:25:13','1602918114232172634','','2022-12-21 15:50:44','系统管理员','1602918114232172634'),('1610480371538956288','主管','manager',1,3,'','zw','2023-01-04 11:36:57','1602918114232172634','','2023-01-04 11:37:35','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `org_role` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `org_user` +-- + +DROP TABLE IF EXISTS `org_user`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `org_user` ( + `id_` varchar(64) NOT NULL, + `fullname_` varchar(255) NOT NULL COMMENT '姓名', + `account_` varchar(255) NOT NULL COMMENT '账号', + `password_` varchar(64) NOT NULL COMMENT '密码', + `email_` varchar(64) DEFAULT NULL COMMENT '邮箱', + `mobile_` varchar(32) DEFAULT NULL COMMENT '手机号码', + `weixin_` varchar(64) DEFAULT NULL COMMENT '微信号', + `address_` varchar(512) DEFAULT NULL COMMENT '地址', + `photo_` varchar(255) DEFAULT NULL COMMENT '头像', + `sex_` varchar(10) DEFAULT NULL COMMENT '性别:男,女,未知', + `signature_` varchar(50) DEFAULT NULL COMMENT '签名', + `from_` varchar(64) DEFAULT NULL COMMENT '来源', + `status_` int NOT NULL DEFAULT '1' COMMENT '0:禁用,1正常', + `openid_` varchar(64) DEFAULT NULL COMMENT 'openid', + `expire_date_` datetime DEFAULT NULL COMMENT '到期时间', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`), + KEY `account` (`account_`), + KEY `idx_mobile` (`mobile_`,`email_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `org_user` +-- + +LOCK TABLES `org_user` WRITE; +/*!40000 ALTER TABLE `org_user` DISABLE KEYS */; +INSERT INTO `org_user` VALUES ('1602918114232172634','系统管理员','admin','a4ayc/80/OGda4BO/1o/V0etpOqiLx1JwB5S3beHW0s=',NULL,NULL,NULL,NULL,'','未知',NULL,'system',1,NULL,NULL,'2022-12-14 14:47:09','1','','2023-07-04 14:35:29','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `org_user` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_application` +-- + +DROP TABLE IF EXISTS `sys_application`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_application` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(64) DEFAULT NULL COMMENT '应用名', + `code_` varchar(64) DEFAULT NULL COMMENT '编码', + `secret_` varchar(255) DEFAULT NULL COMMENT '密匙', + `resource_ids_` varchar(2000) DEFAULT NULL COMMENT '资源集合', + `scope_` varchar(300) DEFAULT NULL COMMENT '授权范围', + `refresh_token_validity_` int DEFAULT NULL COMMENT '刷新秒数', + `access_token_validity_` int DEFAULT NULL COMMENT '有效期', + `grant_types_` varchar(256) NOT NULL COMMENT '授权类型', + `autoapprove_` int DEFAULT NULL COMMENT '自动授权(0禁用 1启用)', + `authorities_` varchar(256) DEFAULT NULL COMMENT '权限', + `url_` varchar(500) DEFAULT NULL COMMENT '系统地址,空则为当前系统', + `redirect_uri_` varchar(500) DEFAULT NULL COMMENT '回调地址', + `open_type_` varchar(64) DEFAULT NULL COMMENT '打开方式', + `enabled_` tinyint DEFAULT NULL COMMENT '是否可用(0禁用 1启用)', + `is_default_` tinyint DEFAULT NULL COMMENT '是否默认打开(0非默认 1默认)', + `desc_` varchar(500) DEFAULT NULL COMMENT '描述备注', + `config_` varchar(2000) DEFAULT NULL COMMENT '扩展配置', + `app_type_` tinyint DEFAULT NULL COMMENT '0 web应用 1 移动端 2第三方应用', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='应用'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_application` +-- + +LOCK TABLES `sys_application` WRITE; +/*!40000 ALTER TABLE `sys_application` DISABLE KEYS */; +INSERT INTO `sys_application` VALUES ('1602918114232172552','敏捷流程开发平台','bpmDevPlatform','1',NULL,'all',360000,360000,'refresh_token,password,authorization_code,implicit,client_credentials',1,NULL,'http://test1.agilebpm.cn/',NULL,'0',1,1,'子系统','用于系统特殊配置',NULL,'2022-12-14 14:47:09','1','','2023-03-13 08:58:20','1602918114232172634','系统管理员'); +/*!40000 ALTER TABLE `sys_application` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_authorization` +-- + +DROP TABLE IF EXISTS `sys_authorization`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_authorization` ( + `rights_id_` varchar(64) NOT NULL COMMENT 'ID', + `rights_object_` varchar(64) NOT NULL COMMENT '授权对象表分区用 (业务类型)', + `rights_target_` varchar(64) NOT NULL COMMENT '授权目标ID', + `rights_type_` varchar(64) NOT NULL COMMENT '权限类型', + `rights_identity_` varchar(64) NOT NULL COMMENT '授权标识', + `rights_identity_name_` varchar(255) NOT NULL COMMENT '标识名字', + `rights_permission_code_` varchar(125) NOT NULL COMMENT '授权code=identity+type', + `rights_create_time_` datetime NOT NULL COMMENT '创建时间', + `rights_create_by_` varchar(64) NOT NULL COMMENT '创建人', + PRIMARY KEY (`rights_id_`), + KEY `idx_permission_code_` (`rights_permission_code_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='通用资源授权配置'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sys_configuration` +-- + +DROP TABLE IF EXISTS `sys_configuration`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_configuration` ( + `id_` varchar(64) NOT NULL, + `conf_type_` varchar(30) DEFAULT NULL COMMENT '配置类型', + `conf_env_` varchar(10) DEFAULT NULL COMMENT '配置所属环境', + `conf_json_` varchar(3000) DEFAULT NULL COMMENT '配置json', + `is_enable_` tinyint DEFAULT NULL COMMENT '是否可用', + `is_encrypt_` tinyint DEFAULT NULL COMMENT '是否加密', + `conf_name_` varchar(60) DEFAULT NULL COMMENT '配置名称', + `conf_comment_` varchar(300) DEFAULT NULL COMMENT '配置描述', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `update_by_` varchar(64) DEFAULT NULL COMMENT '修改人', + `update_time_` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 ROW_FORMAT=DYNAMIC; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_configuration` +-- + +LOCK TABLES `sys_configuration` WRITE; +/*!40000 ALTER TABLE `sys_configuration` DISABLE KEYS */; +INSERT INTO `sys_configuration` VALUES ('1','qywx','dev','{\"appId\":\"ww87c2829160c0cd11\",\"redirect_uri\":\"http://osscqkead4.a5.tongzhouyun.com/#/wxQyAuthorization\"}',1,0,NULL,NULL,NULL,NULL,NULL,NULL),('2','appQuickLoginType','dev','{\"loginType\":\"\",\"qywx\":\"/wxQyAuthorization\"}',1,0,NULL,NULL,NULL,NULL,NULL,NULL); +/*!40000 ALTER TABLE `sys_configuration` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_connect_record` +-- + +DROP TABLE IF EXISTS `sys_connect_record`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_connect_record` ( + `id_` varchar(20) NOT NULL, + `type_` varchar(64) DEFAULT NULL COMMENT '类型', + `source_id_` varchar(64) NOT NULL COMMENT '源ID', + `target_id_` varchar(64) NOT NULL COMMENT '关联ID', + `notice` varchar(500) DEFAULT NULL COMMENT '提示信息', + PRIMARY KEY (`id_`), + KEY `connect_source_id` (`source_id_`), + KEY `connect_target_id` (`target_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='公共业务关联记录'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sys_daily_phrases` +-- + +DROP TABLE IF EXISTS `sys_daily_phrases`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_daily_phrases` ( + `id_` varchar(64) NOT NULL COMMENT '主键id非空', + `locution_` varchar(255) DEFAULT NULL COMMENT '常用语文本', + `enable_` tinyint DEFAULT NULL COMMENT '是否启用(0禁用, 1启用)', + `is_default_` tinyint DEFAULT NULL COMMENT '是否默认 0自定义 1默认', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建者', + `creator_` varchar(64) DEFAULT NULL COMMENT '创建者姓名', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `update_time_` datetime DEFAULT NULL COMMENT '修改时间', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户常用语'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_daily_phrases` +-- + +LOCK TABLES `sys_daily_phrases` WRITE; +/*!40000 ALTER TABLE `sys_daily_phrases` DISABLE KEYS */; +INSERT INTO `sys_daily_phrases` VALUES ('1602918114232172592','会签同意',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'),('1602918114232172597','会签反对',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'),('1602918114232172602','同意',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'),('1602918114232172606','反对',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'),('1602918114232172612','情况紧急,请见快处理',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'),('1602918114232172613','请注意,情况复杂',1,1,'1','系统管理员','2022-12-14 14:47:09','2022-12-14 14:47:09'); +/*!40000 ALTER TABLE `sys_daily_phrases` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_data_dict` +-- + +DROP TABLE IF EXISTS `sys_data_dict`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_data_dict` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `parent_id_` varchar(64) DEFAULT NULL COMMENT '上级id', + `code_` varchar(255) NOT NULL COMMENT '编码', + `name_` varchar(255) NOT NULL COMMENT 'name', + `dict_key_` varchar(255) NOT NULL COMMENT '字典key', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分组字典编码', + `sn_` int DEFAULT NULL COMMENT '排序', + `dict_type_` varchar(10) NOT NULL COMMENT 'dict/node字典项', + `extend1` varchar(64) DEFAULT NULL COMMENT '扩展字段1', + `extend2` varchar(64) DEFAULT NULL COMMENT '扩展字段2', + `is_system_` tinyint DEFAULT NULL COMMENT '是否系统内置', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='数据字典'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_data_dict` +-- + +LOCK TABLES `sys_data_dict` WRITE; +/*!40000 ALTER TABLE `sys_data_dict` DISABLE KEYS */; +INSERT INTO `sys_data_dict` VALUES ('1602918110000000001','0','dictType','数据字典分类','dictType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-21 14:57:26','系统管理员','1602918114232172634'),('1602918110000000002','1602918110000000001','systemDefault','系统内置','dictType','dictType',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000003','1602918110000000001','bus','业务相关','dictType','dictType',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:44:11','系统管理员','1602918114232172634'),('1602918110000000004','1602918110000000001','yhxg','用户相关','dictType','dictType',1,'node',NULL,NULL,0,'2022-12-21 11:44:41','1602918114232172634','','2022-12-21 11:44:41','系统管理员','1602918114232172634'),('1602918110000000005','1602918110000000001','ht','合同','dictType','systemDefault',1,'node',NULL,NULL,0,'2023-01-09 14:59:02','1602918114232172634','1602918114232172544','2023-01-09 14:59:02','系统管理员','1602918114232172634'),('1602918110000000006','0','gglx','公告类型','gglx','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000007','1602918110000000006','zdtz','重大通知','gglx','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000008','1602918110000000006','gg','公告','gglx','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000009','1602918110000000006','fjtz','放假通知','gglx','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000010','0','fileType','附件分类','fileType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000011','1602918110000000010','ywfj','业务附件','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000012','1602918110000000010','xtfj','系统附件','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000013','1602918110000000012','news','新闻','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000014','1602918110000000012','notice','公告','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000015','1602918110000000012','user','用户','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000016','1602918110000000012','document','文档库','fileType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000017','0','property','系统属性分类','property','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000018','1602918110000000017','sysProperty','系统参数','property','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000019','1602918110000000017','lcxg','流程配置','property','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2023-01-09 18:45:27','系统管理员','1602918114232172634'),('1602918110000000020','1602918110000000017','yjxg','邮件配置','property','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2023-01-09 18:45:15','系统管理员','1602918114232172634'),('1602918110000000021','1602918110000000017','dlpz','登陆配置','property','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000022','1602918110000000017','bdxg','业务表单配置','property','systemDefault',1,'node',NULL,NULL,0,'2023-01-09 17:40:26','1602918114232172634','1602918114232172544','2023-01-09 18:44:43','系统管理员','1602918114232172634'),('1602918110000000023','1602918110000000017','qywx','企业微信配置','property','systemDefault',1,'node',NULL,NULL,0,'2023-01-09 18:31:56','1602918114232172634','1602918114232172544','2023-01-09 18:44:56','系统管理员','1602918114232172634'),('1602918110000000024','1602918110000000017','ddpzxg','钉钉配置配置','property','systemDefault',1,'node',NULL,NULL,0,'2023-01-09 18:40:23','1602918114232172634','1602918114232172544','2023-01-09 18:45:04','系统管理员','1602918114232172634'),('1602918110000000025','1602918110000000017','aly','阿里云配置','property','systemDefault',1,'node',NULL,NULL,0,'2023-01-09 18:44:04','1602918114232172634','1602918114232172544','2023-01-09 18:44:04','系统管理员','1602918114232172634'),('1602918110000000026','0','jsfl','角色分类','jsfl','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000027','1602918110000000026','mrjs','默认角色','jsfl','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000028','1602918110000000026','gz','工种','jsfl','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 14:51:54','1602918114232172634','','2022-12-21 14:51:54','系统管理员','1602918114232172634'),('1602918110000000029','1602918110000000026','zw','职务','jsfl','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 14:52:01','1602918114232172634','','2022-12-21 14:52:01','系统管理员','1602918114232172634'),('1602918110000000030','0','homeType','首页组件分类','homeType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000031','1602918110000000030','mrfl','默认分类','homeType','systemDefault',1,'node',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000032','1602918110000000030','ywfl','业务分类','homeType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000033','0','biz','视图分类','biz','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000034','1602918110000000033','mrfl','默认分类','biz','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000035','1602918110000000033','mr','办公流程','biz','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:42:20','系统管理员','1602918114232172634'),('1602918110000000036','1602918110000000033','ywlc','业务流程','biz','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 11:42:11','1602918114232172634','','2022-12-21 11:42:11','系统管理员','1602918114232172634'),('1602918110000000037','0','dataPrivilege','数据权限分类','dataPrivilege','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000038','1602918110000000037','lc','流程管理','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:40:09','系统管理员','1602918114232172634'),('1602918110000000039','1602918110000000037','zz','用户组织','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:40:21','系统管理员','1602918114232172634'),('1602918110000000040','1602918110000000037','bd','表单管理','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:40:28','系统管理员','1602918114232172634'),('1602918110000000041','1602918110000000037','cms','内容管理','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:40:36','系统管理员','1602918114232172634'),('1602918110000000042','1602918110000000037','xt','系统配置','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:40:45','系统管理员','1602918114232172634'),('1602918110000000043','1602918110000000037','zdylb','应用列表','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:41:15','系统管理员','1602918114232172634'),('1602918110000000044','1602918110000000037','zdydhk','自定义对话框','dataPrivilege','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 14:56:45','系统管理员','1602918114232172634'),('1602918110000000045','0','crontab','系统定时计划','crontab','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000046','1602918110000000045','ywxg','业务相关','crontab','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 11:38:26','1602918114232172634','','2022-12-21 11:38:26','系统管理员','1602918114232172634'),('1602918110000000047','1602918110000000045','lcxg','流程相关','crontab','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000048','0','auditLogType','审计日志分类','auditLogType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000049','1602918110000000048','myOffice','个人办公','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000050','1602918110000000048','CMS','内容管理','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:36:53','系统管理员','1602918114232172634'),('1602918110000000051','1602918110000000048','sysSettings','系统配置','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000052','1602918110000000048','orgManger','用户组织','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000053','1602918110000000048','formManager','表单管理','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000054','1602918110000000048','flowManager','流程管理','auditLogType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000055','0','holidaySystemType','节假日系统分类','holidaySystemType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000056','1602918110000000055','public','公有','holidaySystemType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000057','1602918110000000055','agilebpm','AgileBPM','holidaySystemType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000058','0','flowType','流程分类','flowType','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000059','1602918110000000058','bglc','办公流程','flowType','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 11:36:00','1602918114232172634','','2022-12-21 11:36:00','系统管理员','1602918114232172634'),('1602918110000000060','1602918110000000058','ywlc','业务流程','flowType','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 11:36:08','1602918114232172634','','2022-12-21 11:36:08','系统管理员','1602918114232172634'),('1602918110000000061','0','script','常用脚本分类','script','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000062','1602918110000000061','systemDefault','系统内置','script','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-21 11:29:49','系统管理员','1602918114232172634'),('1602918110000000063','1602918110000000061','currentUser','当前用户','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:13:40','1602918114232172634','','2023-01-03 19:13:40','系统管理员','1602918114232172634'),('1602918110000000064','1602918110000000061','executeSQL','执行SQL','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:14:32','1602918114232172634','','2023-01-03 19:14:32','系统管理员','1602918114232172634'),('1602918110000000065','1602918110000000061','operationBusinessObject','业务对象操作','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:15:22','1602918114232172634','','2023-01-03 19:15:22','系统管理员','1602918114232172634'),('1602918110000000066','1602918110000000061','getGroupInformation','获取组信息','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:16:04','1602918114232172634','','2023-01-03 19:16:04','系统管理员','1602918114232172634'),('1602918110000000067','1602918110000000061','getCandidatesSQL','获取候选人SQL','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:17:15','1602918114232172634','','2023-01-03 19:17:15','系统管理员','1602918114232172634'),('1602918110000000068','1602918110000000061','setCandidate','设置候选人','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:17:49','1602918114232172634','','2023-01-03 19:17:49','系统管理员','1602918114232172634'),('1602918110000000069','1602918110000000061','operationsProcessRelated','流程相关操作','script','systemDefault',1,'node',NULL,NULL,0,'2023-01-03 19:18:37','1602918114232172634','','2023-01-03 19:18:37','系统管理员','1602918114232172634'),('1602918110000000070','0','environment','环境分类','environment','systemDefault',NULL,'dict',NULL,NULL,1,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000071','1602918110000000070','DEV','开发','environment','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000072','1602918110000000070','SIT','测试','environment','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000073','1602918110000000070','GRAY','灰度','environment','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000074','1602918110000000070','PROD','生产','environment','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918110000000075','0','sex','性别','sex','yhxg',NULL,'dict',NULL,NULL,0,'2022-12-21 11:45:08','1602918114232172634','','2022-12-21 11:45:08','系统管理员','1602918114232172634'),('1602918110000000076','1602918110000000075','male','男','sex','yhxg',2,'node',NULL,NULL,0,'2022-12-21 11:45:45','1602918114232172634','','2023-07-03 09:23:17','系统管理员','1602918114232172634'),('1602918110000000077','1602918110000000075','female','女','sex','yhxg',1,'node',NULL,NULL,0,'2022-12-21 11:46:11','1602918114232172634','','2023-07-03 09:23:17','系统管理员','1602918114232172634'),('1602918110000000078','0','idType','证件类型','idType','yhxg',NULL,'dict',NULL,NULL,0,'2022-12-21 11:46:41','1602918114232172634','','2022-12-21 11:46:41','系统管理员','1602918114232172634'),('1602918110000000079','1602918110000000078','idCard','身份证','idType','yhxg',1,'node',NULL,NULL,0,'2022-12-21 11:47:16','1602918114232172634','','2022-12-21 11:47:16','系统管理员','1602918114232172634'),('1602918110000000080','1602918110000000078','militaryID','军官证','idType','yhxg',1,'node',NULL,NULL,0,'2022-12-21 11:47:49','1602918114232172634','','2022-12-21 11:47:49','系统管理员','1602918114232172634'),('1602918110000000081','1602918110000000078','passport','护照','idType','yhxg',1,'node',NULL,NULL,0,'2022-12-21 11:48:15','1602918114232172634','','2022-12-21 11:48:15','系统管理员','1602918114232172634'),('1602918110000000082','0','bizCommonTree','组联式表单分类','bizCommonTree','systemDefault',NULL,'dict',NULL,NULL,0,'2022-12-21 16:44:43','1602918114232172634','','2022-12-21 16:44:43','系统管理员','1602918114232172634'),('1602918110000000083','1602918110000000082','mrfl','默认分类','bizCommonTree','systemDefault',1,'node',NULL,NULL,0,'2022-12-21 16:45:00','1602918114232172634','','2022-12-21 16:45:00','系统管理员','1602918114232172634'),('1602918110000000084','1602918110000000010','mrfl','默认分类','fileType','systemDefault',1,'node',NULL,NULL,0,'2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1632923603478065152','0','groupGrade','组织级别','groupGrade','systemDefault',NULL,'dict',NULL,NULL,1,'2023-03-07 09:58:20','1','410054569125740545','2023-03-07 09:59:02','管理员','1'),('1632932326728253440','1632923603478065152','0','0-集团','groupGrade','systemDefault',1,'node',NULL,NULL,1,'2023-03-07 10:33:00','1','410054569125740545','2023-03-07 10:33:00','管理员','1'),('1632932388577460224','1632923603478065152','1','1-公司','groupGrade','systemDefault',1,'node',NULL,NULL,1,'2023-03-07 10:33:15','1','410054569125740545','2023-03-07 10:33:15','管理员','1'),('1632932431158034432','1632923603478065152','3','3-部门','groupGrade','systemDefault',1,'node',NULL,NULL,1,'2023-03-07 10:33:25','1','410054569125740545','2023-03-07 10:33:25','管理员','1'),('1632932482961883136','1632923603478065152','5','5-班组','groupGrade','systemDefault',1,'node',NULL,NULL,1,'2023-03-07 10:33:37','1','410054569125740545','2023-03-07 10:33:37','管理员','1'); +/*!40000 ALTER TABLE `sys_data_dict` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_file` +-- + +DROP TABLE IF EXISTS `sys_file`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_file` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(64) DEFAULT NULL COMMENT '附件名', + `uploader_` varchar(128) DEFAULT NULL COMMENT '上传器', + `path_` varchar(256) DEFAULT NULL COMMENT '附件路径', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分类编码', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + `delete_` tinyint DEFAULT NULL COMMENT '逻辑删除标记', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统附件'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sys_log_err` +-- + +DROP TABLE IF EXISTS `sys_log_err`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_log_err` ( + `id_` varchar(50) NOT NULL COMMENT 'ID', + `account_` varchar(20) DEFAULT NULL COMMENT '帐号', + `full_name_` varchar(64) DEFAULT '' COMMENT '操作用户名称', + `ip_` varchar(20) DEFAULT NULL COMMENT 'IP', + `ip_address_` varchar(255) DEFAULT NULL COMMENT 'IP归属地', + `status_` varchar(20) DEFAULT 'unchecked' COMMENT '状态:unchecked,checked,fixed', + `url_` varchar(500) DEFAULT NULL COMMENT '错误URL', + `content_` text COMMENT '出错信息', + `heads_` text COMMENT '请求Head', + `request_param_` text COMMENT '请求参数', + `create_time_` datetime DEFAULT NULL COMMENT '出错时间', + `stack_trace_` longtext COMMENT '出错异常堆栈', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统异常日志'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Table structure for table `sys_properties` +-- + +DROP TABLE IF EXISTS `sys_properties`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_properties` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `code_` varchar(64) DEFAULT NULL COMMENT '属性编码', + `name_` varchar(64) DEFAULT NULL COMMENT '属性名字', + `value_` varchar(500) DEFAULT NULL COMMENT '值', + `desc_` varchar(500) DEFAULT NULL COMMENT '描述', + `encrypt_` tinyint DEFAULT NULL COMMENT '是否加密', + `environment_` varchar(64) DEFAULT NULL COMMENT '环境参数', + `type_code_` varchar(64) DEFAULT NULL COMMENT '分组', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统属性'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_properties` +-- + +LOCK TABLES `sys_properties` WRITE; +/*!40000 ALTER TABLE `sys_properties` DISABLE KEYS */; +INSERT INTO `sys_properties` VALUES ('1602918114232172547','admin.accounts','系统管理号','admin','配置管理员',0,'DEV','sysProperty','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172571','flowLeftTreeTypeSwitch','流程左侧树统计分类数据开关','true','待办,申请历史,办理历史,左侧分离树,是否展示每个分类下的数量,默认不展示',0,'DEV','lcxg','2022-12-14 14:47:09','1','','2023-01-09 18:57:04','系统管理员','1602918114232172634'),('1602918114232172579','email_host','邮箱SMTP服务器域名','smtp.qq.com','',0,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172586','email_port','邮箱SMTP服务端口','465','',0,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172603','email_address','邮件服务发送方','','',0,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172604','email_nickname','邮箱用户名','','',0,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172608','email_password','邮箱密码','','',1,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172627','email_ssl','邮箱否使用SSL安全连接','true','',0,'DEV','yjxg','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172644','loginFailedlockTimeDesc','登陆失败锁定时长','24小时','登录失败锁定账户的时间描述,请自行和缓存时间配置匹配,缓存配置24h,这里建议配置24小时,默认为24小时',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172646','loginCount','连续登陆失败最大次数','5','连续登录验证失败最大次数',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172651','pwdLose','新密码的有效时长','180','修改密码下次到期时间默认180天',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172658','checkingRuleKey','密码检验规则','^[A-Za-z0-9_!@#$%&*]{6,20}$','密码检验规则',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172665','checkingRuleTxt','密码校验提示','密码长度在6-20位之间由数字、字母组合','密码检验失败提示信息',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172676','isOpenResetPwdByEmail','找回密码','false','是否开启邮件找回密码功能',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172685','captchaSwitch','登陆验证','false','用户登陆是否进行验证',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172694','isResetPwd','初始化密码是否必须重置后登录','false','默认值为false:初始化密码不需要重置后登录;',0,'DEV','dlpz','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1602918114232172702','systemVersion','系统版本号','开源版:2.5.0','系统版本号',0,'DEV','sysProperty','2022-12-14 14:47:09','1','','2022-12-14 14:47:09','系统管理员','1'),('1611303682638409728','bpmNotDefaultButtons','流程非默认按钮','oppose,reject2Start,custMultiExecution,addSign,manualEnd','',0,'DEV','lcxg','2023-01-06 18:08:29','1602918114232172634','1602918114232172544','2023-01-06 18:08:29','系统管理员','1602918114232172634'),('1612384210355040256','formTemplateUrl','表单模板URL地址','src/main/resources/templates','表单模板URL地址',0,'DEV','bdxg','2023-01-09 17:42:07','1602918114232172634','1602918114232172544','2023-01-09 17:42:07','系统管理员','1602918114232172634'),('1612385031574597632','task_remove_message_push','任务删除是否推送消息','false','任务删除是否推送消息,默认是不推送(false)',0,'DEV','lcxg','2023-01-09 17:45:23','1602918114232172634','1602918114232172544','2023-01-09 17:45:23','系统管理员','1602918114232172634'),('1612385419128287232','doTaskSuccessional','连续处理任务','true','任务后面节点还是自己任务时,连续处理,默认是 true',0,'DEV','lcxg','2023-01-09 17:46:55','1602918114232172634','1602918114232172544','2023-01-09 17:46:55','系统管理员','1602918114232172634'),('1649335415346970624','changePwdIsLogOut','修改密码是否退出登录','false','用户修改个人密码后时候强制退出登录',0,'DEV','dlpz','2023-04-21 16:53:01','1','410054569125740545','2023-06-12 09:34:52','系统管理员','1602918114232172634'),('1649336284813606912','changePwdIsExitSystem','修改密码是否强制退出系统','false','修改个人密码后是否强制退出系统,包括当前账户登录的移动端应用和其他IP登录的信息,清除用户相关的所有token',0,'DEV','dlpz','2023-04-21 16:56:28','1','410054569125740545','2023-06-12 09:34:42','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `sys_properties` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_region` +-- + +DROP TABLE IF EXISTS `sys_region`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_region` ( + `id` int NOT NULL COMMENT '区域主键', + `name` varchar(40) DEFAULT NULL COMMENT '区域名称', + `parentId` int DEFAULT NULL COMMENT '区域上级标识', + `sname` varchar(40) DEFAULT NULL COMMENT '地名简称', + `level` int DEFAULT NULL COMMENT '区域等级', + `citycode` varchar(20) DEFAULT NULL COMMENT '区域编码', + `yzcode` varchar(20) DEFAULT NULL COMMENT '邮政编码', + `mername` varchar(100) DEFAULT NULL COMMENT '组合名称', + `Lng` float DEFAULT NULL, + `Lat` float DEFAULT NULL, + `pinyin` varchar(100) DEFAULT NULL, + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '创建所属组织', + PRIMARY KEY (`id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='省市区信息表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_region` +-- + +LOCK TABLES `sys_region` WRITE; +/*!40000 ALTER TABLE `sys_region` DISABLE KEYS */; +INSERT INTO `sys_region` VALUES (100000,'中国',0,'中国',0,'','','中国',116.368,39.9151,'China',NULL,NULL),(110000,'北京',100000,'北京',1,'','','中国,北京',116.405,39.905,'Beijing',NULL,NULL),(110100,'北京市',110000,'北京',2,'010','100000','中国,北京,北京市',116.405,39.905,'Beijing',NULL,NULL),(110101,'东城区',110100,'东城',3,'010','100010','中国,北京,北京市,东城区',116.41,39.9316,'Dongcheng',NULL,NULL),(110102,'西城区',110100,'西城',3,'010','100032','中国,北京,北京市,西城区',116.36,39.9305,'Xicheng',NULL,NULL),(110105,'朝阳区',110100,'朝阳',3,'010','100020','中国,北京,北京市,朝阳区',116.485,39.9484,'Chaoyang',NULL,NULL),(110106,'丰台区',110100,'丰台',3,'010','100071','中国,北京,北京市,丰台区',116.286,39.8585,'Fengtai',NULL,NULL),(110107,'石景山区',110100,'石景山',3,'010','100043','中国,北京,北京市,石景山区',116.223,39.9056,'Shijingshan',NULL,NULL),(110108,'海淀区',110100,'海淀',3,'010','100089','中国,北京,北京市,海淀区',116.298,39.9593,'Haidian',NULL,NULL),(110109,'门头沟区',110100,'门头沟',3,'010','102300','中国,北京,北京市,门头沟区',116.101,39.9404,'Mentougou',NULL,NULL),(110111,'房山区',110100,'房山',3,'010','102488','中国,北京,北京市,房山区',116.143,39.7479,'Fangshan',NULL,NULL),(110112,'通州区',110100,'通州',3,'010','101149','中国,北京,北京市,通州区',116.657,39.9097,'Tongzhou',NULL,NULL),(110113,'顺义区',110100,'顺义',3,'010','101300','中国,北京,北京市,顺义区',116.654,40.1302,'Shunyi',NULL,NULL),(110114,'昌平区',110100,'昌平',3,'010','102200','中国,北京,北京市,昌平区',116.231,40.2207,'Changping',NULL,NULL),(110115,'大兴区',110100,'大兴',3,'010','102600','中国,北京,北京市,大兴区',116.341,39.7267,'Daxing',NULL,NULL),(110116,'怀柔区',110100,'怀柔',3,'010','101400','中国,北京,北京市,怀柔区',116.632,40.316,'Huairou',NULL,NULL),(110117,'平谷区',110100,'平谷',3,'010','101200','中国,北京,北京市,平谷区',117.121,40.1406,'Pinggu',NULL,NULL),(110228,'密云县',110100,'密云',3,'010','101500','中国,北京,北京市,密云县',116.843,40.3762,'Miyun',NULL,NULL),(110229,'延庆县',110100,'延庆',3,'010','102100','中国,北京,北京市,延庆县',115.975,40.4567,'Yanqing',NULL,NULL),(120000,'天津',100000,'天津',1,'','','中国,天津',117.19,39.1256,'Tianjin',NULL,NULL),(120100,'天津市',120000,'天津',2,'022','300000','中国,天津,天津市',117.19,39.1256,'Tianjin',NULL,NULL),(120101,'和平区',120100,'和平',3,'022','300041','中国,天津,天津市,和平区',117.215,39.1172,'Heping',NULL,NULL),(120102,'河东区',120100,'河东',3,'022','300171','中国,天津,天津市,河东区',117.226,39.1232,'Hedong',NULL,NULL),(120103,'河西区',120100,'河西',3,'022','300202','中国,天津,天津市,河西区',117.223,39.1096,'Hexi',NULL,NULL),(120104,'南开区',120100,'南开',3,'022','300110','中国,天津,天津市,南开区',117.151,39.1382,'Nankai',NULL,NULL),(120105,'河北区',120100,'河北',3,'022','300143','中国,天津,天津市,河北区',117.197,39.1482,'Hebei',NULL,NULL),(120106,'红桥区',120100,'红桥',3,'022','300131','中国,天津,天津市,红桥区',117.151,39.1671,'Hongqiao',NULL,NULL),(120110,'东丽区',120100,'东丽',3,'022','300300','中国,天津,天津市,东丽区',117.314,39.0863,'Dongli',NULL,NULL),(120111,'西青区',120100,'西青',3,'022','300380','中国,天津,天津市,西青区',117.009,39.1412,'Xiqing',NULL,NULL),(120112,'津南区',120100,'津南',3,'022','300350','中国,天津,天津市,津南区',117.385,38.9914,'Jinnan',NULL,NULL),(120113,'北辰区',120100,'北辰',3,'022','300400','中国,天津,天津市,北辰区',117.132,39.2213,'Beichen',NULL,NULL),(120114,'武清区',120100,'武清',3,'022','301700','中国,天津,天津市,武清区',117.044,39.3842,'Wuqing',NULL,NULL),(120115,'宝坻区',120100,'宝坻',3,'022','301800','中国,天津,天津市,宝坻区',117.31,39.7176,'Baodi',NULL,NULL),(120116,'滨海新区',120100,'滨海新区',3,'022','300451','中国,天津,天津市,滨海新区',117.702,39.0267,'Binhaixinqu',NULL,NULL),(120221,'宁河县',120100,'宁河',3,'022','301500','中国,天津,天津市,宁河县',117.826,39.3305,'Ninghe',NULL,NULL),(120223,'静海县',120100,'静海',3,'022','301600','中国,天津,天津市,静海县',116.974,38.9458,'Jinghai',NULL,NULL),(120225,'蓟县',120100,'蓟县',3,'022','301900','中国,天津,天津市,蓟县',117.408,40.0457,'Jixian',NULL,NULL),(130000,'河北省',100000,'河北',1,'','','中国,河北省',114.502,38.0455,'Hebei',NULL,NULL),(130100,'石家庄市',130000,'石家庄',2,'0311','050011','中国,河北省,石家庄市',114.502,38.0455,'Shijiazhuang',NULL,NULL),(130102,'长安区',130100,'长安',3,'0311','050011','中国,河北省,石家庄市,长安区',114.539,38.0367,'Chang\'an',NULL,NULL),(130104,'桥西区',130100,'桥西',3,'0311','050091','中国,河北省,石家庄市,桥西区',114.47,38.0322,'Qiaoxi',NULL,NULL),(130105,'新华区',130100,'新华',3,'0311','050051','中国,河北省,石家庄市,新华区',114.463,38.0509,'Xinhua',NULL,NULL),(130107,'井陉矿区',130100,'井陉矿区',3,'0311','050100','中国,河北省,石家庄市,井陉矿区',114.065,38.0671,'Jingxingkuangqu',NULL,NULL),(130108,'裕华区',130100,'裕华',3,'0311','050031','中国,河北省,石家庄市,裕华区',114.531,38.006,'Yuhua',NULL,NULL),(130109,'藁城区',130100,'藁城',3,'0311','052160','中国,河北省,石家庄市,藁城区',114.847,38.0216,'Gaocheng',NULL,NULL),(130110,'鹿泉区',130100,'鹿泉',3,'0311','050200','中国,河北省,石家庄市,鹿泉区',114.313,38.0878,'Luquan',NULL,NULL),(130111,'栾城区',130100,'栾城',3,'0311','051430','中国,河北省,石家庄市,栾城区',114.648,37.9002,'Luancheng',NULL,NULL),(130121,'井陉县',130100,'井陉',3,'0311','050300','中国,河北省,石家庄市,井陉县',114.143,38.0369,'Jingxing',NULL,NULL),(130123,'正定县',130100,'正定',3,'0311','050800','中国,河北省,石家庄市,正定县',114.573,38.1445,'Zhengding',NULL,NULL),(130125,'行唐县',130100,'行唐',3,'0311','050600','中国,河北省,石家庄市,行唐县',114.553,38.4365,'Xingtang',NULL,NULL),(130126,'灵寿县',130100,'灵寿',3,'0311','050500','中国,河北省,石家庄市,灵寿县',114.383,38.3084,'Lingshou',NULL,NULL),(130127,'高邑县',130100,'高邑',3,'0311','051330','中国,河北省,石家庄市,高邑县',114.611,37.6156,'Gaoyi',NULL,NULL),(130128,'深泽县',130100,'深泽',3,'0311','052560','中国,河北省,石家庄市,深泽县',115.204,38.1835,'Shenze',NULL,NULL),(130129,'赞皇县',130100,'赞皇',3,'0311','051230','中国,河北省,石家庄市,赞皇县',114.388,37.6614,'Zanhuang',NULL,NULL),(130130,'无极县',130100,'无极',3,'0311','052460','中国,河北省,石家庄市,无极县',114.975,38.1765,'Wuji',NULL,NULL),(130131,'平山县',130100,'平山',3,'0311','050400','中国,河北省,石家庄市,平山县',114.186,38.2599,'Pingshan',NULL,NULL),(130132,'元氏县',130100,'元氏',3,'0311','051130','中国,河北省,石家庄市,元氏县',114.525,37.7667,'Yuanshi',NULL,NULL),(130133,'赵县',130100,'赵县',3,'0311','051530','中国,河北省,石家庄市,赵县',114.776,37.7563,'Zhaoxian',NULL,NULL),(130181,'辛集市',130100,'辛集',3,'0311','052360','中国,河北省,石家庄市,辛集市',115.206,37.9408,'Xinji',NULL,NULL),(130183,'晋州市',130100,'晋州',3,'0311','052260','中国,河北省,石家庄市,晋州市',115.043,38.0313,'Jinzhou',NULL,NULL),(130184,'新乐市',130100,'新乐',3,'0311','050700','中国,河北省,石家庄市,新乐市',114.69,38.3442,'Xinle',NULL,NULL),(130200,'唐山市',130000,'唐山',2,'0315','063000','中国,河北省,唐山市',118.175,39.6351,'Tangshan',NULL,NULL),(130202,'路南区',130200,'路南',3,'0315','063000','中国,河北省,唐山市,路南区',118.154,39.625,'Lunan',NULL,NULL),(130203,'路北区',130200,'路北',3,'0315','063000','中国,河北省,唐山市,路北区',118.201,39.6244,'Lubei',NULL,NULL),(130204,'古冶区',130200,'古冶',3,'0315','063100','中国,河北省,唐山市,古冶区',118.458,39.7199,'Guye',NULL,NULL),(130205,'开平区',130200,'开平',3,'0315','063021','中国,河北省,唐山市,开平区',118.262,39.6713,'Kaiping',NULL,NULL),(130207,'丰南区',130200,'丰南',3,'0315','063300','中国,河北省,唐山市,丰南区',118.113,39.5648,'Fengnan',NULL,NULL),(130208,'丰润区',130200,'丰润',3,'0315','064000','中国,河北省,唐山市,丰润区',118.13,39.8244,'Fengrun',NULL,NULL),(130209,'曹妃甸区',130200,'曹妃甸',3,'0315','063200','中国,河北省,唐山市,曹妃甸区',118.46,39.2731,'Caofeidian',NULL,NULL),(130223,'滦县',130200,'滦县',3,'0315','063700','中国,河北省,唐山市,滦县',118.703,39.7406,'Luanxian',NULL,NULL),(130224,'滦南县',130200,'滦南',3,'0315','063500','中国,河北省,唐山市,滦南县',118.674,39.5039,'Luannan',NULL,NULL),(130225,'乐亭县',130200,'乐亭',3,'0315','063600','中国,河北省,唐山市,乐亭县',118.912,39.4256,'Laoting',NULL,NULL),(130227,'迁西县',130200,'迁西',3,'0315','064300','中国,河北省,唐山市,迁西县',118.316,40.1459,'Qianxi',NULL,NULL),(130229,'玉田县',130200,'玉田',3,'0315','064100','中国,河北省,唐山市,玉田县',117.739,39.9005,'Yutian',NULL,NULL),(130281,'遵化市',130200,'遵化',3,'0315','064200','中国,河北省,唐山市,遵化市',117.964,40.1874,'Zunhua',NULL,NULL),(130283,'迁安市',130200,'迁安',3,'0315','064400','中国,河北省,唐山市,迁安市',118.701,39.9983,'Qian\'an',NULL,NULL),(130300,'秦皇岛市',130000,'秦皇岛',2,'0335','066000','中国,河北省,秦皇岛市',119.587,39.9425,'Qinhuangdao',NULL,NULL),(130302,'海港区',130300,'海港',3,'0335','066000','中国,河北省,秦皇岛市,海港区',119.61,39.9345,'Haigang',NULL,NULL),(130303,'山海关区',130300,'山海关',3,'0335','066200','中国,河北省,秦皇岛市,山海关区',119.776,39.9787,'Shanhaiguan',NULL,NULL),(130304,'北戴河区',130300,'北戴河',3,'0335','066100','中国,河北省,秦皇岛市,北戴河区',119.484,39.8341,'Beidaihe',NULL,NULL),(130321,'青龙满族自治县',130300,'青龙',3,'0335','066500','中国,河北省,秦皇岛市,青龙满族自治县',118.952,40.4074,'Qinglong',NULL,NULL),(130322,'昌黎县',130300,'昌黎',3,'0335','066600','中国,河北省,秦皇岛市,昌黎县',119.166,39.7088,'Changli',NULL,NULL),(130323,'抚宁县',130300,'抚宁',3,'0335','066300','中国,河北省,秦皇岛市,抚宁县',119.245,39.8754,'Funing',NULL,NULL),(130324,'卢龙县',130300,'卢龙',3,'0335','066400','中国,河北省,秦皇岛市,卢龙县',118.893,39.8918,'Lulong',NULL,NULL),(130400,'邯郸市',130000,'邯郸',2,'0310','056002','中国,河北省,邯郸市',114.491,36.6123,'Handan',NULL,NULL),(130402,'邯山区',130400,'邯山',3,'0310','056001','中国,河北省,邯郸市,邯山区',114.484,36.6001,'Hanshan',NULL,NULL),(130403,'丛台区',130400,'丛台',3,'0310','056002','中国,河北省,邯郸市,丛台区',114.493,36.6185,'Congtai',NULL,NULL),(130404,'复兴区',130400,'复兴',3,'0310','056003','中国,河北省,邯郸市,复兴区',114.459,36.6113,'Fuxing',NULL,NULL),(130406,'峰峰矿区',130400,'峰峰矿区',3,'0310','056200','中国,河北省,邯郸市,峰峰矿区',114.211,36.4194,'Fengfengkuangqu',NULL,NULL),(130421,'邯郸县',130400,'邯郸',3,'0310','056101','中国,河北省,邯郸市,邯郸县',114.531,36.5938,'Handan',NULL,NULL),(130423,'临漳县',130400,'临漳',3,'0310','056600','中国,河北省,邯郸市,临漳县',114.619,36.3346,'Linzhang',NULL,NULL),(130424,'成安县',130400,'成安',3,'0310','056700','中国,河北省,邯郸市,成安县',114.67,36.4441,'Cheng\'an',NULL,NULL),(130425,'大名县',130400,'大名',3,'0310','056900','中国,河北省,邯郸市,大名县',115.154,36.2799,'Daming',NULL,NULL),(130426,'涉县',130400,'涉县',3,'0310','056400','中国,河北省,邯郸市,涉县',113.692,36.5807,'Shexian',NULL,NULL),(130427,'磁县',130400,'磁县',3,'0310','056500','中国,河北省,邯郸市,磁县',114.374,36.3739,'Cixian',NULL,NULL),(130428,'肥乡县',130400,'肥乡',3,'0310','057550','中国,河北省,邯郸市,肥乡县',114.8,36.5481,'Feixiang',NULL,NULL),(130429,'永年县',130400,'永年',3,'0310','057150','中国,河北省,邯郸市,永年县',114.489,36.7836,'Yongnian',NULL,NULL),(130430,'邱县',130400,'邱县',3,'0310','057450','中国,河北省,邯郸市,邱县',115.174,36.8208,'Qiuxian',NULL,NULL),(130431,'鸡泽县',130400,'鸡泽',3,'0310','057350','中国,河北省,邯郸市,鸡泽县',114.874,36.9237,'Jize',NULL,NULL),(130432,'广平县',130400,'广平',3,'0310','057650','中国,河北省,邯郸市,广平县',114.947,36.4805,'Guangping',NULL,NULL),(130433,'馆陶县',130400,'馆陶',3,'0310','057750','中国,河北省,邯郸市,馆陶县',115.299,36.5372,'Guantao',NULL,NULL),(130434,'魏县',130400,'魏县',3,'0310','056800','中国,河北省,邯郸市,魏县',114.935,36.3617,'Weixian',NULL,NULL),(130435,'曲周县',130400,'曲周',3,'0310','057250','中国,河北省,邯郸市,曲周县',114.952,36.7767,'Quzhou',NULL,NULL),(130481,'武安市',130400,'武安',3,'0310','056300','中国,河北省,邯郸市,武安市',114.202,36.6928,'Wu\'an',NULL,NULL),(130500,'邢台市',130000,'邢台',2,'0319','054001','中国,河北省,邢台市',114.509,37.0682,'Xingtai',NULL,NULL),(130502,'桥东区',130500,'桥东',3,'0319','054001','中国,河北省,邢台市,桥东区',114.507,37.068,'Qiaodong',NULL,NULL),(130503,'桥西区',130500,'桥西',3,'0319','054000','中国,河北省,邢台市,桥西区',114.468,37.0598,'Qiaoxi',NULL,NULL),(130521,'邢台县',130500,'邢台',3,'0319','054001','中国,河北省,邢台市,邢台县',114.566,37.0456,'Xingtai',NULL,NULL),(130522,'临城县',130500,'临城',3,'0319','054300','中国,河北省,邢台市,临城县',114.504,37.4398,'Lincheng',NULL,NULL),(130523,'内丘县',130500,'内丘',3,'0319','054200','中国,河北省,邢台市,内丘县',114.512,37.2867,'Neiqiu',NULL,NULL),(130524,'柏乡县',130500,'柏乡',3,'0319','055450','中国,河北省,邢台市,柏乡县',114.693,37.4824,'Baixiang',NULL,NULL),(130525,'隆尧县',130500,'隆尧',3,'0319','055350','中国,河北省,邢台市,隆尧县',114.776,37.3535,'Longyao',NULL,NULL),(130526,'任县',130500,'任县',3,'0319','055150','中国,河北省,邢台市,任县',114.684,37.1258,'Renxian',NULL,NULL),(130527,'南和县',130500,'南和',3,'0319','054400','中国,河北省,邢台市,南和县',114.684,37.0049,'Nanhe',NULL,NULL),(130528,'宁晋县',130500,'宁晋',3,'0319','055550','中国,河北省,邢台市,宁晋县',114.921,37.617,'Ningjin',NULL,NULL),(130529,'巨鹿县',130500,'巨鹿',3,'0319','055250','中国,河北省,邢台市,巨鹿县',115.035,37.218,'Julu',NULL,NULL),(130530,'新河县',130500,'新河',3,'0319','055650','中国,河北省,邢台市,新河县',115.25,37.5272,'Xinhe',NULL,NULL),(130531,'广宗县',130500,'广宗',3,'0319','054600','中国,河北省,邢台市,广宗县',115.143,37.0746,'Guangzong',NULL,NULL),(130532,'平乡县',130500,'平乡',3,'0319','054500','中国,河北省,邢台市,平乡县',115.03,37.0632,'Pingxiang',NULL,NULL),(130533,'威县',130500,'威县',3,'0319','054700','中国,河北省,邢台市,威县',115.264,36.9768,'Weixian',NULL,NULL),(130534,'清河县',130500,'清河',3,'0319','054800','中国,河北省,邢台市,清河县',115.665,37.0712,'Qinghe',NULL,NULL),(130535,'临西县',130500,'临西',3,'0319','054900','中国,河北省,邢台市,临西县',115.501,36.8708,'Linxi',NULL,NULL),(130581,'南宫市',130500,'南宫',3,'0319','055750','中国,河北省,邢台市,南宫市',115.391,37.358,'Nangong',NULL,NULL),(130582,'沙河市',130500,'沙河',3,'0319','054100','中国,河北省,邢台市,沙河市',114.498,36.8577,'Shahe',NULL,NULL),(130600,'保定市',130000,'保定',2,'0312','071052','中国,河北省,保定市',115.482,38.8677,'Baoding',NULL,NULL),(130602,'新市区',130600,'新市',3,'0312','071051','中国,河北省,保定市,新市区',115.459,38.8775,'Xinshi',NULL,NULL),(130603,'北市区',130600,'北市',3,'0312','071000','中国,河北省,保定市,北市区',115.497,38.8832,'Beishi',NULL,NULL),(130604,'南市区',130600,'南市',3,'0312','071001','中国,河北省,保定市,南市区',115.529,38.8545,'Nanshi',NULL,NULL),(130621,'满城县',130600,'满城',3,'0312','072150','中国,河北省,保定市,满城县',115.323,38.9497,'Mancheng',NULL,NULL),(130622,'清苑县',130600,'清苑',3,'0312','071100','中国,河北省,保定市,清苑县',115.493,38.7671,'Qingyuan',NULL,NULL),(130623,'涞水县',130600,'涞水',3,'0312','074100','中国,河北省,保定市,涞水县',115.715,39.394,'Laishui',NULL,NULL),(130624,'阜平县',130600,'阜平',3,'0312','073200','中国,河北省,保定市,阜平县',114.197,38.8476,'Fuping',NULL,NULL),(130625,'徐水县',130600,'徐水',3,'0312','072550','中国,河北省,保定市,徐水县',115.658,39.021,'Xushui',NULL,NULL),(130626,'定兴县',130600,'定兴',3,'0312','072650','中国,河北省,保定市,定兴县',115.808,39.2631,'Dingxing',NULL,NULL),(130627,'唐县',130600,'唐县',3,'0312','072350','中国,河北省,保定市,唐县',114.985,38.7451,'Tangxian',NULL,NULL),(130628,'高阳县',130600,'高阳',3,'0312','071500','中国,河北省,保定市,高阳县',115.779,38.7,'Gaoyang',NULL,NULL),(130629,'容城县',130600,'容城',3,'0312','071700','中国,河北省,保定市,容城县',115.872,39.0535,'Rongcheng',NULL,NULL),(130630,'涞源县',130600,'涞源',3,'0312','074300','中国,河北省,保定市,涞源县',114.691,39.3539,'Laiyuan',NULL,NULL),(130631,'望都县',130600,'望都',3,'0312','072450','中国,河北省,保定市,望都县',115.157,38.71,'Wangdu',NULL,NULL),(130632,'安新县',130600,'安新',3,'0312','071600','中国,河北省,保定市,安新县',115.936,38.9353,'Anxin',NULL,NULL),(130633,'易县',130600,'易县',3,'0312','074200','中国,河北省,保定市,易县',115.498,39.3489,'Yixian',NULL,NULL),(130634,'曲阳县',130600,'曲阳',3,'0312','073100','中国,河北省,保定市,曲阳县',114.701,38.6215,'Quyang',NULL,NULL),(130635,'蠡县',130600,'蠡县',3,'0312','071400','中国,河北省,保定市,蠡县',115.577,38.4897,'Lixian',NULL,NULL),(130636,'顺平县',130600,'顺平',3,'0312','072250','中国,河北省,保定市,顺平县',115.135,38.8385,'Shunping',NULL,NULL),(130637,'博野县',130600,'博野',3,'0312','071300','中国,河北省,保定市,博野县',115.47,38.4564,'Boye',NULL,NULL),(130638,'雄县',130600,'雄县',3,'0312','071800','中国,河北省,保定市,雄县',116.109,38.9944,'Xiongxian',NULL,NULL),(130681,'涿州市',130600,'涿州',3,'0312','072750','中国,河北省,保定市,涿州市',115.981,39.4862,'Zhuozhou',NULL,NULL),(130682,'定州市',130600,'定州',3,'0312','073000','中国,河北省,保定市,定州市',114.99,38.5162,'Dingzhou',NULL,NULL),(130683,'安国市',130600,'安国',3,'0312','071200','中国,河北省,保定市,安国市',115.323,38.4139,'Anguo',NULL,NULL),(130684,'高碑店市',130600,'高碑店',3,'0312','074000','中国,河北省,保定市,高碑店市',115.874,39.3265,'Gaobeidian',NULL,NULL),(130700,'张家口市',130000,'张家口',2,'0313','075000','中国,河北省,张家口市',114.884,40.8119,'Zhangjiakou',NULL,NULL),(130702,'桥东区',130700,'桥东',3,'0313','075000','中国,河北省,张家口市,桥东区',114.894,40.7884,'Qiaodong',NULL,NULL),(130703,'桥西区',130700,'桥西',3,'0313','075061','中国,河北省,张家口市,桥西区',114.87,40.8195,'Qiaoxi',NULL,NULL),(130705,'宣化区',130700,'宣化',3,'0313','075100','中国,河北省,张家口市,宣化区',115.065,40.6096,'Xuanhua',NULL,NULL),(130706,'下花园区',130700,'下花园',3,'0313','075300','中国,河北省,张家口市,下花园区',115.287,40.5024,'Xiahuayuan',NULL,NULL),(130721,'宣化县',130700,'宣化',3,'0313','075100','中国,河北省,张家口市,宣化县',115.155,40.5662,'Xuanhua',NULL,NULL),(130722,'张北县',130700,'张北',3,'0313','076450','中国,河北省,张家口市,张北县',114.714,41.1598,'Zhangbei',NULL,NULL),(130723,'康保县',130700,'康保',3,'0313','076650','中国,河北省,张家口市,康保县',114.6,41.8522,'Kangbao',NULL,NULL),(130724,'沽源县',130700,'沽源',3,'0313','076550','中国,河北省,张家口市,沽源县',115.689,41.6696,'Guyuan',NULL,NULL),(130725,'尚义县',130700,'尚义',3,'0313','076750','中国,河北省,张家口市,尚义县',113.971,41.0778,'Shangyi',NULL,NULL),(130726,'蔚县',130700,'蔚县',3,'0313','075700','中国,河北省,张家口市,蔚县',114.589,39.8407,'Yuxian',NULL,NULL),(130727,'阳原县',130700,'阳原',3,'0313','075800','中国,河北省,张家口市,阳原县',114.151,40.1036,'Yangyuan',NULL,NULL),(130728,'怀安县',130700,'怀安',3,'0313','076150','中国,河北省,张家口市,怀安县',114.386,40.6743,'Huai\'an',NULL,NULL),(130729,'万全县',130700,'万全',3,'0313','076250','中国,河北省,张家口市,万全县',114.741,40.7669,'Wanquan',NULL,NULL),(130730,'怀来县',130700,'怀来',3,'0313','075400','中国,河北省,张家口市,怀来县',115.518,40.4154,'Huailai',NULL,NULL),(130731,'涿鹿县',130700,'涿鹿',3,'0313','075600','中国,河北省,张家口市,涿鹿县',115.224,40.3764,'Zhuolu',NULL,NULL),(130732,'赤城县',130700,'赤城',3,'0313','075500','中国,河北省,张家口市,赤城县',115.832,40.9144,'Chicheng',NULL,NULL),(130733,'崇礼县',130700,'崇礼',3,'0313','076350','中国,河北省,张家口市,崇礼县',115.28,40.9752,'Chongli',NULL,NULL),(130800,'承德市',130000,'承德',2,'0314','067000','中国,河北省,承德市',117.939,40.9762,'Chengde',NULL,NULL),(130802,'双桥区',130800,'双桥',3,'0314','067000','中国,河北省,承德市,双桥区',117.943,40.9747,'Shuangqiao',NULL,NULL),(130803,'双滦区',130800,'双滦',3,'0314','067001','中国,河北省,承德市,双滦区',117.745,40.9538,'Shuangluan',NULL,NULL),(130804,'鹰手营子矿区',130800,'鹰手营子矿区',3,'0314','067200','中国,河北省,承德市,鹰手营子矿区',117.66,40.5474,'Yingshouyingzikuangqu',NULL,NULL),(130821,'承德县',130800,'承德',3,'0314','067400','中国,河北省,承德市,承德县',118.176,40.7699,'Chengde',NULL,NULL),(130822,'兴隆县',130800,'兴隆',3,'0314','067300','中国,河北省,承德市,兴隆县',117.501,40.4171,'Xinglong',NULL,NULL),(130823,'平泉县',130800,'平泉',3,'0314','067500','中国,河北省,承德市,平泉县',118.702,41.0184,'Pingquan',NULL,NULL),(130824,'滦平县',130800,'滦平',3,'0314','068250','中国,河北省,承德市,滦平县',117.333,40.9415,'Luanping',NULL,NULL),(130825,'隆化县',130800,'隆化',3,'0314','068150','中国,河北省,承德市,隆化县',117.73,41.3141,'Longhua',NULL,NULL),(130826,'丰宁满族自治县',130800,'丰宁',3,'0314','068350','中国,河北省,承德市,丰宁满族自治县',116.649,41.2048,'Fengning',NULL,NULL),(130827,'宽城满族自治县',130800,'宽城',3,'0314','067600','中国,河北省,承德市,宽城满族自治县',118.492,40.6083,'Kuancheng',NULL,NULL),(130828,'围场满族蒙古族自治县',130800,'围场',3,'0314','068450','中国,河北省,承德市,围场满族蒙古族自治县',117.76,41.9437,'Weichang',NULL,NULL),(130900,'沧州市',130000,'沧州',2,'0317','061001','中国,河北省,沧州市',116.857,38.3106,'Cangzhou',NULL,NULL),(130902,'新华区',130900,'新华',3,'0317','061000','中国,河北省,沧州市,新华区',116.866,38.3144,'Xinhua',NULL,NULL),(130903,'运河区',130900,'运河',3,'0317','061001','中国,河北省,沧州市,运河区',116.857,38.3135,'Yunhe',NULL,NULL),(130921,'沧县',130900,'沧县',3,'0317','061000','中国,河北省,沧州市,沧县',116.878,38.2936,'Cangxian',NULL,NULL),(130922,'青县',130900,'青县',3,'0317','062650','中国,河北省,沧州市,青县',116.803,38.5835,'Qingxian',NULL,NULL),(130923,'东光县',130900,'东光',3,'0317','061600','中国,河北省,沧州市,东光县',116.537,37.8857,'Dongguang',NULL,NULL),(130924,'海兴县',130900,'海兴',3,'0317','061200','中国,河北省,沧州市,海兴县',117.498,38.1396,'Haixing',NULL,NULL),(130925,'盐山县',130900,'盐山',3,'0317','061300','中国,河北省,沧州市,盐山县',117.231,38.0565,'Yanshan',NULL,NULL),(130926,'肃宁县',130900,'肃宁',3,'0317','062350','中国,河北省,沧州市,肃宁县',115.83,38.4227,'Suning',NULL,NULL),(130927,'南皮县',130900,'南皮',3,'0317','061500','中国,河北省,沧州市,南皮县',116.702,38.0411,'Nanpi',NULL,NULL),(130928,'吴桥县',130900,'吴桥',3,'0317','061800','中国,河北省,沧州市,吴桥县',116.385,37.6255,'Wuqiao',NULL,NULL),(130929,'献县',130900,'献县',3,'0317','062250','中国,河北省,沧州市,献县',116.127,38.1923,'Xianxian',NULL,NULL),(130930,'孟村回族自治县',130900,'孟村',3,'0317','061400','中国,河北省,沧州市,孟村回族自治县',117.104,38.0534,'Mengcun',NULL,NULL),(130981,'泊头市',130900,'泊头',3,'0317','062150','中国,河北省,沧州市,泊头市',116.578,38.0836,'Botou',NULL,NULL),(130982,'任丘市',130900,'任丘',3,'0317','062550','中国,河北省,沧州市,任丘市',116.103,38.7112,'Renqiu',NULL,NULL),(130983,'黄骅市',130900,'黄骅',3,'0317','061100','中国,河北省,沧州市,黄骅市',117.339,38.3706,'Huanghua',NULL,NULL),(130984,'河间市',130900,'河间',3,'0317','062450','中国,河北省,沧州市,河间市',116.099,38.4455,'Hejian',NULL,NULL),(131000,'廊坊市',130000,'廊坊',2,'0316','065000','中国,河北省,廊坊市',116.714,39.5292,'Langfang',NULL,NULL),(131002,'安次区',131000,'安次',3,'0316','065000','中国,河北省,廊坊市,安次区',116.703,39.5206,'Anci',NULL,NULL),(131003,'广阳区',131000,'广阳',3,'0316','065000','中国,河北省,廊坊市,广阳区',116.711,39.5228,'Guangyang',NULL,NULL),(131022,'固安县',131000,'固安',3,'0316','065500','中国,河北省,廊坊市,固安县',116.299,39.4383,'Gu\'an',NULL,NULL),(131023,'永清县',131000,'永清',3,'0316','065600','中国,河北省,廊坊市,永清县',116.501,39.3207,'Yongqing',NULL,NULL),(131024,'香河县',131000,'香河',3,'0316','065400','中国,河北省,廊坊市,香河县',117.006,39.7613,'Xianghe',NULL,NULL),(131025,'大城县',131000,'大城',3,'0316','065900','中国,河北省,廊坊市,大城县',116.654,38.7053,'Daicheng',NULL,NULL),(131026,'文安县',131000,'文安',3,'0316','065800','中国,河北省,廊坊市,文安县',116.458,38.8732,'Wen\'an',NULL,NULL),(131028,'大厂回族自治县',131000,'大厂',3,'0316','065300','中国,河北省,廊坊市,大厂回族自治县',116.989,39.8865,'Dachang',NULL,NULL),(131081,'霸州市',131000,'霸州',3,'0316','065700','中国,河北省,廊坊市,霸州市',116.392,39.1257,'Bazhou',NULL,NULL),(131082,'三河市',131000,'三河',3,'0316','065200','中国,河北省,廊坊市,三河市',117.072,39.9836,'Sanhe',NULL,NULL),(131100,'衡水市',130000,'衡水',2,'0318','053000','中国,河北省,衡水市',115.666,37.7351,'Hengshui',NULL,NULL),(131102,'桃城区',131100,'桃城',3,'0318','053000','中国,河北省,衡水市,桃城区',115.675,37.735,'Taocheng',NULL,NULL),(131121,'枣强县',131100,'枣强',3,'0318','053100','中国,河北省,衡水市,枣强县',115.726,37.5103,'Zaoqiang',NULL,NULL),(131122,'武邑县',131100,'武邑',3,'0318','053400','中国,河北省,衡水市,武邑县',115.887,37.8018,'Wuyi',NULL,NULL),(131123,'武强县',131100,'武强',3,'0318','053300','中国,河北省,衡水市,武强县',115.982,38.0414,'Wuqiang',NULL,NULL),(131124,'饶阳县',131100,'饶阳',3,'0318','053900','中国,河北省,衡水市,饶阳县',115.726,38.2353,'Raoyang',NULL,NULL),(131125,'安平县',131100,'安平',3,'0318','053600','中国,河北省,衡水市,安平县',115.519,38.2339,'Anping',NULL,NULL),(131126,'故城县',131100,'故城',3,'0318','053800','中国,河北省,衡水市,故城县',115.971,37.3477,'Gucheng',NULL,NULL),(131127,'景县',131100,'景县',3,'0318','053500','中国,河北省,衡水市,景县',116.269,37.6926,'Jingxian',NULL,NULL),(131128,'阜城县',131100,'阜城',3,'0318','053700','中国,河北省,衡水市,阜城县',116.144,37.8688,'Fucheng',NULL,NULL),(131181,'冀州市',131100,'冀州',3,'0318','053200','中国,河北省,衡水市,冀州市',115.579,37.5508,'Jizhou',NULL,NULL),(131182,'深州市',131100,'深州',3,'0318','053800','中国,河北省,衡水市,深州市',115.56,38.0011,'Shenzhou',NULL,NULL),(140000,'山西省',100000,'山西',1,'','','中国,山西省',112.549,37.857,'Shanxi',NULL,NULL),(140100,'太原市',140000,'太原',2,'0351','030082','中国,山西省,太原市',112.549,37.857,'Taiyuan',NULL,NULL),(140105,'小店区',140100,'小店',3,'0351','030032','中国,山西省,太原市,小店区',112.569,37.7356,'Xiaodian',NULL,NULL),(140106,'迎泽区',140100,'迎泽',3,'0351','030002','中国,山西省,太原市,迎泽区',112.563,37.8633,'Yingze',NULL,NULL),(140107,'杏花岭区',140100,'杏花岭',3,'0351','030009','中国,山西省,太原市,杏花岭区',112.562,37.8843,'Xinghualing',NULL,NULL),(140108,'尖草坪区',140100,'尖草坪',3,'0351','030023','中国,山西省,太原市,尖草坪区',112.487,37.9419,'Jiancaoping',NULL,NULL),(140109,'万柏林区',140100,'万柏林',3,'0351','030024','中国,山西省,太原市,万柏林区',112.516,37.8592,'Wanbailin',NULL,NULL),(140110,'晋源区',140100,'晋源',3,'0351','030025','中国,山西省,太原市,晋源区',112.48,37.7248,'Jinyuan',NULL,NULL),(140121,'清徐县',140100,'清徐',3,'0351','030400','中国,山西省,太原市,清徐县',112.359,37.6076,'Qingxu',NULL,NULL),(140122,'阳曲县',140100,'阳曲',3,'0351','030100','中国,山西省,太原市,阳曲县',112.679,38.0599,'Yangqu',NULL,NULL),(140123,'娄烦县',140100,'娄烦',3,'0351','030300','中国,山西省,太原市,娄烦县',111.795,38.0669,'Loufan',NULL,NULL),(140181,'古交市',140100,'古交',3,'0351','030200','中国,山西省,太原市,古交市',112.169,37.9098,'Gujiao',NULL,NULL),(140200,'大同市',140000,'大同',2,'0352','037008','中国,山西省,大同市',113.295,40.0903,'Datong',NULL,NULL),(140202,'城区',140200,'城区',3,'0352','037008','中国,山西省,大同市,城区',113.298,40.0757,'Chengqu',NULL,NULL),(140203,'矿区',140200,'矿区',3,'0352','037003','中国,山西省,大同市,矿区',113.177,40.0368,'Kuangqu',NULL,NULL),(140211,'南郊区',140200,'南郊',3,'0352','037001','中国,山西省,大同市,南郊区',113.149,40.0054,'Nanjiao',NULL,NULL),(140212,'新荣区',140200,'新荣',3,'0352','037002','中国,山西省,大同市,新荣区',113.135,40.2562,'Xinrong',NULL,NULL),(140221,'阳高县',140200,'阳高',3,'0352','038100','中国,山西省,大同市,阳高县',113.75,40.3626,'Yanggao',NULL,NULL),(140222,'天镇县',140200,'天镇',3,'0352','038200','中国,山西省,大同市,天镇县',114.093,40.423,'Tianzhen',NULL,NULL),(140223,'广灵县',140200,'广灵',3,'0352','037500','中国,山西省,大同市,广灵县',114.282,39.7608,'Guangling',NULL,NULL),(140224,'灵丘县',140200,'灵丘',3,'0352','034400','中国,山西省,大同市,灵丘县',114.237,39.4404,'Lingqiu',NULL,NULL),(140225,'浑源县',140200,'浑源',3,'0352','037400','中国,山西省,大同市,浑源县',113.696,39.6996,'Hunyuan',NULL,NULL),(140226,'左云县',140200,'左云',3,'0352','037100','中国,山西省,大同市,左云县',112.703,40.0134,'Zuoyun',NULL,NULL),(140227,'大同县',140200,'大同',3,'0352','037300','中国,山西省,大同市,大同县',113.612,40.0401,'Datong',NULL,NULL),(140300,'阳泉市',140000,'阳泉',2,'0353','045000','中国,山西省,阳泉市',113.583,37.8612,'Yangquan',NULL,NULL),(140302,'城区',140300,'城区',3,'0353','045000','中国,山西省,阳泉市,城区',113.601,37.8474,'Chengqu',NULL,NULL),(140303,'矿区',140300,'矿区',3,'0353','045000','中国,山西省,阳泉市,矿区',113.557,37.8689,'Kuangqu',NULL,NULL),(140311,'郊区',140300,'郊区',3,'0353','045011','中国,山西省,阳泉市,郊区',113.585,37.9414,'Jiaoqu',NULL,NULL),(140321,'平定县',140300,'平定',3,'0353','045200','中国,山西省,阳泉市,平定县',113.658,37.786,'Pingding',NULL,NULL),(140322,'盂县',140300,'盂县',3,'0353','045100','中国,山西省,阳泉市,盂县',113.412,38.0858,'Yuxian',NULL,NULL),(140400,'长治市',140000,'长治',2,'0355','046000','中国,山西省,长治市',113.114,36.1911,'Changzhi',NULL,NULL),(140402,'城区',140400,'城区',3,'0355','046011','中国,山西省,长治市,城区',113.123,36.2035,'Chengqu',NULL,NULL),(140411,'郊区',140400,'郊区',3,'0355','046011','中国,山西省,长治市,郊区',113.127,36.1992,'Jiaoqu',NULL,NULL),(140421,'长治县',140400,'长治',3,'0355','047100','中国,山西省,长治市,长治县',113.048,36.0472,'Changzhi',NULL,NULL),(140423,'襄垣县',140400,'襄垣',3,'0355','046200','中国,山西省,长治市,襄垣县',113.052,36.5353,'Xiangyuan',NULL,NULL),(140424,'屯留县',140400,'屯留',3,'0355','046100','中国,山西省,长治市,屯留县',112.892,36.3158,'Tunliu',NULL,NULL),(140425,'平顺县',140400,'平顺',3,'0355','047400','中国,山西省,长治市,平顺县',113.436,36.2001,'Pingshun',NULL,NULL),(140426,'黎城县',140400,'黎城',3,'0355','047600','中国,山西省,长治市,黎城县',113.388,36.503,'Licheng',NULL,NULL),(140427,'壶关县',140400,'壶关',3,'0355','047300','中国,山西省,长治市,壶关县',113.207,36.113,'Huguan',NULL,NULL),(140428,'长子县',140400,'长子',3,'0355','046600','中国,山西省,长治市,长子县',112.877,36.1213,'Zhangzi',NULL,NULL),(140429,'武乡县',140400,'武乡',3,'0355','046300','中国,山西省,长治市,武乡县',112.863,36.8369,'Wuxiang',NULL,NULL),(140430,'沁县',140400,'沁县',3,'0355','046400','中国,山西省,长治市,沁县',112.699,36.7563,'Qinxian',NULL,NULL),(140431,'沁源县',140400,'沁源',3,'0355','046500','中国,山西省,长治市,沁源县',112.338,36.5001,'Qinyuan',NULL,NULL),(140481,'潞城市',140400,'潞城',3,'0355','047500','中国,山西省,长治市,潞城市',113.229,36.3341,'Lucheng',NULL,NULL),(140500,'晋城市',140000,'晋城',2,'0356','048000','中国,山西省,晋城市',112.851,35.4976,'Jincheng',NULL,NULL),(140502,'城区',140500,'城区',3,'0356','048000','中国,山西省,晋城市,城区',112.853,35.5018,'Chengqu',NULL,NULL),(140521,'沁水县',140500,'沁水',3,'0356','048200','中国,山西省,晋城市,沁水县',112.187,35.691,'Qinshui',NULL,NULL),(140522,'阳城县',140500,'阳城',3,'0356','048100','中国,山西省,晋城市,阳城县',112.415,35.4861,'Yangcheng',NULL,NULL),(140524,'陵川县',140500,'陵川',3,'0356','048300','中国,山西省,晋城市,陵川县',113.281,35.7753,'Lingchuan',NULL,NULL),(140525,'泽州县',140500,'泽州',3,'0356','048012','中国,山西省,晋城市,泽州县',112.839,35.5079,'Zezhou',NULL,NULL),(140581,'高平市',140500,'高平',3,'0356','048400','中国,山西省,晋城市,高平市',112.923,35.7971,'Gaoping',NULL,NULL),(140600,'朔州市',140000,'朔州',2,'0349','038500','中国,山西省,朔州市',112.433,39.3313,'Shuozhou',NULL,NULL),(140602,'朔城区',140600,'朔城',3,'0349','036000','中国,山西省,朔州市,朔城区',112.432,39.3198,'Shuocheng',NULL,NULL),(140603,'平鲁区',140600,'平鲁',3,'0349','038600','中国,山西省,朔州市,平鲁区',112.288,39.5116,'Pinglu',NULL,NULL),(140621,'山阴县',140600,'山阴',3,'0349','036900','中国,山西省,朔州市,山阴县',112.817,39.527,'Shanyin',NULL,NULL),(140622,'应县',140600,'应县',3,'0349','037600','中国,山西省,朔州市,应县',113.191,39.5528,'Yingxian',NULL,NULL),(140623,'右玉县',140600,'右玉',3,'0349','037200','中国,山西省,朔州市,右玉县',112.469,39.9901,'Youyu',NULL,NULL),(140624,'怀仁县',140600,'怀仁',3,'0349','038300','中国,山西省,朔州市,怀仁县',113.1,39.8281,'Huairen',NULL,NULL),(140700,'晋中市',140000,'晋中',2,'0354','030600','中国,山西省,晋中市',112.736,37.6965,'Jinzhong',NULL,NULL),(140702,'榆次区',140700,'榆次',3,'0354','030600','中国,山西省,晋中市,榆次区',112.708,37.6978,'Yuci',NULL,NULL),(140721,'榆社县',140700,'榆社',3,'0354','031800','中国,山西省,晋中市,榆社县',112.976,37.0721,'Yushe',NULL,NULL),(140722,'左权县',140700,'左权',3,'0354','032600','中国,山西省,晋中市,左权县',113.379,37.0824,'Zuoquan',NULL,NULL),(140723,'和顺县',140700,'和顺',3,'0354','032700','中国,山西省,晋中市,和顺县',113.57,37.3296,'Heshun',NULL,NULL),(140724,'昔阳县',140700,'昔阳',3,'0354','045300','中国,山西省,晋中市,昔阳县',113.705,37.6186,'Xiyang',NULL,NULL),(140725,'寿阳县',140700,'寿阳',3,'0354','045400','中国,山西省,晋中市,寿阳县',113.175,37.889,'Shouyang',NULL,NULL),(140726,'太谷县',140700,'太谷',3,'0354','030800','中国,山西省,晋中市,太谷县',112.552,37.4216,'Taigu',NULL,NULL),(140727,'祁县',140700,'祁县',3,'0354','030900','中国,山西省,晋中市,祁县',112.334,37.3579,'Qixian',NULL,NULL),(140728,'平遥县',140700,'平遥',3,'0354','031100','中国,山西省,晋中市,平遥县',112.176,37.1892,'Pingyao',NULL,NULL),(140729,'灵石县',140700,'灵石',3,'0354','031300','中国,山西省,晋中市,灵石县',111.777,36.8481,'Lingshi',NULL,NULL),(140781,'介休市',140700,'介休',3,'0354','032000','中国,山西省,晋中市,介休市',111.918,37.0277,'Jiexiu',NULL,NULL),(140800,'运城市',140000,'运城',2,'0359','044000','中国,山西省,运城市',111.004,35.0228,'Yuncheng',NULL,NULL),(140802,'盐湖区',140800,'盐湖',3,'0359','044000','中国,山西省,运城市,盐湖区',110.998,35.0151,'Yanhu',NULL,NULL),(140821,'临猗县',140800,'临猗',3,'0359','044100','中国,山西省,运城市,临猗县',110.774,35.1446,'Linyi',NULL,NULL),(140822,'万荣县',140800,'万荣',3,'0359','044200','中国,山西省,运城市,万荣县',110.837,35.4156,'Wanrong',NULL,NULL),(140823,'闻喜县',140800,'闻喜',3,'0359','043800','中国,山西省,运城市,闻喜县',111.223,35.3555,'Wenxi',NULL,NULL),(140824,'稷山县',140800,'稷山',3,'0359','043200','中国,山西省,运城市,稷山县',110.979,35.5999,'Jishan',NULL,NULL),(140825,'新绛县',140800,'新绛',3,'0359','043100','中国,山西省,运城市,新绛县',111.225,35.6157,'Xinjiang',NULL,NULL),(140826,'绛县',140800,'绛县',3,'0359','043600','中国,山西省,运城市,绛县',111.567,35.491,'Jiangxian',NULL,NULL),(140827,'垣曲县',140800,'垣曲',3,'0359','043700','中国,山西省,运城市,垣曲县',111.672,35.2992,'Yuanqu',NULL,NULL),(140828,'夏县',140800,'夏县',3,'0359','044400','中国,山西省,运城市,夏县',111.22,35.1412,'Xiaxian',NULL,NULL),(140829,'平陆县',140800,'平陆',3,'0359','044300','中国,山西省,运城市,平陆县',111.217,34.8377,'Pinglu',NULL,NULL),(140830,'芮城县',140800,'芮城',3,'0359','044600','中国,山西省,运城市,芮城县',110.695,34.6938,'Ruicheng',NULL,NULL),(140881,'永济市',140800,'永济',3,'0359','044500','中国,山西省,运城市,永济市',110.445,34.8656,'Yongji',NULL,NULL),(140882,'河津市',140800,'河津',3,'0359','043300','中国,山西省,运城市,河津市',110.712,35.5948,'Hejin',NULL,NULL),(140900,'忻州市',140000,'忻州',2,'0350','034000','中国,山西省,忻州市',112.734,38.4177,'Xinzhou',NULL,NULL),(140902,'忻府区',140900,'忻府',3,'0350','034000','中国,山西省,忻州市,忻府区',112.746,38.4041,'Xinfu',NULL,NULL),(140921,'定襄县',140900,'定襄',3,'0350','035400','中国,山西省,忻州市,定襄县',112.957,38.4739,'Dingxiang',NULL,NULL),(140922,'五台县',140900,'五台',3,'0350','035500','中国,山西省,忻州市,五台县',113.253,38.7277,'Wutai',NULL,NULL),(140923,'代县',140900,'代县',3,'0350','034200','中国,山西省,忻州市,代县',112.959,39.0672,'Daixian',NULL,NULL),(140924,'繁峙县',140900,'繁峙',3,'0350','034300','中国,山西省,忻州市,繁峙县',113.263,39.1889,'Fanshi',NULL,NULL),(140925,'宁武县',140900,'宁武',3,'0350','036700','中国,山西省,忻州市,宁武县',112.304,39.0021,'Ningwu',NULL,NULL),(140926,'静乐县',140900,'静乐',3,'0350','035100','中国,山西省,忻州市,静乐县',111.942,38.3602,'Jingle',NULL,NULL),(140927,'神池县',140900,'神池',3,'0350','036100','中国,山西省,忻州市,神池县',112.205,39.09,'Shenchi',NULL,NULL),(140928,'五寨县',140900,'五寨',3,'0350','036200','中国,山西省,忻州市,五寨县',111.849,38.9076,'Wuzhai',NULL,NULL),(140929,'岢岚县',140900,'岢岚',3,'0350','036300','中国,山西省,忻州市,岢岚县',111.574,38.7045,'Kelan',NULL,NULL),(140930,'河曲县',140900,'河曲',3,'0350','036500','中国,山西省,忻州市,河曲县',111.138,39.3844,'Hequ',NULL,NULL),(140931,'保德县',140900,'保德',3,'0350','036600','中国,山西省,忻州市,保德县',111.087,39.0225,'Baode',NULL,NULL),(140932,'偏关县',140900,'偏关',3,'0350','036400','中国,山西省,忻州市,偏关县',111.509,39.4361,'Pianguan',NULL,NULL),(140981,'原平市',140900,'原平',3,'0350','034100','中国,山西省,忻州市,原平市',112.706,38.7318,'Yuanping',NULL,NULL),(141000,'临汾市',140000,'临汾',2,'0357','041000','中国,山西省,临汾市',111.518,36.0841,'Linfen',NULL,NULL),(141002,'尧都区',141000,'尧都',3,'0357','041000','中国,山西省,临汾市,尧都区',111.579,36.083,'Yaodu',NULL,NULL),(141021,'曲沃县',141000,'曲沃',3,'0357','043400','中国,山西省,临汾市,曲沃县',111.475,35.6412,'Quwo',NULL,NULL),(141022,'翼城县',141000,'翼城',3,'0357','043500','中国,山西省,临汾市,翼城县',111.718,35.7388,'Yicheng',NULL,NULL),(141023,'襄汾县',141000,'襄汾',3,'0357','041500','中国,山西省,临汾市,襄汾县',111.442,35.8771,'Xiangfen',NULL,NULL),(141024,'洪洞县',141000,'洪洞',3,'0357','041600','中国,山西省,临汾市,洪洞县',111.675,36.2542,'Hongtong',NULL,NULL),(141025,'古县',141000,'古县',3,'0357','042400','中国,山西省,临汾市,古县',111.92,36.2669,'Guxian',NULL,NULL),(141026,'安泽县',141000,'安泽',3,'0357','042500','中国,山西省,临汾市,安泽县',112.25,36.148,'Anze',NULL,NULL),(141027,'浮山县',141000,'浮山',3,'0357','042600','中国,山西省,临汾市,浮山县',111.847,35.9685,'Fushan',NULL,NULL),(141028,'吉县',141000,'吉县',3,'0357','042200','中国,山西省,临汾市,吉县',110.681,36.0987,'Jixian',NULL,NULL),(141029,'乡宁县',141000,'乡宁',3,'0357','042100','中国,山西省,临汾市,乡宁县',110.847,35.9707,'Xiangning',NULL,NULL),(141030,'大宁县',141000,'大宁',3,'0357','042300','中国,山西省,临汾市,大宁县',110.752,36.4662,'Daning',NULL,NULL),(141031,'隰县',141000,'隰县',3,'0357','041300','中国,山西省,临汾市,隰县',110.939,36.6926,'Xixian',NULL,NULL),(141032,'永和县',141000,'永和',3,'0357','041400','中国,山西省,临汾市,永和县',110.632,36.7584,'Yonghe',NULL,NULL),(141033,'蒲县',141000,'蒲县',3,'0357','041200','中国,山西省,临汾市,蒲县',111.097,36.4124,'Puxian',NULL,NULL),(141034,'汾西县',141000,'汾西',3,'0357','031500','中国,山西省,临汾市,汾西县',111.568,36.6506,'Fenxi',NULL,NULL),(141081,'侯马市',141000,'侯马',3,'0357','043000','中国,山西省,临汾市,侯马市',111.372,35.619,'Houma',NULL,NULL),(141082,'霍州市',141000,'霍州',3,'0357','031400','中国,山西省,临汾市,霍州市',111.755,36.5638,'Huozhou',NULL,NULL),(141100,'吕梁市',140000,'吕梁',2,'0358','033000','中国,山西省,吕梁市',111.134,37.5244,'Lvliang',NULL,NULL),(141102,'离石区',141100,'离石',3,'0358','033000','中国,山西省,吕梁市,离石区',111.151,37.5177,'Lishi',NULL,NULL),(141121,'文水县',141100,'文水',3,'0358','032100','中国,山西省,吕梁市,文水县',112.028,37.4384,'Wenshui',NULL,NULL),(141122,'交城县',141100,'交城',3,'0358','030500','中国,山西省,吕梁市,交城县',112.159,37.5512,'Jiaocheng',NULL,NULL),(141123,'兴县',141100,'兴县',3,'0358','033600','中国,山西省,吕梁市,兴县',111.127,38.4632,'Xingxian',NULL,NULL),(141124,'临县',141100,'临县',3,'0358','033200','中国,山西省,吕梁市,临县',110.993,37.9527,'Linxian',NULL,NULL),(141125,'柳林县',141100,'柳林',3,'0358','033300','中国,山西省,吕梁市,柳林县',110.889,37.4293,'Liulin',NULL,NULL),(141126,'石楼县',141100,'石楼',3,'0358','032500','中国,山西省,吕梁市,石楼县',110.835,36.9973,'Shilou',NULL,NULL),(141127,'岚县',141100,'岚县',3,'0358','033500','中国,山西省,吕梁市,岚县',111.676,38.2787,'Lanxian',NULL,NULL),(141128,'方山县',141100,'方山',3,'0358','033100','中国,山西省,吕梁市,方山县',111.24,37.8898,'Fangshan',NULL,NULL),(141129,'中阳县',141100,'中阳',3,'0358','033400','中国,山西省,吕梁市,中阳县',111.179,37.3572,'Zhongyang',NULL,NULL),(141130,'交口县',141100,'交口',3,'0358','032400','中国,山西省,吕梁市,交口县',111.181,36.9821,'Jiaokou',NULL,NULL),(141181,'孝义市',141100,'孝义',3,'0358','032300','中国,山西省,吕梁市,孝义市',111.774,37.1441,'Xiaoyi',NULL,NULL),(141182,'汾阳市',141100,'汾阳',3,'0358','032200','中国,山西省,吕梁市,汾阳市',111.788,37.266,'Fenyang',NULL,NULL),(150000,'内蒙古自治区',100000,'内蒙古',1,'','','中国,内蒙古自治区',111.671,40.8183,'Inner Mongolia',NULL,NULL),(150100,'呼和浩特市',150000,'呼和浩特',2,'0471','010000','中国,内蒙古自治区,呼和浩特市',111.671,40.8183,'Hohhot',NULL,NULL),(150102,'新城区',150100,'新城',3,'0471','010050','中国,内蒙古自治区,呼和浩特市,新城区',111.666,40.8583,'Xincheng',NULL,NULL),(150103,'回民区',150100,'回民',3,'0471','010030','中国,内蒙古自治区,呼和浩特市,回民区',111.624,40.8083,'Huimin',NULL,NULL),(150104,'玉泉区',150100,'玉泉',3,'0471','010020','中国,内蒙古自治区,呼和浩特市,玉泉区',111.675,40.7523,'Yuquan',NULL,NULL),(150105,'赛罕区',150100,'赛罕',3,'0471','010020','中国,内蒙古自治区,呼和浩特市,赛罕区',111.702,40.7921,'Saihan',NULL,NULL),(150121,'土默特左旗',150100,'土默特左旗',3,'0471','010100','中国,内蒙古自治区,呼和浩特市,土默特左旗',111.149,40.7223,'Tumotezuoqi',NULL,NULL),(150122,'托克托县',150100,'托克托',3,'0471','010200','中国,内蒙古自治区,呼和浩特市,托克托县',111.191,40.2749,'Tuoketuo',NULL,NULL),(150123,'和林格尔县',150100,'和林格尔',3,'0471','011500','中国,内蒙古自治区,呼和浩特市,和林格尔县',111.822,40.3789,'Helingeer',NULL,NULL),(150124,'清水河县',150100,'清水河',3,'0471','011600','中国,内蒙古自治区,呼和浩特市,清水河县',111.683,39.9097,'Qingshuihe',NULL,NULL),(150125,'武川县',150100,'武川',3,'0471','011700','中国,内蒙古自治区,呼和浩特市,武川县',111.458,41.0929,'Wuchuan',NULL,NULL),(150200,'包头市',150000,'包头',2,'0472','014025','中国,内蒙古自治区,包头市',109.84,40.6582,'Baotou',NULL,NULL),(150202,'东河区',150200,'东河',3,'0472','014040','中国,内蒙古自治区,包头市,东河区',110.046,40.5824,'Donghe',NULL,NULL),(150203,'昆都仑区',150200,'昆都仑',3,'0472','014010','中国,内蒙古自治区,包头市,昆都仑区',109.839,40.6418,'Kundulun',NULL,NULL),(150204,'青山区',150200,'青山',3,'0472','014030','中国,内蒙古自治区,包头市,青山区',109.901,40.6433,'Qingshan',NULL,NULL),(150205,'石拐区',150200,'石拐',3,'0472','014070','中国,内蒙古自治区,包头市,石拐区',110.273,40.673,'Shiguai',NULL,NULL),(150206,'白云鄂博矿区',150200,'白云鄂博矿区',3,'0472','014080','中国,内蒙古自治区,包头市,白云鄂博矿区',109.974,41.7697,'Baiyunebokuangqu',NULL,NULL),(150207,'九原区',150200,'九原',3,'0472','014060','中国,内蒙古自治区,包头市,九原区',109.965,40.6055,'Jiuyuan',NULL,NULL),(150221,'土默特右旗',150200,'土默特右旗',3,'0472','014100','中国,内蒙古自治区,包头市,土默特右旗',110.524,40.5688,'Tumoteyouqi',NULL,NULL),(150222,'固阳县',150200,'固阳',3,'0472','014200','中国,内蒙古自治区,包头市,固阳县',110.064,41.0185,'Guyang',NULL,NULL),(150223,'达尔罕茂明安联合旗',150200,'达茂旗',3,'0472','014500','中国,内蒙古自治区,包头市,达尔罕茂明安联合旗',110.433,41.6987,'Damaoqi',NULL,NULL),(150300,'乌海市',150000,'乌海',2,'0473','016000','中国,内蒙古自治区,乌海市',106.826,39.6737,'Wuhai',NULL,NULL),(150302,'海勃湾区',150300,'海勃湾',3,'0473','016000','中国,内蒙古自治区,乌海市,海勃湾区',106.822,39.6696,'Haibowan',NULL,NULL),(150303,'海南区',150300,'海南',3,'0473','016030','中国,内蒙古自治区,乌海市,海南区',106.887,39.4413,'Hainan',NULL,NULL),(150304,'乌达区',150300,'乌达',3,'0473','016040','中国,内蒙古自治区,乌海市,乌达区',106.727,39.505,'Wuda',NULL,NULL),(150400,'赤峰市',150000,'赤峰',2,'0476','024000','中国,内蒙古自治区,赤峰市',118.957,42.2753,'Chifeng',NULL,NULL),(150402,'红山区',150400,'红山',3,'0476','024020','中国,内蒙古自治区,赤峰市,红山区',118.958,42.2431,'Hongshan',NULL,NULL),(150403,'元宝山区',150400,'元宝山',3,'0476','024076','中国,内蒙古自治区,赤峰市,元宝山区',119.289,42.0401,'Yuanbaoshan',NULL,NULL),(150404,'松山区',150400,'松山',3,'0476','024005','中国,内蒙古自治区,赤峰市,松山区',118.933,42.2861,'Songshan',NULL,NULL),(150421,'阿鲁科尔沁旗',150400,'阿鲁科尔沁旗',3,'0476','025550','中国,内蒙古自治区,赤峰市,阿鲁科尔沁旗',120.065,43.8799,'Alukeerqinqi',NULL,NULL),(150422,'巴林左旗',150400,'巴林左旗',3,'0476','025450','中国,内蒙古自治区,赤峰市,巴林左旗',119.38,43.9703,'Balinzuoqi',NULL,NULL),(150423,'巴林右旗',150400,'巴林右旗',3,'0476','025150','中国,内蒙古自治区,赤峰市,巴林右旗',118.665,43.5339,'Balinyouqi',NULL,NULL),(150424,'林西县',150400,'林西',3,'0476','025250','中国,内蒙古自治区,赤峰市,林西县',118.047,43.6116,'Linxi',NULL,NULL),(150425,'克什克腾旗',150400,'克什克腾旗',3,'0476','025350','中国,内蒙古自治区,赤峰市,克什克腾旗',117.546,43.265,'Keshiketengqi',NULL,NULL),(150426,'翁牛特旗',150400,'翁牛特旗',3,'0476','024500','中国,内蒙古自治区,赤峰市,翁牛特旗',119.03,42.9315,'Wengniuteqi',NULL,NULL),(150428,'喀喇沁旗',150400,'喀喇沁旗',3,'0476','024400','中国,内蒙古自治区,赤峰市,喀喇沁旗',118.701,41.9292,'Kalaqinqi',NULL,NULL),(150429,'宁城县',150400,'宁城',3,'0476','024200','中国,内蒙古自治区,赤峰市,宁城县',119.344,41.5966,'Ningcheng',NULL,NULL),(150430,'敖汉旗',150400,'敖汉旗',3,'0476','024300','中国,内蒙古自治区,赤峰市,敖汉旗',119.922,42.2907,'Aohanqi',NULL,NULL),(150500,'通辽市',150000,'通辽',2,'0475','028000','中国,内蒙古自治区,通辽市',122.263,43.6174,'Tongliao',NULL,NULL),(150502,'科尔沁区',150500,'科尔沁',3,'0475','028000','中国,内蒙古自治区,通辽市,科尔沁区',122.256,43.6226,'Keerqin',NULL,NULL),(150521,'科尔沁左翼中旗',150500,'科尔沁左翼中旗',3,'0475','029300','中国,内蒙古自治区,通辽市,科尔沁左翼中旗',123.319,44.1301,'Keerqinzuoyizhongqi',NULL,NULL),(150522,'科尔沁左翼后旗',150500,'科尔沁左翼后旗',3,'0475','028100','中国,内蒙古自治区,通辽市,科尔沁左翼后旗',122.357,42.949,'Keerqinzuoyihouqi',NULL,NULL),(150523,'开鲁县',150500,'开鲁',3,'0475','028400','中国,内蒙古自治区,通辽市,开鲁县',121.319,43.6,'Kailu',NULL,NULL),(150524,'库伦旗',150500,'库伦旗',3,'0475','028200','中国,内蒙古自治区,通辽市,库伦旗',121.776,42.73,'Kulunqi',NULL,NULL),(150525,'奈曼旗',150500,'奈曼旗',3,'0475','028300','中国,内蒙古自治区,通辽市,奈曼旗',120.663,42.8453,'Naimanqi',NULL,NULL),(150526,'扎鲁特旗',150500,'扎鲁特旗',3,'0475','029100','中国,内蒙古自治区,通辽市,扎鲁特旗',120.915,44.5559,'Zhaluteqi',NULL,NULL),(150581,'霍林郭勒市',150500,'霍林郭勒',3,'0475','029200','中国,内蒙古自治区,通辽市,霍林郭勒市',119.654,45.5345,'Huolinguole',NULL,NULL),(150600,'鄂尔多斯市',150000,'鄂尔多斯',2,'0477','017004','中国,内蒙古自治区,鄂尔多斯市',109.99,39.8172,'Ordos',NULL,NULL),(150602,'东胜区',150600,'东胜',3,'0477','017000','中国,内蒙古自治区,鄂尔多斯市,东胜区',109.963,39.8224,'Dongsheng',NULL,NULL),(150621,'达拉特旗',150600,'达拉特旗',3,'0477','014300','中国,内蒙古自治区,鄂尔多斯市,达拉特旗',110.033,40.4001,'Dalateqi',NULL,NULL),(150622,'准格尔旗',150600,'准格尔旗',3,'0477','017100','中国,内蒙古自治区,鄂尔多斯市,准格尔旗',111.236,39.8678,'Zhungeerqi',NULL,NULL),(150623,'鄂托克前旗',150600,'鄂托克前旗',3,'0477','016200','中国,内蒙古自治区,鄂尔多斯市,鄂托克前旗',107.484,38.184,'Etuokeqianqi',NULL,NULL),(150624,'鄂托克旗',150600,'鄂托克旗',3,'0477','016100','中国,内蒙古自治区,鄂尔多斯市,鄂托克旗',107.982,39.0946,'Etuokeqi',NULL,NULL),(150625,'杭锦旗',150600,'杭锦旗',3,'0477','017400','中国,内蒙古自治区,鄂尔多斯市,杭锦旗',108.729,39.8402,'Hangjinqi',NULL,NULL),(150626,'乌审旗',150600,'乌审旗',3,'0477','017300','中国,内蒙古自治区,鄂尔多斯市,乌审旗',108.846,38.5909,'Wushenqi',NULL,NULL),(150627,'伊金霍洛旗',150600,'伊金霍洛旗',3,'0477','017200','中国,内蒙古自治区,鄂尔多斯市,伊金霍洛旗',109.749,39.5739,'Yijinhuoluoqi',NULL,NULL),(150700,'呼伦贝尔市',150000,'呼伦贝尔',2,'0470','021008','中国,内蒙古自治区,呼伦贝尔市',119.758,49.2153,'Hulunber',NULL,NULL),(150702,'海拉尔区',150700,'海拉尔',3,'0470','021000','中国,内蒙古自治区,呼伦贝尔市,海拉尔区',119.736,49.2122,'Hailaer',NULL,NULL),(150703,'扎赉诺尔区',150700,'扎赉诺尔',3,'0470','021410','中国,内蒙古自治区,呼伦贝尔市,扎赉诺尔区',117.793,49.4869,'Zhalainuoer',NULL,NULL),(150721,'阿荣旗',150700,'阿荣旗',3,'0470','162750','中国,内蒙古自治区,呼伦贝尔市,阿荣旗',123.459,48.1258,'Arongqi',NULL,NULL),(150722,'莫力达瓦达斡尔族自治旗',150700,'莫旗',3,'0470','162850','中国,内蒙古自治区,呼伦贝尔市,莫力达瓦达斡尔族自治旗',124.515,48.4805,'Moqi',NULL,NULL),(150723,'鄂伦春自治旗',150700,'鄂伦春',3,'0470','165450','中国,内蒙古自治区,呼伦贝尔市,鄂伦春自治旗',123.726,50.5978,'Elunchun',NULL,NULL),(150724,'鄂温克族自治旗',150700,'鄂温',3,'0470','021100','中国,内蒙古自治区,呼伦贝尔市,鄂温克族自治旗',119.757,49.1428,'Ewen',NULL,NULL),(150725,'陈巴尔虎旗',150700,'陈巴尔虎旗',3,'0470','021500','中国,内蒙古自治区,呼伦贝尔市,陈巴尔虎旗',119.424,49.3268,'Chenbaerhuqi',NULL,NULL),(150726,'新巴尔虎左旗',150700,'新巴尔虎左旗',3,'0470','021200','中国,内蒙古自治区,呼伦贝尔市,新巴尔虎左旗',118.27,48.2184,'Xinbaerhuzuoqi',NULL,NULL),(150727,'新巴尔虎右旗',150700,'新巴尔虎右旗',3,'0470','021300','中国,内蒙古自治区,呼伦贝尔市,新巴尔虎右旗',116.824,48.6647,'Xinbaerhuyouqi',NULL,NULL),(150781,'满洲里市',150700,'满洲里',3,'0470','021400','中国,内蒙古自治区,呼伦贝尔市,满洲里市',117.479,49.5827,'Manzhouli',NULL,NULL),(150782,'牙克石市',150700,'牙克石',3,'0470','022150','中国,内蒙古自治区,呼伦贝尔市,牙克石市',120.712,49.2856,'Yakeshi',NULL,NULL),(150783,'扎兰屯市',150700,'扎兰屯',3,'0470','162650','中国,内蒙古自治区,呼伦贝尔市,扎兰屯市',122.738,48.0136,'Zhalantun',NULL,NULL),(150784,'额尔古纳市',150700,'额尔古纳',3,'0470','022250','中国,内蒙古自治区,呼伦贝尔市,额尔古纳市',120.191,50.2425,'Eerguna',NULL,NULL),(150785,'根河市',150700,'根河',3,'0470','022350','中国,内蒙古自治区,呼伦贝尔市,根河市',121.522,50.78,'Genhe',NULL,NULL),(150800,'巴彦淖尔市',150000,'巴彦淖尔',2,'0478','015001','中国,内蒙古自治区,巴彦淖尔市',107.417,40.7574,'Bayan Nur',NULL,NULL),(150802,'临河区',150800,'临河',3,'0478','015001','中国,内蒙古自治区,巴彦淖尔市,临河区',107.427,40.7583,'Linhe',NULL,NULL),(150821,'五原县',150800,'五原',3,'0478','015100','中国,内蒙古自治区,巴彦淖尔市,五原县',108.269,41.0963,'Wuyuan',NULL,NULL),(150822,'磴口县',150800,'磴口',3,'0478','015200','中国,内蒙古自治区,巴彦淖尔市,磴口县',107.009,40.3306,'Dengkou',NULL,NULL),(150823,'乌拉特前旗',150800,'乌拉特前旗',3,'0478','014400','中国,内蒙古自治区,巴彦淖尔市,乌拉特前旗',108.652,40.7365,'Wulateqianqi',NULL,NULL),(150824,'乌拉特中旗',150800,'乌拉特中旗',3,'0478','015300','中国,内蒙古自治区,巴彦淖尔市,乌拉特中旗',108.526,41.5679,'Wulatezhongqi',NULL,NULL),(150825,'乌拉特后旗',150800,'乌拉特后旗',3,'0478','015500','中国,内蒙古自治区,巴彦淖尔市,乌拉特后旗',106.99,41.4315,'Wulatehouqi',NULL,NULL),(150826,'杭锦后旗',150800,'杭锦后旗',3,'0478','015400','中国,内蒙古自治区,巴彦淖尔市,杭锦后旗',107.151,40.8863,'Hangjinhouqi',NULL,NULL),(150900,'乌兰察布市',150000,'乌兰察布',2,'0474','012000','中国,内蒙古自治区,乌兰察布市',113.115,41.0341,'Ulanqab',NULL,NULL),(150902,'集宁区',150900,'集宁',3,'0474','012000','中国,内蒙古自治区,乌兰察布市,集宁区',113.115,41.0353,'Jining',NULL,NULL),(150921,'卓资县',150900,'卓资',3,'0474','012300','中国,内蒙古自治区,乌兰察布市,卓资县',112.578,40.8941,'Zhuozi',NULL,NULL),(150922,'化德县',150900,'化德',3,'0474','013350','中国,内蒙古自治区,乌兰察布市,化德县',114.011,41.9043,'Huade',NULL,NULL),(150923,'商都县',150900,'商都',3,'0474','013450','中国,内蒙古自治区,乌兰察布市,商都县',113.578,41.5621,'Shangdu',NULL,NULL),(150924,'兴和县',150900,'兴和',3,'0474','013650','中国,内蒙古自治区,乌兰察布市,兴和县',113.834,40.8719,'Xinghe',NULL,NULL),(150925,'凉城县',150900,'凉城',3,'0474','013750','中国,内蒙古自治区,乌兰察布市,凉城县',112.496,40.5335,'Liangcheng',NULL,NULL),(150926,'察哈尔右翼前旗',150900,'察右前旗',3,'0474','012200','中国,内蒙古自治区,乌兰察布市,察哈尔右翼前旗',113.221,40.7788,'Chayouqianqi',NULL,NULL),(150927,'察哈尔右翼中旗',150900,'察右中旗',3,'0474','013550','中国,内蒙古自治区,乌兰察布市,察哈尔右翼中旗',112.635,41.2774,'Chayouzhongqi',NULL,NULL),(150928,'察哈尔右翼后旗',150900,'察右后旗',3,'0474','012400','中国,内蒙古自治区,乌兰察布市,察哈尔右翼后旗',113.192,41.4355,'Chayouhouqi',NULL,NULL),(150929,'四子王旗',150900,'四子王旗',3,'0474','011800','中国,内蒙古自治区,乌兰察布市,四子王旗',111.707,41.5331,'Siziwangqi',NULL,NULL),(150981,'丰镇市',150900,'丰镇',3,'0474','012100','中国,内蒙古自治区,乌兰察布市,丰镇市',113.11,40.4369,'Fengzhen',NULL,NULL),(152200,'兴安盟',150000,'兴安盟',2,'0482','137401','中国,内蒙古自治区,兴安盟',122.07,46.0763,'Hinggan',NULL,NULL),(152201,'乌兰浩特市',152200,'乌兰浩特',3,'0482','137401','中国,内蒙古自治区,兴安盟,乌兰浩特市',122.064,46.0624,'Wulanhaote',NULL,NULL),(152202,'阿尔山市',152200,'阿尔山',3,'0482','137800','中国,内蒙古自治区,兴安盟,阿尔山市',119.943,47.1772,'Aershan',NULL,NULL),(152221,'科尔沁右翼前旗',152200,'科右前旗',3,'0482','137423','中国,内蒙古自治区,兴安盟,科尔沁右翼前旗',121.953,46.0795,'Keyouqianqi',NULL,NULL),(152222,'科尔沁右翼中旗',152200,'科右中旗',3,'0482','029400','中国,内蒙古自治区,兴安盟,科尔沁右翼中旗',121.468,45.056,'Keyouzhongqi',NULL,NULL),(152223,'扎赉特旗',152200,'扎赉特旗',3,'0482','137600','中国,内蒙古自治区,兴安盟,扎赉特旗',122.912,46.7267,'Zhalaiteqi',NULL,NULL),(152224,'突泉县',152200,'突泉',3,'0482','137500','中国,内蒙古自治区,兴安盟,突泉县',121.594,45.3819,'Tuquan',NULL,NULL),(152500,'锡林郭勒盟',150000,'锡林郭勒盟',2,'0479','026000','中国,内蒙古自治区,锡林郭勒盟',116.091,43.944,'Xilin Gol',NULL,NULL),(152501,'二连浩特市',152500,'二连浩特',3,'0479','011100','中国,内蒙古自治区,锡林郭勒盟,二连浩特市',111.983,43.653,'Erlianhaote',NULL,NULL),(152502,'锡林浩特市',152500,'锡林浩特',3,'0479','026021','中国,内蒙古自治区,锡林郭勒盟,锡林浩特市',116.086,43.9334,'Xilinhaote',NULL,NULL),(152522,'阿巴嘎旗',152500,'阿巴嘎旗',3,'0479','011400','中国,内蒙古自治区,锡林郭勒盟,阿巴嘎旗',114.968,44.0217,'Abagaqi',NULL,NULL),(152523,'苏尼特左旗',152500,'苏尼特左旗',3,'0479','011300','中国,内蒙古自治区,锡林郭勒盟,苏尼特左旗',113.651,43.8569,'Sunitezuoqi',NULL,NULL),(152524,'苏尼特右旗',152500,'苏尼特右旗',3,'0479','011200','中国,内蒙古自治区,锡林郭勒盟,苏尼特右旗',112.657,42.7469,'Suniteyouqi',NULL,NULL),(152525,'东乌珠穆沁旗',152500,'东乌旗',3,'0479','026300','中国,内蒙古自治区,锡林郭勒盟,东乌珠穆沁旗',116.973,45.5111,'Dongwuqi',NULL,NULL),(152526,'西乌珠穆沁旗',152500,'西乌旗',3,'0479','026200','中国,内蒙古自治区,锡林郭勒盟,西乌珠穆沁旗',117.61,44.5962,'Xiwuqi',NULL,NULL),(152527,'太仆寺旗',152500,'太仆寺旗',3,'0479','027000','中国,内蒙古自治区,锡林郭勒盟,太仆寺旗',115.283,41.8773,'Taipusiqi',NULL,NULL),(152528,'镶黄旗',152500,'镶黄旗',3,'0479','013250','中国,内蒙古自治区,锡林郭勒盟,镶黄旗',113.845,42.2393,'Xianghuangqi',NULL,NULL),(152529,'正镶白旗',152500,'正镶白旗',3,'0479','013800','中国,内蒙古自治区,锡林郭勒盟,正镶白旗',115.001,42.3071,'Zhengxiangbaiqi',NULL,NULL),(152530,'正蓝旗',152500,'正蓝旗',3,'0479','027200','中国,内蒙古自治区,锡林郭勒盟,正蓝旗',116.004,42.2523,'Zhenglanqi',NULL,NULL),(152531,'多伦县',152500,'多伦',3,'0479','027300','中国,内蒙古自治区,锡林郭勒盟,多伦县',116.486,42.203,'Duolun',NULL,NULL),(152900,'阿拉善盟',150000,'阿拉善盟',2,'0483','750306','中国,内蒙古自治区,阿拉善盟',105.706,38.8448,'Alxa',NULL,NULL),(152921,'阿拉善左旗',152900,'阿拉善左旗',3,'0483','750306','中国,内蒙古自治区,阿拉善盟,阿拉善左旗',105.675,38.8293,'Alashanzuoqi',NULL,NULL),(152922,'阿拉善右旗',152900,'阿拉善右旗',3,'0483','737300','中国,内蒙古自治区,阿拉善盟,阿拉善右旗',101.667,39.2153,'Alashanyouqi',NULL,NULL),(152923,'额济纳旗',152900,'额济纳旗',3,'0483','735400','中国,内蒙古自治区,阿拉善盟,额济纳旗',101.069,41.9675,'Ejinaqi',NULL,NULL),(210000,'辽宁省',100000,'辽宁',1,'','','中国,辽宁省',123.429,41.7968,'Liaoning',NULL,NULL),(210100,'沈阳市',210000,'沈阳',2,'024','110013','中国,辽宁省,沈阳市',123.429,41.7968,'Shenyang',NULL,NULL),(210102,'和平区',210100,'和平',3,'024','110001','中国,辽宁省,沈阳市,和平区',123.42,41.79,'Heping',NULL,NULL),(210103,'沈河区',210100,'沈河',3,'024','110011','中国,辽宁省,沈阳市,沈河区',123.459,41.7962,'Shenhe',NULL,NULL),(210104,'大东区',210100,'大东',3,'024','110041','中国,辽宁省,沈阳市,大东区',123.47,41.8054,'Dadong',NULL,NULL),(210105,'皇姑区',210100,'皇姑',3,'024','110031','中国,辽宁省,沈阳市,皇姑区',123.425,41.8204,'Huanggu',NULL,NULL),(210106,'铁西区',210100,'铁西',3,'024','110021','中国,辽宁省,沈阳市,铁西区',123.377,41.8027,'Tiexi',NULL,NULL),(210111,'苏家屯区',210100,'苏家屯',3,'024','110101','中国,辽宁省,沈阳市,苏家屯区',123.344,41.6647,'Sujiatun',NULL,NULL),(210112,'浑南区',210100,'浑南',3,'024','110015','中国,辽宁省,沈阳市,浑南区',123.458,41.7195,'Hunnan',NULL,NULL),(210113,'沈北新区',210100,'沈北新区',3,'024','110121','中国,辽宁省,沈阳市,沈北新区',123.527,42.053,'Shenbeixinqu',NULL,NULL),(210114,'于洪区',210100,'于洪',3,'024','110141','中国,辽宁省,沈阳市,于洪区',123.308,41.794,'Yuhong',NULL,NULL),(210122,'辽中县',210100,'辽中',3,'024','110200','中国,辽宁省,沈阳市,辽中县',122.727,41.513,'Liaozhong',NULL,NULL),(210123,'康平县',210100,'康平',3,'024','110500','中国,辽宁省,沈阳市,康平县',123.354,42.7508,'Kangping',NULL,NULL),(210124,'法库县',210100,'法库',3,'024','110400','中国,辽宁省,沈阳市,法库县',123.412,42.5061,'Faku',NULL,NULL),(210181,'新民市',210100,'新民',3,'024','110300','中国,辽宁省,沈阳市,新民市',122.829,41.9985,'Xinmin',NULL,NULL),(210200,'大连市',210000,'大连',2,'0411','116011','中国,辽宁省,大连市',121.619,38.9146,'Dalian',NULL,NULL),(210202,'中山区',210200,'中山',3,'0411','116001','中国,辽宁省,大连市,中山区',121.645,38.9186,'Zhongshan',NULL,NULL),(210203,'西岗区',210200,'西岗',3,'0411','116011','中国,辽宁省,大连市,西岗区',121.612,38.9147,'Xigang',NULL,NULL),(210204,'沙河口区',210200,'沙河口',3,'0411','116021','中国,辽宁省,大连市,沙河口区',121.58,38.9054,'Shahekou',NULL,NULL),(210211,'甘井子区',210200,'甘井子',3,'0411','116033','中国,辽宁省,大连市,甘井子区',121.566,38.9502,'Ganjingzi',NULL,NULL),(210212,'旅顺口区',210200,'旅顺口',3,'0411','116041','中国,辽宁省,大连市,旅顺口区',121.262,38.8512,'Lvshunkou',NULL,NULL),(210213,'金州区',210200,'金州',3,'0411','116100','中国,辽宁省,大连市,金州区',121.719,39.1004,'Jinzhou',NULL,NULL),(210224,'长海县',210200,'长海',3,'0411','116500','中国,辽宁省,大连市,长海县',122.589,39.2727,'Changhai',NULL,NULL),(210281,'瓦房店市',210200,'瓦房店',3,'0411','116300','中国,辽宁省,大连市,瓦房店市',121.981,39.6284,'Wafangdian',NULL,NULL),(210282,'普兰店市',210200,'普兰店',3,'0411','116200','中国,辽宁省,大连市,普兰店市',121.963,39.3946,'Pulandian',NULL,NULL),(210283,'庄河市',210200,'庄河',3,'0411','116400','中国,辽宁省,大连市,庄河市',122.967,39.6881,'Zhuanghe',NULL,NULL),(210300,'鞍山市',210000,'鞍山',2,'0412','114001','中国,辽宁省,鞍山市',122.996,41.1106,'Anshan',NULL,NULL),(210302,'铁东区',210300,'铁东',3,'0412','114001','中国,辽宁省,鞍山市,铁东区',122.991,41.0897,'Tiedong',NULL,NULL),(210303,'铁西区',210300,'铁西',3,'0413','114013','中国,辽宁省,鞍山市,铁西区',122.97,41.1198,'Tiexi',NULL,NULL),(210304,'立山区',210300,'立山',3,'0414','114031','中国,辽宁省,鞍山市,立山区',123.029,41.1501,'Lishan',NULL,NULL),(210311,'千山区',210300,'千山',3,'0415','114041','中国,辽宁省,鞍山市,千山区',122.96,41.0751,'Qianshan',NULL,NULL),(210321,'台安县',210300,'台安',3,'0417','114100','中国,辽宁省,鞍山市,台安县',122.436,41.4127,'Tai\'an',NULL,NULL),(210323,'岫岩满族自治县',210300,'岫岩',3,'0418','114300','中国,辽宁省,鞍山市,岫岩满族自治县',123.289,40.28,'Xiuyan',NULL,NULL),(210381,'海城市',210300,'海城',3,'0416','114200','中国,辽宁省,鞍山市,海城市',122.685,40.8814,'Haicheng',NULL,NULL),(210400,'抚顺市',210000,'抚顺',2,'024','113008','中国,辽宁省,抚顺市',123.921,41.876,'Fushun',NULL,NULL),(210402,'新抚区',210400,'新抚',3,'024','113008','中国,辽宁省,抚顺市,新抚区',123.913,41.862,'Xinfu',NULL,NULL),(210403,'东洲区',210400,'东洲',3,'024','113003','中国,辽宁省,抚顺市,东洲区',124.038,41.8519,'Dongzhou',NULL,NULL),(210404,'望花区',210400,'望花',3,'024','113001','中国,辽宁省,抚顺市,望花区',123.783,41.8553,'Wanghua',NULL,NULL),(210411,'顺城区',210400,'顺城',3,'024','113006','中国,辽宁省,抚顺市,顺城区',123.945,41.8832,'Shuncheng',NULL,NULL),(210421,'抚顺县',210400,'抚顺',3,'024','113006','中国,辽宁省,抚顺市,抚顺县',124.178,41.7122,'Fushun',NULL,NULL),(210422,'新宾满族自治县',210400,'新宾',3,'024','113200','中国,辽宁省,抚顺市,新宾满族自治县',125.04,41.7341,'Xinbin',NULL,NULL),(210423,'清原满族自治县',210400,'清原',3,'024','113300','中国,辽宁省,抚顺市,清原满族自治县',124.928,42.1022,'Qingyuan',NULL,NULL),(210500,'本溪市',210000,'本溪',2,'0414','117000','中国,辽宁省,本溪市',123.771,41.2979,'Benxi',NULL,NULL),(210502,'平山区',210500,'平山',3,'0414','117000','中国,辽宁省,本溪市,平山区',123.769,41.2997,'Pingshan',NULL,NULL),(210503,'溪湖区',210500,'溪湖',3,'0414','117002','中国,辽宁省,本溪市,溪湖区',123.768,41.3292,'Xihu',NULL,NULL),(210504,'明山区',210500,'明山',3,'0414','117021','中国,辽宁省,本溪市,明山区',123.817,41.3083,'Mingshan',NULL,NULL),(210505,'南芬区',210500,'南芬',3,'0414','117014','中国,辽宁省,本溪市,南芬区',123.745,41.1006,'Nanfen',NULL,NULL),(210521,'本溪满族自治县',210500,'本溪',3,'0414','117100','中国,辽宁省,本溪市,本溪满族自治县',124.127,41.3006,'Benxi',NULL,NULL),(210522,'桓仁满族自治县',210500,'桓仁',3,'0414','117200','中国,辽宁省,本溪市,桓仁满族自治县',125.361,41.268,'Huanren',NULL,NULL),(210600,'丹东市',210000,'丹东',2,'0415','118000','中国,辽宁省,丹东市',124.383,40.1243,'Dandong',NULL,NULL),(210602,'元宝区',210600,'元宝',3,'0415','118000','中国,辽宁省,丹东市,元宝区',124.396,40.1365,'Yuanbao',NULL,NULL),(210603,'振兴区',210600,'振兴',3,'0415','118002','中国,辽宁省,丹东市,振兴区',124.36,40.1049,'Zhenxing',NULL,NULL),(210604,'振安区',210600,'振安',3,'0415','118001','中国,辽宁省,丹东市,振安区',124.428,40.1583,'Zhen\'an',NULL,NULL),(210624,'宽甸满族自治县',210600,'宽甸',3,'0415','118200','中国,辽宁省,丹东市,宽甸满族自治县',124.782,40.7319,'Kuandian',NULL,NULL),(210681,'东港市',210600,'东港',3,'0415','118300','中国,辽宁省,丹东市,东港市',124.163,39.8626,'Donggang',NULL,NULL),(210682,'凤城市',210600,'凤城',3,'0415','118100','中国,辽宁省,丹东市,凤城市',124.067,40.453,'Fengcheng',NULL,NULL),(210700,'锦州市',210000,'锦州',2,'0416','121000','中国,辽宁省,锦州市',121.136,41.1193,'Jinzhou',NULL,NULL),(210702,'古塔区',210700,'古塔',3,'0416','121001','中国,辽宁省,锦州市,古塔区',121.128,41.1172,'Guta',NULL,NULL),(210703,'凌河区',210700,'凌河',3,'0416','121000','中国,辽宁省,锦州市,凌河区',121.151,41.115,'Linghe',NULL,NULL),(210711,'太和区',210700,'太和',3,'0416','121011','中国,辽宁省,锦州市,太和区',121.104,41.1093,'Taihe',NULL,NULL),(210726,'黑山县',210700,'黑山',3,'0416','121400','中国,辽宁省,锦州市,黑山县',122.121,41.6942,'Heishan',NULL,NULL),(210727,'义县',210700,'义县',3,'0416','121100','中国,辽宁省,锦州市,义县',121.24,41.5346,'Yixian',NULL,NULL),(210781,'凌海市',210700,'凌海',3,'0416','121200','中国,辽宁省,锦州市,凌海市',121.357,41.1737,'Linghai',NULL,NULL),(210782,'北镇市',210700,'北镇',3,'0416','121300','中国,辽宁省,锦州市,北镇市',121.799,41.5954,'Beizhen',NULL,NULL),(210800,'营口市',210000,'营口',2,'0417','115003','中国,辽宁省,营口市',122.235,40.6674,'Yingkou',NULL,NULL),(210802,'站前区',210800,'站前',3,'0417','115002','中国,辽宁省,营口市,站前区',122.259,40.6727,'Zhanqian',NULL,NULL),(210803,'西市区',210800,'西市',3,'0417','115004','中国,辽宁省,营口市,西市区',122.206,40.6664,'Xishi',NULL,NULL),(210804,'鲅鱼圈区',210800,'鲅鱼圈',3,'0417','115007','中国,辽宁省,营口市,鲅鱼圈区',122.133,40.2687,'Bayuquan',NULL,NULL),(210811,'老边区',210800,'老边',3,'0417','115005','中国,辽宁省,营口市,老边区',122.38,40.6803,'Laobian',NULL,NULL),(210881,'盖州市',210800,'盖州',3,'0417','115200','中国,辽宁省,营口市,盖州市',122.355,40.4045,'Gaizhou',NULL,NULL),(210882,'大石桥市',210800,'大石桥',3,'0417','115100','中国,辽宁省,营口市,大石桥市',122.509,40.6457,'Dashiqiao',NULL,NULL),(210900,'阜新市',210000,'阜新',2,'0418','123000','中国,辽宁省,阜新市',121.649,42.0118,'Fuxin',NULL,NULL),(210902,'海州区',210900,'海州',3,'0418','123000','中国,辽宁省,阜新市,海州区',121.656,42.0134,'Haizhou',NULL,NULL),(210903,'新邱区',210900,'新邱',3,'0418','123005','中国,辽宁省,阜新市,新邱区',121.793,42.0918,'Xinqiu',NULL,NULL),(210904,'太平区',210900,'太平',3,'0418','123003','中国,辽宁省,阜新市,太平区',121.679,42.0107,'Taiping',NULL,NULL),(210905,'清河门区',210900,'清河门',3,'0418','123006','中国,辽宁省,阜新市,清河门区',121.416,41.7831,'Qinghemen',NULL,NULL),(210911,'细河区',210900,'细河',3,'0418','123000','中国,辽宁省,阜新市,细河区',121.68,42.0253,'Xihe',NULL,NULL),(210921,'阜新蒙古族自治县',210900,'阜新',3,'0418','123100','中国,辽宁省,阜新市,阜新蒙古族自治县',121.758,42.0651,'Fuxin',NULL,NULL),(210922,'彰武县',210900,'彰武',3,'0418','123200','中国,辽宁省,阜新市,彰武县',122.54,42.3862,'Zhangwu',NULL,NULL),(211000,'辽阳市',210000,'辽阳',2,'0419','111000','中国,辽宁省,辽阳市',123.182,41.2694,'Liaoyang',NULL,NULL),(211002,'白塔区',211000,'白塔',3,'0419','111000','中国,辽宁省,辽阳市,白塔区',123.175,41.2702,'Baita',NULL,NULL),(211003,'文圣区',211000,'文圣',3,'0419','111000','中国,辽宁省,辽阳市,文圣区',123.185,41.2627,'Wensheng',NULL,NULL),(211004,'宏伟区',211000,'宏伟',3,'0419','111003','中国,辽宁省,辽阳市,宏伟区',123.193,41.2185,'Hongwei',NULL,NULL),(211005,'弓长岭区',211000,'弓长岭',3,'0419','111008','中国,辽宁省,辽阳市,弓长岭区',123.42,41.1518,'Gongchangling',NULL,NULL),(211011,'太子河区',211000,'太子河',3,'0419','111000','中国,辽宁省,辽阳市,太子河区',123.182,41.2534,'Taizihe',NULL,NULL),(211021,'辽阳县',211000,'辽阳',3,'0419','111200','中国,辽宁省,辽阳市,辽阳县',123.106,41.2054,'Liaoyang',NULL,NULL),(211081,'灯塔市',211000,'灯塔',3,'0419','111300','中国,辽宁省,辽阳市,灯塔市',123.339,41.4261,'Dengta',NULL,NULL),(211100,'盘锦市',210000,'盘锦',2,'0427','124010','中国,辽宁省,盘锦市',122.07,41.1245,'Panjin',NULL,NULL),(211102,'双台子区',211100,'双台子',3,'0427','124000','中国,辽宁省,盘锦市,双台子区',122.06,41.1906,'Shuangtaizi',NULL,NULL),(211103,'兴隆台区',211100,'兴隆台',3,'0427','124010','中国,辽宁省,盘锦市,兴隆台区',122.075,41.124,'Xinglongtai',NULL,NULL),(211121,'大洼县',211100,'大洼',3,'0427','124200','中国,辽宁省,盘锦市,大洼县',122.082,41.0024,'Dawa',NULL,NULL),(211122,'盘山县',211100,'盘山',3,'0427','124000','中国,辽宁省,盘锦市,盘山县',121.998,41.238,'Panshan',NULL,NULL),(211200,'铁岭市',210000,'铁岭',2,'024','112000','中国,辽宁省,铁岭市',123.844,42.2906,'Tieling',NULL,NULL),(211202,'银州区',211200,'银州',3,'024','112000','中国,辽宁省,铁岭市,银州区',123.857,42.2951,'Yinzhou',NULL,NULL),(211204,'清河区',211200,'清河',3,'024','112003','中国,辽宁省,铁岭市,清河区',124.159,42.5468,'Qinghe',NULL,NULL),(211221,'铁岭县',211200,'铁岭',3,'024','112000','中国,辽宁省,铁岭市,铁岭县',123.773,42.225,'Tieling',NULL,NULL),(211223,'西丰县',211200,'西丰',3,'024','112400','中国,辽宁省,铁岭市,西丰县',124.73,42.7376,'Xifeng',NULL,NULL),(211224,'昌图县',211200,'昌图',3,'024','112500','中国,辽宁省,铁岭市,昌图县',124.112,42.7843,'Changtu',NULL,NULL),(211281,'调兵山市',211200,'调兵山',3,'024','112700','中国,辽宁省,铁岭市,调兵山市',123.567,42.4675,'Diaobingshan',NULL,NULL),(211282,'开原市',211200,'开原',3,'024','112300','中国,辽宁省,铁岭市,开原市',124.039,42.5458,'Kaiyuan',NULL,NULL),(211300,'朝阳市',210000,'朝阳',2,'0421','122000','中国,辽宁省,朝阳市',120.451,41.5768,'Chaoyang',NULL,NULL),(211302,'双塔区',211300,'双塔',3,'0421','122000','中国,辽宁省,朝阳市,双塔区',120.454,41.566,'Shuangta',NULL,NULL),(211303,'龙城区',211300,'龙城',3,'0421','122000','中国,辽宁省,朝阳市,龙城区',120.437,41.5926,'Longcheng',NULL,NULL),(211321,'朝阳县',211300,'朝阳',3,'0421','122000','中国,辽宁省,朝阳市,朝阳县',120.174,41.4324,'Chaoyang',NULL,NULL),(211322,'建平县',211300,'建平',3,'0421','122400','中国,辽宁省,朝阳市,建平县',119.644,41.4031,'Jianping',NULL,NULL),(211324,'喀喇沁左翼蒙古族自治县',211300,'喀喇沁左翼',3,'0421','122300','中国,辽宁省,朝阳市,喀喇沁左翼蒙古族自治县',119.742,41.128,'Kalaqinzuoyi',NULL,NULL),(211381,'北票市',211300,'北票',3,'0421','122100','中国,辽宁省,朝阳市,北票市',120.77,41.802,'Beipiao',NULL,NULL),(211382,'凌源市',211300,'凌源',3,'0421','122500','中国,辽宁省,朝阳市,凌源市',119.401,41.2456,'Lingyuan',NULL,NULL),(211400,'葫芦岛市',210000,'葫芦岛',2,'0429','125000','中国,辽宁省,葫芦岛市',120.856,40.7556,'Huludao',NULL,NULL),(211402,'连山区',211400,'连山',3,'0429','125001','中国,辽宁省,葫芦岛市,连山区',120.864,40.7555,'Lianshan',NULL,NULL),(211403,'龙港区',211400,'龙港',3,'0429','125003','中国,辽宁省,葫芦岛市,龙港区',120.949,40.7192,'Longgang',NULL,NULL),(211404,'南票区',211400,'南票',3,'0429','125027','中国,辽宁省,葫芦岛市,南票区',120.75,41.1071,'Nanpiao',NULL,NULL),(211421,'绥中县',211400,'绥中',3,'0429','125200','中国,辽宁省,葫芦岛市,绥中县',120.345,40.3255,'Suizhong',NULL,NULL),(211422,'建昌县',211400,'建昌',3,'0429','125300','中国,辽宁省,葫芦岛市,建昌县',119.838,40.8245,'Jianchang',NULL,NULL),(211481,'兴城市',211400,'兴城',3,'0429','125100','中国,辽宁省,葫芦岛市,兴城市',120.725,40.6149,'Xingcheng',NULL,NULL),(211500,'金普新区',210000,'金普新区',2,'0411','116100','中国,辽宁省,金普新区',121.79,39.0555,'Jinpuxinqu',NULL,NULL),(211501,'金州新区',211500,'金州新区',3,'0411','116100','中国,辽宁省,金普新区,金州新区',121.785,39.0523,'Jinzhouxinqu',NULL,NULL),(211502,'普湾新区',211500,'普湾新区',3,'0411','116200','中国,辽宁省,金普新区,普湾新区',121.813,39.3301,'Puwanxinqu',NULL,NULL),(211503,'保税区',211500,'保税区',3,'0411','116100','中国,辽宁省,金普新区,保税区',121.943,39.2246,'Baoshuiqu',NULL,NULL),(220000,'吉林省',100000,'吉林',1,'','','中国,吉林省',125.325,43.8868,'Jilin',NULL,NULL),(220100,'长春市',220000,'长春',2,'0431','130022','中国,吉林省,长春市',125.325,43.8868,'Changchun',NULL,NULL),(220102,'南关区',220100,'南关',3,'0431','130022','中国,吉林省,长春市,南关区',125.35,43.864,'Nanguan',NULL,NULL),(220103,'宽城区',220100,'宽城',3,'0431','130051','中国,吉林省,长春市,宽城区',125.326,43.9018,'Kuancheng',NULL,NULL),(220104,'朝阳区',220100,'朝阳',3,'0431','130012','中国,吉林省,长春市,朝阳区',125.288,43.8334,'Chaoyang',NULL,NULL),(220105,'二道区',220100,'二道',3,'0431','130031','中国,吉林省,长春市,二道区',125.374,43.865,'Erdao',NULL,NULL),(220106,'绿园区',220100,'绿园',3,'0431','130062','中国,吉林省,长春市,绿园区',125.256,43.8805,'Lvyuan',NULL,NULL),(220112,'双阳区',220100,'双阳',3,'0431','130600','中国,吉林省,长春市,双阳区',125.656,43.528,'Shuangyang',NULL,NULL),(220113,'九台区',220100,'九台',3,'0431','130500','中国,吉林省,长春市,九台区',125.84,44.1516,'Jiutai',NULL,NULL),(220122,'农安县',220100,'农安',3,'0431','130200','中国,吉林省,长春市,农安县',125.185,44.4327,'Nong\'an',NULL,NULL),(220182,'榆树市',220100,'榆树',3,'0431','130400','中国,吉林省,长春市,榆树市',126.557,44.8252,'Yushu',NULL,NULL),(220183,'德惠市',220100,'德惠',3,'0431','130300','中国,吉林省,长春市,德惠市',125.705,44.5372,'Dehui',NULL,NULL),(220200,'吉林市',220000,'吉林',2,'0432','132011','中国,吉林省,吉林市',126.553,43.8436,'Jilin',NULL,NULL),(220202,'昌邑区',220200,'昌邑',3,'0432','132002','中国,吉林省,吉林市,昌邑区',126.574,43.8818,'Changyi',NULL,NULL),(220203,'龙潭区',220200,'龙潭',3,'0432','132021','中国,吉林省,吉林市,龙潭区',126.562,43.9105,'Longtan',NULL,NULL),(220204,'船营区',220200,'船营',3,'0432','132011','中国,吉林省,吉林市,船营区',126.541,43.8334,'Chuanying',NULL,NULL),(220211,'丰满区',220200,'丰满',3,'0432','132013','中国,吉林省,吉林市,丰满区',126.562,43.8224,'Fengman',NULL,NULL),(220221,'永吉县',220200,'永吉',3,'0432','132200','中国,吉林省,吉林市,永吉县',126.496,43.672,'Yongji',NULL,NULL),(220281,'蛟河市',220200,'蛟河',3,'0432','132500','中国,吉林省,吉林市,蛟河市',127.344,43.727,'Jiaohe',NULL,NULL),(220282,'桦甸市',220200,'桦甸',3,'0432','132400','中国,吉林省,吉林市,桦甸市',126.746,42.9721,'Huadian',NULL,NULL),(220283,'舒兰市',220200,'舒兰',3,'0432','132600','中国,吉林省,吉林市,舒兰市',126.965,44.4058,'Shulan',NULL,NULL),(220284,'磐石市',220200,'磐石',3,'0432','132300','中国,吉林省,吉林市,磐石市',126.062,42.9463,'Panshi',NULL,NULL),(220300,'四平市',220000,'四平',2,'0434','136000','中国,吉林省,四平市',124.371,43.1703,'Siping',NULL,NULL),(220302,'铁西区',220300,'铁西',3,'0434','136000','中国,吉林省,四平市,铁西区',124.374,43.1746,'Tiexi',NULL,NULL),(220303,'铁东区',220300,'铁东',3,'0434','136001','中国,吉林省,四平市,铁东区',124.41,43.1624,'Tiedong',NULL,NULL),(220322,'梨树县',220300,'梨树',3,'0434','136500','中国,吉林省,四平市,梨树县',124.336,43.3072,'Lishu',NULL,NULL),(220323,'伊通满族自治县',220300,'伊通',3,'0434','130700','中国,吉林省,四平市,伊通满族自治县',125.306,43.3443,'Yitong',NULL,NULL),(220381,'公主岭市',220300,'公主岭',3,'0434','136100','中国,吉林省,四平市,公主岭市',124.823,43.5045,'Gongzhuling',NULL,NULL),(220382,'双辽市',220300,'双辽',3,'0434','136400','中国,吉林省,四平市,双辽市',123.501,43.521,'Shuangliao',NULL,NULL),(220400,'辽源市',220000,'辽源',2,'0437','136200','中国,吉林省,辽源市',125.145,42.9027,'Liaoyuan',NULL,NULL),(220402,'龙山区',220400,'龙山',3,'0437','136200','中国,吉林省,辽源市,龙山区',125.136,42.8971,'Longshan',NULL,NULL),(220403,'西安区',220400,'西安',3,'0437','136201','中国,吉林省,辽源市,西安区',125.149,42.927,'Xi\'an',NULL,NULL),(220421,'东丰县',220400,'东丰',3,'0437','136300','中国,吉林省,辽源市,东丰县',125.532,42.6783,'Dongfeng',NULL,NULL),(220422,'东辽县',220400,'东辽',3,'0437','136600','中国,吉林省,辽源市,东辽县',124.986,42.9249,'Dongliao',NULL,NULL),(220500,'通化市',220000,'通化',2,'0435','134001','中国,吉林省,通化市',125.937,41.7212,'Tonghua',NULL,NULL),(220502,'东昌区',220500,'东昌',3,'0435','134001','中国,吉林省,通化市,东昌区',125.955,41.7285,'Dongchang',NULL,NULL),(220503,'二道江区',220500,'二道江',3,'0435','134003','中国,吉林省,通化市,二道江区',126.043,41.7741,'Erdaojiang',NULL,NULL),(220521,'通化县',220500,'通化',3,'0435','134100','中国,吉林省,通化市,通化县',125.759,41.6793,'Tonghua',NULL,NULL),(220523,'辉南县',220500,'辉南',3,'0435','135100','中国,吉林省,通化市,辉南县',126.047,42.685,'Huinan',NULL,NULL),(220524,'柳河县',220500,'柳河',3,'0435','135300','中国,吉林省,通化市,柳河县',125.745,42.2847,'Liuhe',NULL,NULL),(220581,'梅河口市',220500,'梅河口',3,'0435','135000','中国,吉林省,通化市,梅河口市',125.71,42.5383,'Meihekou',NULL,NULL),(220582,'集安市',220500,'集安',3,'0435','134200','中国,吉林省,通化市,集安市',126.188,41.1227,'Ji\'an',NULL,NULL),(220600,'白山市',220000,'白山',2,'0439','134300','中国,吉林省,白山市',126.428,41.9425,'Baishan',NULL,NULL),(220602,'浑江区',220600,'浑江',3,'0439','134300','中国,吉林省,白山市,浑江区',126.422,41.9457,'Hunjiang',NULL,NULL),(220605,'江源区',220600,'江源',3,'0439','134700','中国,吉林省,白山市,江源区',126.591,42.0566,'Jiangyuan',NULL,NULL),(220621,'抚松县',220600,'抚松',3,'0439','134500','中国,吉林省,白山市,抚松县',127.28,42.342,'Fusong',NULL,NULL),(220622,'靖宇县',220600,'靖宇',3,'0439','135200','中国,吉林省,白山市,靖宇县',126.813,42.3886,'Jingyu',NULL,NULL),(220623,'长白朝鲜族自治县',220600,'长白',3,'0439','134400','中国,吉林省,白山市,长白朝鲜族自治县',128.2,41.42,'Changbai',NULL,NULL),(220681,'临江市',220600,'临江',3,'0439','134600','中国,吉林省,白山市,临江市',126.918,41.8114,'Linjiang',NULL,NULL),(220700,'松原市',220000,'松原',2,'0438','138000','中国,吉林省,松原市',124.824,45.1182,'Songyuan',NULL,NULL),(220702,'宁江区',220700,'宁江',3,'0438','138000','中国,吉林省,松原市,宁江区',124.817,45.1717,'Ningjiang',NULL,NULL),(220721,'前郭尔罗斯蒙古族自治县',220700,'前郭尔罗斯',3,'0438','138000','中国,吉林省,松原市,前郭尔罗斯蒙古族自治县',124.824,45.1173,'Qianguoerluosi',NULL,NULL),(220722,'长岭县',220700,'长岭',3,'0438','131500','中国,吉林省,松原市,长岭县',123.967,44.2758,'Changling',NULL,NULL),(220723,'乾安县',220700,'乾安',3,'0438','131400','中国,吉林省,松原市,乾安县',124.027,45.0107,'Qian\'an',NULL,NULL),(220781,'扶余市',220700,'扶余',3,'0438','131200','中国,吉林省,松原市,扶余市',126.043,44.9862,'Fuyu',NULL,NULL),(220800,'白城市',220000,'白城',2,'0436','137000','中国,吉林省,白城市',122.841,45.619,'Baicheng',NULL,NULL),(220802,'洮北区',220800,'洮北',3,'0436','137000','中国,吉林省,白城市,洮北区',122.851,45.6217,'Taobei',NULL,NULL),(220821,'镇赉县',220800,'镇赉',3,'0436','137300','中国,吉林省,白城市,镇赉县',123.199,45.8478,'Zhenlai',NULL,NULL),(220822,'通榆县',220800,'通榆',3,'0436','137200','中国,吉林省,白城市,通榆县',123.088,44.8139,'Tongyu',NULL,NULL),(220881,'洮南市',220800,'洮南',3,'0436','137100','中国,吉林省,白城市,洮南市',122.788,45.335,'Taonan',NULL,NULL),(220882,'大安市',220800,'大安',3,'0436','131300','中国,吉林省,白城市,大安市',124.295,45.5085,'Da\'an',NULL,NULL),(222400,'延边朝鲜族自治州',220000,'延边',2,'0433','133000','中国,吉林省,延边朝鲜族自治州',129.513,42.9048,'Yanbian',NULL,NULL),(222401,'延吉市',222400,'延吉',3,'0433','133000','中国,吉林省,延边朝鲜族自治州,延吉市',129.514,42.9068,'Yanji',NULL,NULL),(222402,'图们市',222400,'图们',3,'0433','133100','中国,吉林省,延边朝鲜族自治州,图们市',129.844,42.968,'Tumen',NULL,NULL),(222403,'敦化市',222400,'敦化',3,'0433','133700','中国,吉林省,延边朝鲜族自治州,敦化市',128.232,43.373,'Dunhua',NULL,NULL),(222404,'珲春市',222400,'珲春',3,'0433','133300','中国,吉林省,延边朝鲜族自治州,珲春市',130.366,42.8624,'Hunchun',NULL,NULL),(222405,'龙井市',222400,'龙井',3,'0433','133400','中国,吉林省,延边朝鲜族自治州,龙井市',129.426,42.768,'Longjing',NULL,NULL),(222406,'和龙市',222400,'和龙',3,'0433','133500','中国,吉林省,延边朝鲜族自治州,和龙市',129.011,42.5464,'Helong',NULL,NULL),(222424,'汪清县',222400,'汪清',3,'0433','133200','中国,吉林省,延边朝鲜族自治州,汪清县',129.771,43.3128,'Wangqing',NULL,NULL),(222426,'安图县',222400,'安图',3,'0433','133600','中国,吉林省,延边朝鲜族自治州,安图县',128.906,43.1153,'Antu',NULL,NULL),(230000,'黑龙江省',100000,'黑龙江',1,'','','中国,黑龙江省',126.642,45.757,'Heilongjiang',NULL,NULL),(230100,'哈尔滨市',230000,'哈尔滨',2,'0451','150010','中国,黑龙江省,哈尔滨市',126.642,45.757,'Harbin',NULL,NULL),(230102,'道里区',230100,'道里',3,'0451','150010','中国,黑龙江省,哈尔滨市,道里区',126.617,45.7559,'Daoli',NULL,NULL),(230103,'南岗区',230100,'南岗',3,'0451','150006','中国,黑龙江省,哈尔滨市,南岗区',126.669,45.76,'Nangang',NULL,NULL),(230104,'道外区',230100,'道外',3,'0451','150020','中国,黑龙江省,哈尔滨市,道外区',126.649,45.7919,'Daowai',NULL,NULL),(230108,'平房区',230100,'平房',3,'0451','150060','中国,黑龙江省,哈尔滨市,平房区',126.637,45.5978,'Pingfang',NULL,NULL),(230109,'松北区',230100,'松北',3,'0451','150028','中国,黑龙江省,哈尔滨市,松北区',126.563,45.8083,'Songbei',NULL,NULL),(230110,'香坊区',230100,'香坊',3,'0451','150036','中国,黑龙江省,哈尔滨市,香坊区',126.68,45.7238,'Xiangfang',NULL,NULL),(230111,'呼兰区',230100,'呼兰',3,'0451','150500','中国,黑龙江省,哈尔滨市,呼兰区',126.588,45.889,'Hulan',NULL,NULL),(230112,'阿城区',230100,'阿城',3,'0451','150300','中国,黑龙江省,哈尔滨市,阿城区',126.975,45.5414,'A\'cheng',NULL,NULL),(230113,'双城区',230100,'双城',3,'0451','150100','中国,黑龙江省,哈尔滨市,双城区',126.309,45.3779,'Shuangcheng',NULL,NULL),(230123,'依兰县',230100,'依兰',3,'0451','154800','中国,黑龙江省,哈尔滨市,依兰县',129.568,46.3247,'Yilan',NULL,NULL),(230124,'方正县',230100,'方正',3,'0451','150800','中国,黑龙江省,哈尔滨市,方正县',128.83,45.8516,'Fangzheng',NULL,NULL),(230125,'宾县',230100,'宾县',3,'0451','150400','中国,黑龙江省,哈尔滨市,宾县',127.487,45.755,'Binxian',NULL,NULL),(230126,'巴彦县',230100,'巴彦',3,'0451','151800','中国,黑龙江省,哈尔滨市,巴彦县',127.408,46.0815,'Bayan',NULL,NULL),(230127,'木兰县',230100,'木兰',3,'0451','151900','中国,黑龙江省,哈尔滨市,木兰县',128.045,45.9494,'Mulan',NULL,NULL),(230128,'通河县',230100,'通河',3,'0451','150900','中国,黑龙江省,哈尔滨市,通河县',128.746,45.9901,'Tonghe',NULL,NULL),(230129,'延寿县',230100,'延寿',3,'0451','150700','中国,黑龙江省,哈尔滨市,延寿县',128.334,45.4554,'Yanshou',NULL,NULL),(230183,'尚志市',230100,'尚志',3,'0451','150600','中国,黑龙江省,哈尔滨市,尚志市',127.962,45.2174,'Shangzhi',NULL,NULL),(230184,'五常市',230100,'五常',3,'0451','150200','中国,黑龙江省,哈尔滨市,五常市',127.168,44.9318,'Wuchang',NULL,NULL),(230200,'齐齐哈尔市',230000,'齐齐哈尔',2,'0452','161005','中国,黑龙江省,齐齐哈尔市',123.953,47.3481,'Qiqihar',NULL,NULL),(230202,'龙沙区',230200,'龙沙',3,'0452','161000','中国,黑龙江省,齐齐哈尔市,龙沙区',123.958,47.3178,'Longsha',NULL,NULL),(230203,'建华区',230200,'建华',3,'0452','161006','中国,黑龙江省,齐齐哈尔市,建华区',124.013,47.3672,'Jianhua',NULL,NULL),(230204,'铁锋区',230200,'铁锋',3,'0452','161000','中国,黑龙江省,齐齐哈尔市,铁锋区',123.978,47.3408,'Tiefeng',NULL,NULL),(230205,'昂昂溪区',230200,'昂昂溪',3,'0452','161031','中国,黑龙江省,齐齐哈尔市,昂昂溪区',123.822,47.1551,'Angangxi',NULL,NULL),(230206,'富拉尔基区',230200,'富拉尔基',3,'0452','161041','中国,黑龙江省,齐齐哈尔市,富拉尔基区',123.629,47.2088,'Fulaerji',NULL,NULL),(230207,'碾子山区',230200,'碾子山',3,'0452','161046','中国,黑龙江省,齐齐哈尔市,碾子山区',122.882,47.5166,'Nianzishan',NULL,NULL),(230208,'梅里斯达斡尔族区',230200,'梅里斯',3,'0452','161021','中国,黑龙江省,齐齐哈尔市,梅里斯达斡尔族区',123.753,47.3095,'Meilisi',NULL,NULL),(230221,'龙江县',230200,'龙江',3,'0452','161100','中国,黑龙江省,齐齐哈尔市,龙江县',123.205,47.3387,'Longjiang',NULL,NULL),(230223,'依安县',230200,'依安',3,'0452','161500','中国,黑龙江省,齐齐哈尔市,依安县',125.309,47.8931,'Yi\'an',NULL,NULL),(230224,'泰来县',230200,'泰来',3,'0452','162400','中国,黑龙江省,齐齐哈尔市,泰来县',123.423,46.3939,'Tailai',NULL,NULL),(230225,'甘南县',230200,'甘南',3,'0452','162100','中国,黑龙江省,齐齐哈尔市,甘南县',123.503,47.9244,'Gannan',NULL,NULL),(230227,'富裕县',230200,'富裕',3,'0452','161200','中国,黑龙江省,齐齐哈尔市,富裕县',124.475,47.7743,'Fuyu',NULL,NULL),(230229,'克山县',230200,'克山',3,'0452','161600','中国,黑龙江省,齐齐哈尔市,克山县',125.874,48.0326,'Keshan',NULL,NULL),(230230,'克东县',230200,'克东',3,'0452','164800','中国,黑龙江省,齐齐哈尔市,克东县',126.249,48.0383,'Kedong',NULL,NULL),(230231,'拜泉县',230200,'拜泉',3,'0452','164700','中国,黑龙江省,齐齐哈尔市,拜泉县',126.092,47.6082,'Baiquan',NULL,NULL),(230281,'讷河市',230200,'讷河',3,'0452','161300','中国,黑龙江省,齐齐哈尔市,讷河市',124.877,48.4839,'Nehe',NULL,NULL),(230300,'鸡西市',230000,'鸡西',2,'0467','158100','中国,黑龙江省,鸡西市',130.976,45.3,'Jixi',NULL,NULL),(230302,'鸡冠区',230300,'鸡冠',3,'0467','158100','中国,黑龙江省,鸡西市,鸡冠区',130.981,45.304,'Jiguan',NULL,NULL),(230303,'恒山区',230300,'恒山',3,'0467','158130','中国,黑龙江省,鸡西市,恒山区',130.905,45.2107,'Hengshan',NULL,NULL),(230304,'滴道区',230300,'滴道',3,'0467','158150','中国,黑龙江省,鸡西市,滴道区',130.848,45.3511,'Didao',NULL,NULL),(230305,'梨树区',230300,'梨树',3,'0467','158160','中国,黑龙江省,鸡西市,梨树区',130.698,45.0904,'Lishu',NULL,NULL),(230306,'城子河区',230300,'城子河',3,'0467','158170','中国,黑龙江省,鸡西市,城子河区',131.011,45.3369,'Chengzihe',NULL,NULL),(230307,'麻山区',230300,'麻山',3,'0467','158180','中国,黑龙江省,鸡西市,麻山区',130.478,45.2121,'Mashan',NULL,NULL),(230321,'鸡东县',230300,'鸡东',3,'0467','158200','中国,黑龙江省,鸡西市,鸡东县',131.124,45.2603,'Jidong',NULL,NULL),(230381,'虎林市',230300,'虎林',3,'0467','158400','中国,黑龙江省,鸡西市,虎林市',132.937,45.7629,'Hulin',NULL,NULL),(230382,'密山市',230300,'密山',3,'0467','158300','中国,黑龙江省,鸡西市,密山市',131.846,45.5297,'Mishan',NULL,NULL),(230400,'鹤岗市',230000,'鹤岗',2,'0468','154100','中国,黑龙江省,鹤岗市',130.277,47.3321,'Hegang',NULL,NULL),(230402,'向阳区',230400,'向阳',3,'0468','154100','中国,黑龙江省,鹤岗市,向阳区',130.294,47.3425,'Xiangyang',NULL,NULL),(230403,'工农区',230400,'工农',3,'0468','154101','中国,黑龙江省,鹤岗市,工农区',130.275,47.3187,'Gongnong',NULL,NULL),(230404,'南山区',230400,'南山',3,'0468','154104','中国,黑龙江省,鹤岗市,南山区',130.277,47.314,'Nanshan',NULL,NULL),(230405,'兴安区',230400,'兴安',3,'0468','154102','中国,黑龙江省,鹤岗市,兴安区',130.24,47.2526,'Xing\'an',NULL,NULL),(230406,'东山区',230400,'东山',3,'0468','154106','中国,黑龙江省,鹤岗市,东山区',130.317,47.3385,'Dongshan',NULL,NULL),(230407,'兴山区',230400,'兴山',3,'0468','154105','中国,黑龙江省,鹤岗市,兴山区',130.293,47.3578,'Xingshan',NULL,NULL),(230421,'萝北县',230400,'萝北',3,'0468','154200','中国,黑龙江省,鹤岗市,萝北县',130.833,47.5796,'Luobei',NULL,NULL),(230422,'绥滨县',230400,'绥滨',3,'0468','156200','中国,黑龙江省,鹤岗市,绥滨县',131.86,47.2903,'Suibin',NULL,NULL),(230500,'双鸭山市',230000,'双鸭山',2,'0469','155100','中国,黑龙江省,双鸭山市',131.157,46.6434,'Shuangyashan',NULL,NULL),(230502,'尖山区',230500,'尖山',3,'0469','155100','中国,黑龙江省,双鸭山市,尖山区',131.158,46.6464,'Jianshan',NULL,NULL),(230503,'岭东区',230500,'岭东',3,'0469','155120','中国,黑龙江省,双鸭山市,岭东区',131.165,46.5904,'Lingdong',NULL,NULL),(230505,'四方台区',230500,'四方台',3,'0469','155130','中国,黑龙江省,双鸭山市,四方台区',131.336,46.595,'Sifangtai',NULL,NULL),(230506,'宝山区',230500,'宝山',3,'0469','155131','中国,黑龙江省,双鸭山市,宝山区',131.402,46.5772,'Baoshan',NULL,NULL),(230521,'集贤县',230500,'集贤',3,'0469','155900','中国,黑龙江省,双鸭山市,集贤县',131.141,46.7268,'Jixian',NULL,NULL),(230522,'友谊县',230500,'友谊',3,'0469','155800','中国,黑龙江省,双鸭山市,友谊县',131.808,46.7674,'Youyi',NULL,NULL),(230523,'宝清县',230500,'宝清',3,'0469','155600','中国,黑龙江省,双鸭山市,宝清县',132.197,46.3272,'Baoqing',NULL,NULL),(230524,'饶河县',230500,'饶河',3,'0469','155700','中国,黑龙江省,双鸭山市,饶河县',134.02,46.799,'Raohe',NULL,NULL),(230600,'大庆市',230000,'大庆',2,'0459','163000','中国,黑龙江省,大庆市',125.113,46.5907,'Daqing',NULL,NULL),(230602,'萨尔图区',230600,'萨尔图',3,'0459','163001','中国,黑龙江省,大庆市,萨尔图区',125.088,46.5936,'Saertu',NULL,NULL),(230603,'龙凤区',230600,'龙凤',3,'0459','163711','中国,黑龙江省,大庆市,龙凤区',125.117,46.5327,'Longfeng',NULL,NULL),(230604,'让胡路区',230600,'让胡路',3,'0459','163712','中国,黑龙江省,大庆市,让胡路区',124.871,46.6522,'Ranghulu',NULL,NULL),(230605,'红岗区',230600,'红岗',3,'0459','163511','中国,黑龙江省,大庆市,红岗区',124.892,46.4013,'Honggang',NULL,NULL),(230606,'大同区',230600,'大同',3,'0459','163515','中国,黑龙江省,大庆市,大同区',124.816,46.033,'Datong',NULL,NULL),(230621,'肇州县',230600,'肇州',3,'0459','166400','中国,黑龙江省,大庆市,肇州县',125.271,45.7041,'Zhaozhou',NULL,NULL),(230622,'肇源县',230600,'肇源',3,'0459','166500','中国,黑龙江省,大庆市,肇源县',125.085,45.5203,'Zhaoyuan',NULL,NULL),(230623,'林甸县',230600,'林甸',3,'0459','166300','中国,黑龙江省,大庆市,林甸县',124.876,47.186,'Lindian',NULL,NULL),(230624,'杜尔伯特蒙古族自治县',230600,'杜尔伯特',3,'0459','166200','中国,黑龙江省,大庆市,杜尔伯特蒙古族自治县',124.449,46.8651,'Duerbote',NULL,NULL),(230700,'伊春市',230000,'伊春',2,'0458','153000','中国,黑龙江省,伊春市',128.899,47.7248,'Yichun',NULL,NULL),(230702,'伊春区',230700,'伊春',3,'0458','153000','中国,黑龙江省,伊春市,伊春区',128.908,47.728,'Yichun',NULL,NULL),(230703,'南岔区',230700,'南岔',3,'0458','153100','中国,黑龙江省,伊春市,南岔区',129.284,47.139,'Nancha',NULL,NULL),(230704,'友好区',230700,'友好',3,'0458','153031','中国,黑龙江省,伊春市,友好区',128.84,47.8537,'Youhao',NULL,NULL),(230705,'西林区',230700,'西林',3,'0458','153025','中国,黑龙江省,伊春市,西林区',129.312,47.481,'Xilin',NULL,NULL),(230706,'翠峦区',230700,'翠峦',3,'0458','153013','中国,黑龙江省,伊春市,翠峦区',128.667,47.725,'Cuiluan',NULL,NULL),(230707,'新青区',230700,'新青',3,'0458','153036','中国,黑龙江省,伊春市,新青区',129.537,48.2907,'Xinqing',NULL,NULL),(230708,'美溪区',230700,'美溪',3,'0458','153021','中国,黑龙江省,伊春市,美溪区',129.137,47.6351,'Meixi',NULL,NULL),(230709,'金山屯区',230700,'金山屯',3,'0458','153026','中国,黑龙江省,伊春市,金山屯区',129.438,47.4135,'Jinshantun',NULL,NULL),(230710,'五营区',230700,'五营',3,'0458','153033','中国,黑龙江省,伊春市,五营区',129.245,48.1079,'Wuying',NULL,NULL),(230711,'乌马河区',230700,'乌马河',3,'0458','153011','中国,黑龙江省,伊春市,乌马河区',128.797,47.728,'Wumahe',NULL,NULL),(230712,'汤旺河区',230700,'汤旺河',3,'0458','153037','中国,黑龙江省,伊春市,汤旺河区',129.572,48.4518,'Tangwanghe',NULL,NULL),(230713,'带岭区',230700,'带岭',3,'0458','153106','中国,黑龙江省,伊春市,带岭区',129.024,47.0255,'Dailing',NULL,NULL),(230714,'乌伊岭区',230700,'乌伊岭',3,'0458','153038','中国,黑龙江省,伊春市,乌伊岭区',129.44,48.596,'Wuyiling',NULL,NULL),(230715,'红星区',230700,'红星',3,'0458','153035','中国,黑龙江省,伊春市,红星区',129.389,48.2394,'Hongxing',NULL,NULL),(230716,'上甘岭区',230700,'上甘岭',3,'0458','153032','中国,黑龙江省,伊春市,上甘岭区',129.024,47.9752,'Shangganling',NULL,NULL),(230722,'嘉荫县',230700,'嘉荫',3,'0458','153200','中国,黑龙江省,伊春市,嘉荫县',130.398,48.8917,'Jiayin',NULL,NULL),(230781,'铁力市',230700,'铁力',3,'0458','152500','中国,黑龙江省,伊春市,铁力市',128.032,46.9857,'Tieli',NULL,NULL),(230800,'佳木斯市',230000,'佳木斯',2,'0454','154002','中国,黑龙江省,佳木斯市',130.362,46.8096,'Jiamusi',NULL,NULL),(230803,'向阳区',230800,'向阳',3,'0454','154002','中国,黑龙江省,佳木斯市,向阳区',130.365,46.8078,'Xiangyang',NULL,NULL),(230804,'前进区',230800,'前进',3,'0454','154002','中国,黑龙江省,佳木斯市,前进区',130.375,46.814,'Qianjin',NULL,NULL),(230805,'东风区',230800,'东风',3,'0454','154005','中国,黑龙江省,佳木斯市,东风区',130.404,46.8226,'Dongfeng',NULL,NULL),(230811,'郊区',230800,'郊区',3,'0454','154004','中国,黑龙江省,佳木斯市,郊区',130.327,46.8096,'Jiaoqu',NULL,NULL),(230822,'桦南县',230800,'桦南',3,'0454','154400','中国,黑龙江省,佳木斯市,桦南县',130.554,46.2392,'Huanan',NULL,NULL),(230826,'桦川县',230800,'桦川',3,'0454','154300','中国,黑龙江省,佳木斯市,桦川县',130.719,47.023,'Huachuan',NULL,NULL),(230828,'汤原县',230800,'汤原',3,'0454','154700','中国,黑龙江省,佳木斯市,汤原县',129.91,46.7276,'Tangyuan',NULL,NULL),(230833,'抚远县',230800,'抚远',3,'0454','156500','中国,黑龙江省,佳木斯市,抚远县',134.296,48.3679,'Fuyuan',NULL,NULL),(230881,'同江市',230800,'同江',3,'0454','156400','中国,黑龙江省,佳木斯市,同江市',132.511,47.6421,'Tongjiang',NULL,NULL),(230882,'富锦市',230800,'富锦',3,'0454','156100','中国,黑龙江省,佳木斯市,富锦市',132.037,47.2513,'Fujin',NULL,NULL),(230900,'七台河市',230000,'七台河',2,'0464','154600','中国,黑龙江省,七台河市',131.016,45.7713,'Qitaihe',NULL,NULL),(230902,'新兴区',230900,'新兴',3,'0464','154604','中国,黑龙江省,七台河市,新兴区',130.932,45.8162,'Xinxing',NULL,NULL),(230903,'桃山区',230900,'桃山',3,'0464','154600','中国,黑龙江省,七台河市,桃山区',131.018,45.7678,'Taoshan',NULL,NULL),(230904,'茄子河区',230900,'茄子河',3,'0464','154622','中国,黑龙江省,七台河市,茄子河区',131.068,45.7852,'Qiezihe',NULL,NULL),(230921,'勃利县',230900,'勃利',3,'0464','154500','中国,黑龙江省,七台河市,勃利县',130.592,45.755,'Boli',NULL,NULL),(231000,'牡丹江市',230000,'牡丹江',2,'0453','157000','中国,黑龙江省,牡丹江市',129.619,44.583,'Mudanjiang',NULL,NULL),(231002,'东安区',231000,'东安',3,'0453','157000','中国,黑龙江省,牡丹江市,东安区',129.627,44.5813,'Dong\'an',NULL,NULL),(231003,'阳明区',231000,'阳明',3,'0453','157013','中国,黑龙江省,牡丹江市,阳明区',129.635,44.596,'Yangming',NULL,NULL),(231004,'爱民区',231000,'爱民',3,'0453','157009','中国,黑龙江省,牡丹江市,爱民区',129.591,44.5965,'Aimin',NULL,NULL),(231005,'西安区',231000,'西安',3,'0453','157000','中国,黑龙江省,牡丹江市,西安区',129.616,44.5777,'Xi\'an',NULL,NULL),(231024,'东宁县',231000,'东宁',3,'0453','157200','中国,黑龙江省,牡丹江市,东宁县',131.128,44.0661,'Dongning',NULL,NULL),(231025,'林口县',231000,'林口',3,'0453','157600','中国,黑龙江省,牡丹江市,林口县',130.284,45.2781,'Linkou',NULL,NULL),(231081,'绥芬河市',231000,'绥芬河',3,'0453','157300','中国,黑龙江省,牡丹江市,绥芬河市',131.151,44.4125,'Suifenhe',NULL,NULL),(231083,'海林市',231000,'海林',3,'0453','157100','中国,黑龙江省,牡丹江市,海林市',129.382,44.59,'Hailin',NULL,NULL),(231084,'宁安市',231000,'宁安',3,'0453','157400','中国,黑龙江省,牡丹江市,宁安市',129.483,44.3402,'Ning\'an',NULL,NULL),(231085,'穆棱市',231000,'穆棱',3,'0453','157500','中国,黑龙江省,牡丹江市,穆棱市',130.525,44.919,'Muling',NULL,NULL),(231100,'黑河市',230000,'黑河',2,'0456','164300','中国,黑龙江省,黑河市',127.499,50.2496,'Heihe',NULL,NULL),(231102,'爱辉区',231100,'爱辉',3,'0456','164300','中国,黑龙江省,黑河市,爱辉区',127.501,50.252,'Aihui',NULL,NULL),(231121,'嫩江县',231100,'嫩江',3,'0456','161400','中国,黑龙江省,黑河市,嫩江县',125.226,49.1784,'Nenjiang',NULL,NULL),(231123,'逊克县',231100,'逊克',3,'0456','164400','中国,黑龙江省,黑河市,逊克县',128.479,49.5798,'Xunke',NULL,NULL),(231124,'孙吴县',231100,'孙吴',3,'0456','164200','中国,黑龙江省,黑河市,孙吴县',127.336,49.4254,'Sunwu',NULL,NULL),(231181,'北安市',231100,'北安',3,'0456','164000','中国,黑龙江省,黑河市,北安市',126.482,48.2387,'Bei\'an',NULL,NULL),(231182,'五大连池市',231100,'五大连池',3,'0456','164100','中国,黑龙江省,黑河市,五大连池市',126.203,48.5151,'Wudalianchi',NULL,NULL),(231200,'绥化市',230000,'绥化',2,'0455','152000','中国,黑龙江省,绥化市',126.993,46.6374,'Suihua',NULL,NULL),(231202,'北林区',231200,'北林',3,'0455','152000','中国,黑龙江省,绥化市,北林区',126.986,46.6373,'Beilin',NULL,NULL),(231221,'望奎县',231200,'望奎',3,'0455','152100','中国,黑龙江省,绥化市,望奎县',126.482,46.8308,'Wangkui',NULL,NULL),(231222,'兰西县',231200,'兰西',3,'0455','151500','中国,黑龙江省,绥化市,兰西县',126.29,46.2525,'Lanxi',NULL,NULL),(231223,'青冈县',231200,'青冈',3,'0455','151600','中国,黑龙江省,绥化市,青冈县',126.113,46.6853,'Qinggang',NULL,NULL),(231224,'庆安县',231200,'庆安',3,'0455','152400','中国,黑龙江省,绥化市,庆安县',127.508,46.8802,'Qing\'an',NULL,NULL),(231225,'明水县',231200,'明水',3,'0455','151700','中国,黑龙江省,绥化市,明水县',125.906,47.1733,'Mingshui',NULL,NULL),(231226,'绥棱县',231200,'绥棱',3,'0455','152200','中国,黑龙江省,绥化市,绥棱县',127.116,47.2427,'Suileng',NULL,NULL),(231281,'安达市',231200,'安达',3,'0455','151400','中国,黑龙江省,绥化市,安达市',125.344,46.4177,'Anda',NULL,NULL),(231282,'肇东市',231200,'肇东',3,'0455','151100','中国,黑龙江省,绥化市,肇东市',125.962,46.0513,'Zhaodong',NULL,NULL),(231283,'海伦市',231200,'海伦',3,'0455','152300','中国,黑龙江省,绥化市,海伦市',126.968,47.4609,'Hailun',NULL,NULL),(232700,'大兴安岭地区',230000,'大兴安岭',2,'0457','165000','中国,黑龙江省,大兴安岭地区',124.712,52.3353,'DaXingAnLing',NULL,NULL),(232701,'加格达奇区',232700,'加格达奇',3,'0457','165000','中国,黑龙江省,大兴安岭地区,加格达奇区',124.31,51.9814,'Jiagedaqi',NULL,NULL),(232702,'新林区',232700,'新林',3,'0457','165000','中国,黑龙江省,大兴安岭地区,新林区',124.398,51.6734,'Xinlin',NULL,NULL),(232703,'松岭区',232700,'松岭',3,'0457','165000','中国,黑龙江省,大兴安岭地区,松岭区',124.19,51.9855,'Songling',NULL,NULL),(232704,'呼中区',232700,'呼中',3,'0457','165000','中国,黑龙江省,大兴安岭地区,呼中区',123.6,52.0335,'Huzhong',NULL,NULL),(232721,'呼玛县',232700,'呼玛',3,'0457','165100','中国,黑龙江省,大兴安岭地区,呼玛县',126.662,51.7311,'Huma',NULL,NULL),(232722,'塔河县',232700,'塔河',3,'0457','165200','中国,黑龙江省,大兴安岭地区,塔河县',124.71,52.3343,'Tahe',NULL,NULL),(232723,'漠河县',232700,'漠河',3,'0457','165300','中国,黑龙江省,大兴安岭地区,漠河县',122.538,52.97,'Mohe',NULL,NULL),(310000,'上海',100000,'上海',1,'','','中国,上海',121.473,31.2317,'Shanghai',NULL,NULL),(310100,'上海市',310000,'上海',2,'021','200000','中国,上海,上海市',121.473,31.2317,'Shanghai',NULL,NULL),(310101,'黄浦区',310100,'黄浦',3,'021','200001','中国,上海,上海市,黄浦区',121.493,31.2234,'Huangpu',NULL,NULL),(310104,'徐汇区',310100,'徐汇',3,'021','200030','中国,上海,上海市,徐汇区',121.437,31.1883,'Xuhui',NULL,NULL),(310105,'长宁区',310100,'长宁',3,'021','200050','中国,上海,上海市,长宁区',121.425,31.2204,'Changning',NULL,NULL),(310106,'静安区',310100,'静安',3,'021','200040','中国,上海,上海市,静安区',121.444,31.2288,'Jing\'an',NULL,NULL),(310107,'普陀区',310100,'普陀',3,'021','200333','中国,上海,上海市,普陀区',121.397,31.2495,'Putuo',NULL,NULL),(310108,'闸北区',310100,'闸北',3,'021','200070','中国,上海,上海市,闸北区',121.446,31.2808,'Zhabei',NULL,NULL),(310109,'虹口区',310100,'虹口',3,'021','200086','中国,上海,上海市,虹口区',121.482,31.2779,'Hongkou',NULL,NULL),(310110,'杨浦区',310100,'杨浦',3,'021','200082','中国,上海,上海市,杨浦区',121.526,31.2595,'Yangpu',NULL,NULL),(310112,'闵行区',310100,'闵行',3,'021','201100','中国,上海,上海市,闵行区',121.382,31.1125,'Minhang',NULL,NULL),(310113,'宝山区',310100,'宝山',3,'021','201900','中国,上海,上海市,宝山区',121.489,31.4045,'Baoshan',NULL,NULL),(310114,'嘉定区',310100,'嘉定',3,'021','201800','中国,上海,上海市,嘉定区',121.266,31.3747,'Jiading',NULL,NULL),(310115,'浦东新区',310100,'浦东',3,'021','200135','中国,上海,上海市,浦东新区',121.545,31.2225,'Pudong',NULL,NULL),(310116,'金山区',310100,'金山',3,'021','200540','中国,上海,上海市,金山区',121.342,30.7416,'Jinshan',NULL,NULL),(310117,'松江区',310100,'松江',3,'021','201600','中国,上海,上海市,松江区',121.229,31.0322,'Songjiang',NULL,NULL),(310118,'青浦区',310100,'青浦',3,'021','201700','中国,上海,上海市,青浦区',121.124,31.1497,'Qingpu',NULL,NULL),(310120,'奉贤区',310100,'奉贤',3,'021','201400','中国,上海,上海市,奉贤区',121.474,30.9179,'Fengxian',NULL,NULL),(310230,'崇明县',310100,'崇明',3,'021','202150','中国,上海,上海市,崇明县',121.398,31.6228,'Chongming',NULL,NULL),(320000,'江苏省',100000,'江苏',1,'','','中国,江苏省',118.767,32.0415,'Jiangsu',NULL,NULL),(320100,'南京市',320000,'南京',2,'025','210008','中国,江苏省,南京市',118.767,32.0415,'Nanjing',NULL,NULL),(320102,'玄武区',320100,'玄武',3,'025','210018','中国,江苏省,南京市,玄武区',118.798,32.0486,'Xuanwu',NULL,NULL),(320104,'秦淮区',320100,'秦淮',3,'025','210001','中国,江苏省,南京市,秦淮区',118.798,32.0111,'Qinhuai',NULL,NULL),(320105,'建邺区',320100,'建邺',3,'025','210004','中国,江苏省,南京市,建邺区',118.766,32.031,'Jianye',NULL,NULL),(320106,'鼓楼区',320100,'鼓楼',3,'025','210009','中国,江苏省,南京市,鼓楼区',118.77,32.0663,'Gulou',NULL,NULL),(320111,'浦口区',320100,'浦口',3,'025','211800','中国,江苏省,南京市,浦口区',118.628,32.0588,'Pukou',NULL,NULL),(320113,'栖霞区',320100,'栖霞',3,'025','210046','中国,江苏省,南京市,栖霞区',118.881,32.1135,'Qixia',NULL,NULL),(320114,'雨花台区',320100,'雨花台',3,'025','210012','中国,江苏省,南京市,雨花台区',118.78,31.992,'Yuhuatai',NULL,NULL),(320115,'江宁区',320100,'江宁',3,'025','211100','中国,江苏省,南京市,江宁区',118.84,31.9526,'Jiangning',NULL,NULL),(320116,'六合区',320100,'六合',3,'025','211500','中国,江苏省,南京市,六合区',118.841,32.3422,'Luhe',NULL,NULL),(320117,'溧水区',320100,'溧水',3,'025','211200','中国,江苏省,南京市,溧水区',119.029,31.6531,'Lishui',NULL,NULL),(320118,'高淳区',320100,'高淳',3,'025','211300','中国,江苏省,南京市,高淳区',118.876,31.3271,'Gaochun',NULL,NULL),(320200,'无锡市',320000,'无锡',2,'0510','214000','中国,江苏省,无锡市',120.302,31.5747,'Wuxi',NULL,NULL),(320202,'崇安区',320200,'崇安',3,'0510','214001','中国,江苏省,无锡市,崇安区',120.3,31.58,'Chong\'an',NULL,NULL),(320203,'南长区',320200,'南长',3,'0510','214021','中国,江苏省,无锡市,南长区',120.309,31.5636,'Nanchang',NULL,NULL),(320204,'北塘区',320200,'北塘',3,'0510','214044','中国,江苏省,无锡市,北塘区',120.294,31.6059,'Beitang',NULL,NULL),(320205,'锡山区',320200,'锡山',3,'0510','214101','中国,江苏省,无锡市,锡山区',120.357,31.5886,'Xishan',NULL,NULL),(320206,'惠山区',320200,'惠山',3,'0510','214174','中国,江苏省,无锡市,惠山区',120.298,31.6809,'Huishan',NULL,NULL),(320211,'滨湖区',320200,'滨湖',3,'0510','214123','中国,江苏省,无锡市,滨湖区',120.295,31.5216,'Binhu',NULL,NULL),(320281,'江阴市',320200,'江阴',3,'0510','214431','中国,江苏省,无锡市,江阴市',120.285,31.92,'Jiangyin',NULL,NULL),(320282,'宜兴市',320200,'宜兴',3,'0510','214200','中国,江苏省,无锡市,宜兴市',119.824,31.3398,'Yixing',NULL,NULL),(320300,'徐州市',320000,'徐州',2,'0516','221003','中国,江苏省,徐州市',117.185,34.2618,'Xuzhou',NULL,NULL),(320302,'鼓楼区',320300,'鼓楼',3,'0516','221005','中国,江苏省,徐州市,鼓楼区',117.186,34.2885,'Gulou',NULL,NULL),(320303,'云龙区',320300,'云龙',3,'0516','221007','中国,江苏省,徐州市,云龙区',117.231,34.249,'Yunlong',NULL,NULL),(320305,'贾汪区',320300,'贾汪',3,'0516','221003','中国,江苏省,徐州市,贾汪区',117.453,34.4426,'Jiawang',NULL,NULL),(320311,'泉山区',320300,'泉山',3,'0516','221006','中国,江苏省,徐州市,泉山区',117.194,34.2442,'Quanshan',NULL,NULL),(320312,'铜山区',320300,'铜山',3,'0516','221106','中国,江苏省,徐州市,铜山区',117.184,34.1929,'Tongshan',NULL,NULL),(320321,'丰县',320300,'丰县',3,'0516','221700','中国,江苏省,徐州市,丰县',116.6,34.6997,'Fengxian',NULL,NULL),(320322,'沛县',320300,'沛县',3,'0516','221600','中国,江苏省,徐州市,沛县',116.937,34.7216,'Peixian',NULL,NULL),(320324,'睢宁县',320300,'睢宁',3,'0516','221200','中国,江苏省,徐州市,睢宁县',117.941,33.9127,'Suining',NULL,NULL),(320381,'新沂市',320300,'新沂',3,'0516','221400','中国,江苏省,徐州市,新沂市',118.355,34.3694,'Xinyi',NULL,NULL),(320382,'邳州市',320300,'邳州',3,'0516','221300','中国,江苏省,徐州市,邳州市',117.959,34.3333,'Pizhou',NULL,NULL),(320400,'常州市',320000,'常州',2,'0519','213000','中国,江苏省,常州市',119.947,31.7728,'Changzhou',NULL,NULL),(320402,'天宁区',320400,'天宁',3,'0519','213000','中国,江苏省,常州市,天宁区',119.951,31.7521,'Tianning',NULL,NULL),(320404,'钟楼区',320400,'钟楼',3,'0519','213023','中国,江苏省,常州市,钟楼区',119.902,31.8022,'Zhonglou',NULL,NULL),(320405,'戚墅堰区',320400,'戚墅堰',3,'0519','213025','中国,江苏省,常州市,戚墅堰区',120.061,31.7196,'Qishuyan',NULL,NULL),(320411,'新北区',320400,'新北',3,'0519','213022','中国,江苏省,常州市,新北区',119.971,31.8305,'Xinbei',NULL,NULL),(320412,'武进区',320400,'武进',3,'0519','213100','中国,江苏省,常州市,武进区',119.942,31.7009,'Wujin',NULL,NULL),(320481,'溧阳市',320400,'溧阳',3,'0519','213300','中国,江苏省,常州市,溧阳市',119.484,31.4154,'Liyang',NULL,NULL),(320482,'金坛市',320400,'金坛',3,'0519','213200','中国,江苏省,常州市,金坛市',119.578,31.7404,'Jintan',NULL,NULL),(320500,'苏州市',320000,'苏州',2,'0512','215002','中国,江苏省,苏州市',120.62,31.2994,'Suzhou',NULL,NULL),(320505,'虎丘区',320500,'虎丘',3,'0512','215004','中国,江苏省,苏州市,虎丘区',120.573,31.2953,'Huqiu',NULL,NULL),(320506,'吴中区',320500,'吴中',3,'0512','215128','中国,江苏省,苏州市,吴中区',120.632,31.2623,'Wuzhong',NULL,NULL),(320507,'相城区',320500,'相城',3,'0512','215131','中国,江苏省,苏州市,相城区',120.642,31.3689,'Xiangcheng',NULL,NULL),(320508,'姑苏区',320500,'姑苏',3,'0512','215031','中国,江苏省,苏州市,姑苏区',120.62,31.2994,'Gusu',NULL,NULL),(320509,'吴江区',320500,'吴江',3,'0512','215200','中国,江苏省,苏州市,吴江区',120.638,31.1598,'Wujiang',NULL,NULL),(320581,'常熟市',320500,'常熟',3,'0512','215500','中国,江苏省,苏州市,常熟市',120.752,31.6537,'Changshu',NULL,NULL),(320582,'张家港市',320500,'张家港',3,'0512','215600','中国,江苏省,苏州市,张家港市',120.555,31.8753,'Zhangjiagang',NULL,NULL),(320583,'昆山市',320500,'昆山',3,'0512','215300','中国,江苏省,苏州市,昆山市',120.981,31.3846,'Kunshan',NULL,NULL),(320585,'太仓市',320500,'太仓',3,'0512','215400','中国,江苏省,苏州市,太仓市',121.109,31.4497,'Taicang',NULL,NULL),(320600,'南通市',320000,'南通',2,'0513','226001','中国,江苏省,南通市',120.865,32.0162,'Nantong',NULL,NULL),(320602,'崇川区',320600,'崇川',3,'0513','226001','中国,江苏省,南通市,崇川区',120.857,32.0098,'Chongchuan',NULL,NULL),(320611,'港闸区',320600,'港闸',3,'0513','226001','中国,江苏省,南通市,港闸区',120.818,32.0316,'Gangzha',NULL,NULL),(320612,'通州区',320600,'通州',3,'0513','226300','中国,江苏省,南通市,通州区',121.073,32.0676,'Tongzhou',NULL,NULL),(320621,'海安县',320600,'海安',3,'0513','226600','中国,江苏省,南通市,海安县',120.459,32.5451,'Hai\'an',NULL,NULL),(320623,'如东县',320600,'如东',3,'0513','226400','中国,江苏省,南通市,如东县',121.189,32.3144,'Rudong',NULL,NULL),(320681,'启东市',320600,'启东',3,'0513','226200','中国,江苏省,南通市,启东市',121.66,31.8108,'Qidong',NULL,NULL),(320682,'如皋市',320600,'如皋',3,'0513','226500','中国,江苏省,南通市,如皋市',120.56,32.376,'Rugao',NULL,NULL),(320684,'海门市',320600,'海门',3,'0513','226100','中国,江苏省,南通市,海门市',121.17,31.8942,'Haimen',NULL,NULL),(320700,'连云港市',320000,'连云港',2,'0518','222002','中国,江苏省,连云港市',119.179,34.6,'Lianyungang',NULL,NULL),(320703,'连云区',320700,'连云',3,'0518','222042','中国,江苏省,连云港市,连云区',119.373,34.7529,'Lianyun',NULL,NULL),(320706,'海州区',320700,'海州',3,'0518','222003','中国,江苏省,连云港市,海州区',119.131,34.5699,'Haizhou',NULL,NULL),(320707,'赣榆区',320700,'赣榆',3,'0518','222100','中国,江苏省,连云港市,赣榆区',119.129,34.8392,'Ganyu',NULL,NULL),(320722,'东海县',320700,'东海',3,'0518','222300','中国,江苏省,连云港市,东海县',118.771,34.5421,'Donghai',NULL,NULL),(320723,'灌云县',320700,'灌云',3,'0518','222200','中国,江苏省,连云港市,灌云县',119.239,34.2839,'Guanyun',NULL,NULL),(320724,'灌南县',320700,'灌南',3,'0518','222500','中国,江苏省,连云港市,灌南县',119.356,34.09,'Guannan',NULL,NULL),(320800,'淮安市',320000,'淮安',2,'0517','223001','中国,江苏省,淮安市',119.021,33.5975,'Huai\'an',NULL,NULL),(320802,'清河区',320800,'清河',3,'0517','223001','中国,江苏省,淮安市,清河区',119.008,33.5995,'Qinghe',NULL,NULL),(320803,'淮安区',320800,'淮安',3,'0517','223200','中国,江苏省,淮安市,淮安区',119.021,33.5975,'Huai\'an',NULL,NULL),(320804,'淮阴区',320800,'淮阴',3,'0517','223300','中国,江苏省,淮安市,淮阴区',119.035,33.6317,'Huaiyin',NULL,NULL),(320811,'清浦区',320800,'清浦',3,'0517','223002','中国,江苏省,淮安市,清浦区',119.026,33.5523,'Qingpu',NULL,NULL),(320826,'涟水县',320800,'涟水',3,'0517','223400','中国,江苏省,淮安市,涟水县',119.261,33.7809,'Lianshui',NULL,NULL),(320829,'洪泽县',320800,'洪泽',3,'0517','223100','中国,江苏省,淮安市,洪泽县',118.873,33.2943,'Hongze',NULL,NULL),(320830,'盱眙县',320800,'盱眙',3,'0517','211700','中国,江苏省,淮安市,盱眙县',118.545,33.0109,'Xuyi',NULL,NULL),(320831,'金湖县',320800,'金湖',3,'0517','211600','中国,江苏省,淮安市,金湖县',119.023,33.0222,'Jinhu',NULL,NULL),(320900,'盐城市',320000,'盐城',2,'0515','224005','中国,江苏省,盐城市',120.14,33.3776,'Yancheng',NULL,NULL),(320902,'亭湖区',320900,'亭湖',3,'0515','224005','中国,江苏省,盐城市,亭湖区',120.166,33.3783,'Tinghu',NULL,NULL),(320903,'盐都区',320900,'盐都',3,'0515','224055','中国,江苏省,盐城市,盐都区',120.154,33.3373,'Yandu',NULL,NULL),(320921,'响水县',320900,'响水',3,'0515','224600','中国,江苏省,盐城市,响水县',119.57,34.2051,'Xiangshui',NULL,NULL),(320922,'滨海县',320900,'滨海',3,'0515','224500','中国,江苏省,盐城市,滨海县',119.821,33.9897,'Binhai',NULL,NULL),(320923,'阜宁县',320900,'阜宁',3,'0515','224400','中国,江苏省,盐城市,阜宁县',119.802,33.7823,'Funing',NULL,NULL),(320924,'射阳县',320900,'射阳',3,'0515','224300','中国,江苏省,盐城市,射阳县',120.26,33.7764,'Sheyang',NULL,NULL),(320925,'建湖县',320900,'建湖',3,'0515','224700','中国,江苏省,盐城市,建湖县',119.799,33.4724,'Jianhu',NULL,NULL),(320981,'东台市',320900,'东台',3,'0515','224200','中国,江苏省,盐城市,东台市',120.324,32.8508,'Dongtai',NULL,NULL),(320982,'大丰市',320900,'大丰',3,'0515','224100','中国,江苏省,盐城市,大丰市',120.466,33.1989,'Dafeng',NULL,NULL),(321000,'扬州市',320000,'扬州',2,'0514','225002','中国,江苏省,扬州市',119.421,32.3932,'Yangzhou',NULL,NULL),(321002,'广陵区',321000,'广陵',3,'0514','225002','中国,江苏省,扬州市,广陵区',119.432,32.3947,'Guangling',NULL,NULL),(321003,'邗江区',321000,'邗江',3,'0514','225002','中国,江苏省,扬州市,邗江区',119.398,32.3765,'Hanjiang',NULL,NULL),(321012,'江都区',321000,'江都',3,'0514','225200','中国,江苏省,扬州市,江都区',119.567,32.4266,'Jiangdu',NULL,NULL),(321023,'宝应县',321000,'宝应',3,'0514','225800','中国,江苏省,扬州市,宝应县',119.312,33.2355,'Baoying',NULL,NULL),(321081,'仪征市',321000,'仪征',3,'0514','211400','中国,江苏省,扬州市,仪征市',119.184,32.272,'Yizheng',NULL,NULL),(321084,'高邮市',321000,'高邮',3,'0514','225600','中国,江苏省,扬州市,高邮市',119.46,32.7813,'Gaoyou',NULL,NULL),(321100,'镇江市',320000,'镇江',2,'0511','212004','中国,江苏省,镇江市',119.453,32.2044,'Zhenjiang',NULL,NULL),(321102,'京口区',321100,'京口',3,'0511','212003','中国,江苏省,镇江市,京口区',119.469,32.1981,'Jingkou',NULL,NULL),(321111,'润州区',321100,'润州',3,'0511','212005','中国,江苏省,镇江市,润州区',119.411,32.1952,'Runzhou',NULL,NULL),(321112,'丹徒区',321100,'丹徒',3,'0511','212028','中国,江苏省,镇江市,丹徒区',119.434,32.1318,'Dantu',NULL,NULL),(321181,'丹阳市',321100,'丹阳',3,'0511','212300','中国,江苏省,镇江市,丹阳市',119.575,31.9912,'Danyang',NULL,NULL),(321182,'扬中市',321100,'扬中',3,'0511','212200','中国,江苏省,镇江市,扬中市',119.797,32.2363,'Yangzhong',NULL,NULL),(321183,'句容市',321100,'句容',3,'0511','212400','中国,江苏省,镇江市,句容市',119.165,31.9559,'Jurong',NULL,NULL),(321200,'泰州市',320000,'泰州',2,'0523','225300','中国,江苏省,泰州市',119.915,32.4849,'Taizhou',NULL,NULL),(321202,'海陵区',321200,'海陵',3,'0523','225300','中国,江苏省,泰州市,海陵区',119.919,32.491,'Hailing',NULL,NULL),(321203,'高港区',321200,'高港',3,'0523','225321','中国,江苏省,泰州市,高港区',119.881,32.3183,'Gaogang',NULL,NULL),(321204,'姜堰区',321200,'姜堰',3,'0523','225500','中国,江苏省,泰州市,姜堰区',120.148,32.5085,'Jiangyan',NULL,NULL),(321281,'兴化市',321200,'兴化',3,'0523','225700','中国,江苏省,泰州市,兴化市',119.852,32.9094,'Xinghua',NULL,NULL),(321282,'靖江市',321200,'靖江',3,'0523','214500','中国,江苏省,泰州市,靖江市',120.273,32.0159,'Jingjiang',NULL,NULL),(321283,'泰兴市',321200,'泰兴',3,'0523','225400','中国,江苏省,泰州市,泰兴市',120.052,32.1719,'Taixing',NULL,NULL),(321300,'宿迁市',320000,'宿迁',2,'0527','223800','中国,江苏省,宿迁市',118.293,33.9452,'Suqian',NULL,NULL),(321302,'宿城区',321300,'宿城',3,'0527','223800','中国,江苏省,宿迁市,宿城区',118.291,33.9422,'Sucheng',NULL,NULL),(321311,'宿豫区',321300,'宿豫',3,'0527','223800','中国,江苏省,宿迁市,宿豫区',118.329,33.9467,'Suyu',NULL,NULL),(321322,'沭阳县',321300,'沭阳',3,'0527','223600','中国,江苏省,宿迁市,沭阳县',118.769,34.1145,'Shuyang',NULL,NULL),(321323,'泗阳县',321300,'泗阳',3,'0527','223700','中国,江苏省,宿迁市,泗阳县',118.703,33.721,'Siyang',NULL,NULL),(321324,'泗洪县',321300,'泗洪',3,'0527','223900','中国,江苏省,宿迁市,泗洪县',118.217,33.46,'Sihong',NULL,NULL),(330000,'浙江省',100000,'浙江',1,'','','中国,浙江省',120.154,30.2875,'Zhejiang',NULL,NULL),(330100,'杭州市',330000,'杭州',2,'0571','310026','中国,浙江省,杭州市',120.154,30.2875,'Hangzhou',NULL,NULL),(330102,'上城区',330100,'上城',3,'0571','310002','中国,浙江省,杭州市,上城区',120.169,30.2425,'Shangcheng',NULL,NULL),(330103,'下城区',330100,'下城',3,'0571','310006','中国,浙江省,杭州市,下城区',120.181,30.2815,'Xiacheng',NULL,NULL),(330104,'江干区',330100,'江干',3,'0571','310016','中国,浙江省,杭州市,江干区',120.205,30.2572,'Jianggan',NULL,NULL),(330105,'拱墅区',330100,'拱墅',3,'0571','310011','中国,浙江省,杭州市,拱墅区',120.142,30.3197,'Gongshu',NULL,NULL),(330106,'西湖区',330100,'西湖',3,'0571','310013','中国,浙江省,杭州市,西湖区',120.13,30.2595,'Xihu',NULL,NULL),(330108,'滨江区',330100,'滨江',3,'0571','310051','中国,浙江省,杭州市,滨江区',120.212,30.2083,'Binjiang',NULL,NULL),(330109,'萧山区',330100,'萧山',3,'0571','311200','中国,浙江省,杭州市,萧山区',120.265,30.185,'Xiaoshan',NULL,NULL),(330110,'余杭区',330100,'余杭',3,'0571','311100','中国,浙江省,杭州市,余杭区',120.3,30.4183,'Yuhang',NULL,NULL),(330122,'桐庐县',330100,'桐庐',3,'0571','311500','中国,浙江省,杭州市,桐庐县',119.689,29.7978,'Tonglu',NULL,NULL),(330127,'淳安县',330100,'淳安',3,'0571','311700','中国,浙江省,杭州市,淳安县',119.043,29.6099,'Chun\'an',NULL,NULL),(330182,'建德市',330100,'建德',3,'0571','311600','中国,浙江省,杭州市,建德市',119.282,29.476,'Jiande',NULL,NULL),(330183,'富阳区',330100,'富阳',3,'0571','311400','中国,浙江省,杭州市,富阳区',119.96,30.0488,'Fuyang',NULL,NULL),(330185,'临安市',330100,'临安',3,'0571','311300','中国,浙江省,杭州市,临安市',119.725,30.2345,'Lin\'an',NULL,NULL),(330200,'宁波市',330000,'宁波',2,'0574','315000','中国,浙江省,宁波市',121.55,29.8684,'Ningbo',NULL,NULL),(330203,'海曙区',330200,'海曙',3,'0574','315000','中国,浙江省,宁波市,海曙区',121.551,29.8598,'Haishu',NULL,NULL),(330204,'江东区',330200,'江东',3,'0574','315040','中国,浙江省,宁波市,江东区',121.57,29.867,'Jiangdong',NULL,NULL),(330205,'江北区',330200,'江北',3,'0574','315020','中国,浙江省,宁波市,江北区',121.557,29.8878,'Jiangbei',NULL,NULL),(330206,'北仑区',330200,'北仑',3,'0574','315800','中国,浙江省,宁波市,北仑区',121.844,29.9007,'Beilun',NULL,NULL),(330211,'镇海区',330200,'镇海',3,'0574','315200','中国,浙江省,宁波市,镇海区',121.716,29.9489,'Zhenhai',NULL,NULL),(330212,'鄞州区',330200,'鄞州',3,'0574','315100','中国,浙江省,宁波市,鄞州区',121.548,29.8161,'Yinzhou',NULL,NULL),(330225,'象山县',330200,'象山',3,'0574','315700','中国,浙江省,宁波市,象山县',121.869,29.4776,'Xiangshan',NULL,NULL),(330226,'宁海县',330200,'宁海',3,'0574','315600','中国,浙江省,宁波市,宁海县',121.431,29.2889,'Ninghai',NULL,NULL),(330281,'余姚市',330200,'余姚',3,'0574','315400','中国,浙江省,宁波市,余姚市',121.153,30.0387,'Yuyao',NULL,NULL),(330282,'慈溪市',330200,'慈溪',3,'0574','315300','中国,浙江省,宁波市,慈溪市',121.266,30.1696,'Cixi',NULL,NULL),(330283,'奉化市',330200,'奉化',3,'0574','315500','中国,浙江省,宁波市,奉化市',121.41,29.6554,'Fenghua',NULL,NULL),(330300,'温州市',330000,'温州',2,'0577','325000','中国,浙江省,温州市',120.672,28.0006,'Wenzhou',NULL,NULL),(330302,'鹿城区',330300,'鹿城',3,'0577','325000','中国,浙江省,温州市,鹿城区',120.655,28.0149,'Lucheng',NULL,NULL),(330303,'龙湾区',330300,'龙湾',3,'0577','325013','中国,浙江省,温州市,龙湾区',120.831,27.9128,'Longwan',NULL,NULL),(330304,'瓯海区',330300,'瓯海',3,'0577','325005','中国,浙江省,温州市,瓯海区',120.638,28.0071,'Ouhai',NULL,NULL),(330322,'洞头县',330300,'洞头',3,'0577','325700','中国,浙江省,温州市,洞头县',121.156,27.8363,'Dongtou',NULL,NULL),(330324,'永嘉县',330300,'永嘉',3,'0577','325100','中国,浙江省,温州市,永嘉县',120.693,28.1546,'Yongjia',NULL,NULL),(330326,'平阳县',330300,'平阳',3,'0577','325400','中国,浙江省,温州市,平阳县',120.565,27.6625,'Pingyang',NULL,NULL),(330327,'苍南县',330300,'苍南',3,'0577','325800','中国,浙江省,温州市,苍南县',120.426,27.5174,'Cangnan',NULL,NULL),(330328,'文成县',330300,'文成',3,'0577','325300','中国,浙江省,温州市,文成县',120.091,27.7868,'Wencheng',NULL,NULL),(330329,'泰顺县',330300,'泰顺',3,'0577','325500','中国,浙江省,温州市,泰顺县',119.718,27.5569,'Taishun',NULL,NULL),(330381,'瑞安市',330300,'瑞安',3,'0577','325200','中国,浙江省,温州市,瑞安市',120.655,27.7804,'Rui\'an',NULL,NULL),(330382,'乐清市',330300,'乐清',3,'0577','325600','中国,浙江省,温州市,乐清市',120.962,28.124,'Yueqing',NULL,NULL),(330400,'嘉兴市',330000,'嘉兴',2,'0573','314000','中国,浙江省,嘉兴市',120.751,30.7627,'Jiaxing',NULL,NULL),(330402,'南湖区',330400,'南湖',3,'0573','314051','中国,浙江省,嘉兴市,南湖区',120.785,30.7486,'Nanhu',NULL,NULL),(330411,'秀洲区',330400,'秀洲',3,'0573','314031','中国,浙江省,嘉兴市,秀洲区',120.709,30.7645,'Xiuzhou',NULL,NULL),(330421,'嘉善县',330400,'嘉善',3,'0573','314100','中国,浙江省,嘉兴市,嘉善县',120.926,30.8299,'Jiashan',NULL,NULL),(330424,'海盐县',330400,'海盐',3,'0573','314300','中国,浙江省,嘉兴市,海盐县',120.946,30.5255,'Haiyan',NULL,NULL),(330481,'海宁市',330400,'海宁',3,'0573','314400','中国,浙江省,嘉兴市,海宁市',120.681,30.5097,'Haining',NULL,NULL),(330482,'平湖市',330400,'平湖',3,'0573','314200','中国,浙江省,嘉兴市,平湖市',121.022,30.6962,'Pinghu',NULL,NULL),(330483,'桐乡市',330400,'桐乡',3,'0573','314500','中国,浙江省,嘉兴市,桐乡市',120.565,30.6302,'Tongxiang',NULL,NULL),(330500,'湖州市',330000,'湖州',2,'0572','313000','中国,浙江省,湖州市',120.102,30.8672,'Huzhou',NULL,NULL),(330502,'吴兴区',330500,'吴兴',3,'0572','313000','中国,浙江省,湖州市,吴兴区',120.125,30.8575,'Wuxing',NULL,NULL),(330503,'南浔区',330500,'南浔',3,'0572','313009','中国,浙江省,湖州市,南浔区',120.42,30.8669,'Nanxun',NULL,NULL),(330521,'德清县',330500,'德清',3,'0572','313200','中国,浙江省,湖州市,德清县',119.978,30.5337,'Deqing',NULL,NULL),(330522,'长兴县',330500,'长兴',3,'0572','313100','中国,浙江省,湖州市,长兴县',119.908,31.0061,'Changxing',NULL,NULL),(330523,'安吉县',330500,'安吉',3,'0572','313300','中国,浙江省,湖州市,安吉县',119.682,30.638,'Anji',NULL,NULL),(330600,'绍兴市',330000,'绍兴',2,'0575','312000','中国,浙江省,绍兴市',120.582,29.9971,'Shaoxing',NULL,NULL),(330602,'越城区',330600,'越城',3,'0575','312000','中国,浙江省,绍兴市,越城区',120.582,29.989,'Yuecheng',NULL,NULL),(330603,'柯桥区',330600,'柯桥',3,'0575','312030','中国,浙江省,绍兴市,柯桥区',120.493,30.0876,'Keqiao ',NULL,NULL),(330604,'上虞区',330600,'上虞',3,'0575','312300','中国,浙江省,绍兴市,上虞区',120.476,30.078,'Shangyu',NULL,NULL),(330624,'新昌县',330600,'新昌',3,'0575','312500','中国,浙江省,绍兴市,新昌县',120.904,29.4999,'Xinchang',NULL,NULL),(330681,'诸暨市',330600,'诸暨',3,'0575','311800','中国,浙江省,绍兴市,诸暨市',120.236,29.7136,'Zhuji',NULL,NULL),(330683,'嵊州市',330600,'嵊州',3,'0575','312400','中国,浙江省,绍兴市,嵊州市',120.822,29.5885,'Shengzhou',NULL,NULL),(330700,'金华市',330000,'金华',2,'0579','321000','中国,浙江省,金华市',119.65,29.0895,'Jinhua',NULL,NULL),(330702,'婺城区',330700,'婺城',3,'0579','321000','中国,浙江省,金华市,婺城区',119.571,29.0952,'Wucheng',NULL,NULL),(330703,'金东区',330700,'金东',3,'0579','321000','中国,浙江省,金华市,金东区',119.693,29.0991,'Jindong',NULL,NULL),(330723,'武义县',330700,'武义',3,'0579','321200','中国,浙江省,金华市,武义县',119.816,28.8933,'Wuyi',NULL,NULL),(330726,'浦江县',330700,'浦江',3,'0579','322200','中国,浙江省,金华市,浦江县',119.892,29.4535,'Pujiang',NULL,NULL),(330727,'磐安县',330700,'磐安',3,'0579','322300','中国,浙江省,金华市,磐安县',120.45,29.0573,'Pan\'an',NULL,NULL),(330781,'兰溪市',330700,'兰溪',3,'0579','321100','中国,浙江省,金华市,兰溪市',119.46,29.2084,'Lanxi',NULL,NULL),(330782,'义乌市',330700,'义乌',3,'0579','322000','中国,浙江省,金华市,义乌市',120.074,29.3056,'Yiwu',NULL,NULL),(330783,'东阳市',330700,'东阳',3,'0579','322100','中国,浙江省,金华市,东阳市',120.242,29.2894,'Dongyang',NULL,NULL),(330784,'永康市',330700,'永康',3,'0579','321300','中国,浙江省,金华市,永康市',120.047,28.8884,'Yongkang',NULL,NULL),(330800,'衢州市',330000,'衢州',2,'0570','324002','中国,浙江省,衢州市',118.873,28.9417,'Quzhou',NULL,NULL),(330802,'柯城区',330800,'柯城',3,'0570','324100','中国,浙江省,衢州市,柯城区',118.871,28.9686,'Kecheng',NULL,NULL),(330803,'衢江区',330800,'衢江',3,'0570','324022','中国,浙江省,衢州市,衢江区',118.96,28.9798,'Qujiang',NULL,NULL),(330822,'常山县',330800,'常山',3,'0570','324200','中国,浙江省,衢州市,常山县',118.51,28.9019,'Changshan',NULL,NULL),(330824,'开化县',330800,'开化',3,'0570','324300','中国,浙江省,衢州市,开化县',118.416,29.1378,'Kaihua',NULL,NULL),(330825,'龙游县',330800,'龙游',3,'0570','324400','中国,浙江省,衢州市,龙游县',119.172,29.0282,'Longyou',NULL,NULL),(330881,'江山市',330800,'江山',3,'0570','324100','中国,浙江省,衢州市,江山市',118.627,28.7386,'Jiangshan',NULL,NULL),(330900,'舟山市',330000,'舟山',2,'0580','316000','中国,浙江省,舟山市',122.107,30.016,'Zhoushan',NULL,NULL),(330902,'定海区',330900,'定海',3,'0580','316000','中国,浙江省,舟山市,定海区',122.107,30.0198,'Dinghai',NULL,NULL),(330903,'普陀区',330900,'普陀',3,'0580','316100','中国,浙江省,舟山市,普陀区',122.303,29.9491,'Putuo',NULL,NULL),(330921,'岱山县',330900,'岱山',3,'0580','316200','中国,浙江省,舟山市,岱山县',122.205,30.2439,'Daishan',NULL,NULL),(330922,'嵊泗县',330900,'嵊泗',3,'0580','202450','中国,浙江省,舟山市,嵊泗县',122.451,30.7268,'Shengsi',NULL,NULL),(331000,'台州市',330000,'台州',2,'0576','318000','中国,浙江省,台州市',121.429,28.6614,'Taizhou',NULL,NULL),(331002,'椒江区',331000,'椒江',3,'0576','318000','中国,浙江省,台州市,椒江区',121.443,28.673,'Jiaojiang',NULL,NULL),(331003,'黄岩区',331000,'黄岩',3,'0576','318020','中国,浙江省,台州市,黄岩区',121.259,28.6508,'Huangyan',NULL,NULL),(331004,'路桥区',331000,'路桥',3,'0576','318050','中国,浙江省,台州市,路桥区',121.374,28.5802,'Luqiao',NULL,NULL),(331021,'玉环县',331000,'玉环',3,'0576','317600','中国,浙江省,台州市,玉环县',121.232,28.1364,'Yuhuan',NULL,NULL),(331022,'三门县',331000,'三门',3,'0576','317100','中国,浙江省,台州市,三门县',121.394,29.1051,'Sanmen',NULL,NULL),(331023,'天台县',331000,'天台',3,'0576','317200','中国,浙江省,台州市,天台县',121.008,29.1429,'Tiantai',NULL,NULL),(331024,'仙居县',331000,'仙居',3,'0576','317300','中国,浙江省,台州市,仙居县',120.729,28.8467,'Xianju',NULL,NULL),(331081,'温岭市',331000,'温岭',3,'0576','317500','中国,浙江省,台州市,温岭市',121.386,28.3718,'Wenling',NULL,NULL),(331082,'临海市',331000,'临海',3,'0576','317000','中国,浙江省,台州市,临海市',121.139,28.856,'Linhai',NULL,NULL),(331100,'丽水市',330000,'丽水',2,'0578','323000','中国,浙江省,丽水市',119.922,28.452,'Lishui',NULL,NULL),(331102,'莲都区',331100,'莲都',3,'0578','323000','中国,浙江省,丽水市,莲都区',119.913,28.4458,'Liandu',NULL,NULL),(331121,'青田县',331100,'青田',3,'0578','323900','中国,浙江省,丽水市,青田县',120.29,28.139,'Qingtian',NULL,NULL),(331122,'缙云县',331100,'缙云',3,'0578','321400','中国,浙江省,丽水市,缙云县',120.09,28.6594,'Jinyun',NULL,NULL),(331123,'遂昌县',331100,'遂昌',3,'0578','323300','中国,浙江省,丽水市,遂昌县',119.276,28.5929,'Suichang',NULL,NULL),(331124,'松阳县',331100,'松阳',3,'0578','323400','中国,浙江省,丽水市,松阳县',119.482,28.4494,'Songyang',NULL,NULL),(331125,'云和县',331100,'云和',3,'0578','323600','中国,浙江省,丽水市,云和县',119.573,28.1164,'Yunhe',NULL,NULL),(331126,'庆元县',331100,'庆元',3,'0578','323800','中国,浙江省,丽水市,庆元县',119.063,27.6184,'Qingyuan',NULL,NULL),(331127,'景宁畲族自治县',331100,'景宁',3,'0578','323500','中国,浙江省,丽水市,景宁畲族自治县',119.638,27.9739,'Jingning',NULL,NULL),(331181,'龙泉市',331100,'龙泉',3,'0578','323700','中国,浙江省,丽水市,龙泉市',119.142,28.0743,'Longquan',NULL,NULL),(331200,'舟山群岛新区',330000,'舟山新区',2,'0580','316000','中国,浙江省,舟山群岛新区',122.318,29.8132,'Zhoushan',NULL,NULL),(331201,'金塘岛',331200,'金塘',3,'0580','316000','中国,浙江省,舟山群岛新区,金塘岛',121.893,30.0406,'Jintang',NULL,NULL),(331202,'六横岛',331200,'六横',3,'0580','316000','中国,浙江省,舟山群岛新区,六横岛',122.143,29.6629,'Liuheng',NULL,NULL),(331203,'衢山岛',331200,'衢山',3,'0580','316000','中国,浙江省,舟山群岛新区,衢山岛',122.358,30.4426,'Qushan',NULL,NULL),(331204,'舟山本岛西北部',331200,'舟山',3,'0580','316000','中国,浙江省,舟山群岛新区,舟山本岛西北部',122.031,30.1404,'Zhoushan',NULL,NULL),(331205,'岱山岛西南部',331200,'岱山',3,'0580','316000','中国,浙江省,舟山群岛新区,岱山岛西南部',122.18,30.2773,'Daishan',NULL,NULL),(331206,'泗礁岛',331200,'泗礁',3,'0580','316000','中国,浙江省,舟山群岛新区,泗礁岛',122.458,30.7251,'Sijiao',NULL,NULL),(331207,'朱家尖岛',331200,'朱家尖',3,'0580','316000','中国,浙江省,舟山群岛新区,朱家尖岛',122.391,29.9163,'Zhujiajian',NULL,NULL),(331208,'洋山岛',331200,'洋山',3,'0580','316000','中国,浙江省,舟山群岛新区,洋山岛',121.996,30.0946,'Yangshan',NULL,NULL),(331209,'长涂岛',331200,'长涂',3,'0580','316000','中国,浙江省,舟山群岛新区,长涂岛',122.285,30.2489,'Changtu',NULL,NULL),(331210,'虾峙岛',331200,'虾峙',3,'0580','316000','中国,浙江省,舟山群岛新区,虾峙岛',122.245,29.7529,'Xiazhi',NULL,NULL),(340000,'安徽省',100000,'安徽',1,'','','中国,安徽省',117.283,31.8612,'Anhui',NULL,NULL),(340100,'合肥市',340000,'合肥',2,'0551','230001','中国,安徽省,合肥市',117.283,31.8612,'Hefei',NULL,NULL),(340102,'瑶海区',340100,'瑶海',3,'0551','230011','中国,安徽省,合肥市,瑶海区',117.309,31.8581,'Yaohai',NULL,NULL),(340103,'庐阳区',340100,'庐阳',3,'0551','230001','中国,安徽省,合肥市,庐阳区',117.265,31.8787,'Luyang',NULL,NULL),(340104,'蜀山区',340100,'蜀山',3,'0551','230031','中国,安徽省,合肥市,蜀山区',117.261,31.8512,'Shushan',NULL,NULL),(340111,'包河区',340100,'包河',3,'0551','230041','中国,安徽省,合肥市,包河区',117.31,31.795,'Baohe',NULL,NULL),(340121,'长丰县',340100,'长丰',3,'0551','231100','中国,安徽省,合肥市,长丰县',117.165,32.4796,'Changfeng',NULL,NULL),(340122,'肥东县',340100,'肥东',3,'0551','231600','中国,安徽省,合肥市,肥东县',117.471,31.8853,'Feidong',NULL,NULL),(340123,'肥西县',340100,'肥西',3,'0551','231200','中国,安徽省,合肥市,肥西县',117.168,31.7214,'Feixi',NULL,NULL),(340124,'庐江县',340100,'庐江',3,'0565','231500','中国,安徽省,合肥市,庐江县',117.29,31.2515,'Lujiang',NULL,NULL),(340181,'巢湖市',340100,'巢湖',3,'0565','238000','中国,安徽省,合肥市,巢湖市',117.874,31.6005,'Chaohu',NULL,NULL),(340200,'芜湖市',340000,'芜湖',2,'0551','241000','中国,安徽省,芜湖市',118.376,31.3263,'Wuhu',NULL,NULL),(340202,'镜湖区',340200,'镜湖',3,'0553','241000','中国,安徽省,芜湖市,镜湖区',118.385,31.3404,'Jinghu',NULL,NULL),(340203,'弋江区',340200,'弋江',3,'0553','241000','中国,安徽省,芜湖市,弋江区',118.373,31.3118,'Yijiang',NULL,NULL),(340207,'鸠江区',340200,'鸠江',3,'0553','241000','中国,安徽省,芜湖市,鸠江区',118.392,31.3693,'Jiujiang',NULL,NULL),(340208,'三山区',340200,'三山',3,'0553','241000','中国,安徽省,芜湖市,三山区',118.225,31.207,'Sanshan',NULL,NULL),(340221,'芜湖县',340200,'芜湖',3,'0553','241100','中国,安徽省,芜湖市,芜湖县',118.575,31.1348,'Wuhu',NULL,NULL),(340222,'繁昌县',340200,'繁昌',3,'0553','241200','中国,安徽省,芜湖市,繁昌县',118.2,31.0832,'Fanchang',NULL,NULL),(340223,'南陵县',340200,'南陵',3,'0553','242400','中国,安徽省,芜湖市,南陵县',118.337,30.9197,'Nanling',NULL,NULL),(340225,'无为县',340200,'无为',3,'0565','238300','中国,安徽省,芜湖市,无为县',117.911,31.3031,'Wuwei',NULL,NULL),(340300,'蚌埠市',340000,'蚌埠',2,'0552','233000','中国,安徽省,蚌埠市',117.362,32.934,'Bengbu',NULL,NULL),(340302,'龙子湖区',340300,'龙子湖',3,'0552','233000','中国,安徽省,蚌埠市,龙子湖区',117.394,32.943,'Longzihu',NULL,NULL),(340303,'蚌山区',340300,'蚌山',3,'0552','233000','中国,安徽省,蚌埠市,蚌山区',117.368,32.9441,'Bengshan',NULL,NULL),(340304,'禹会区',340300,'禹会',3,'0552','233010','中国,安徽省,蚌埠市,禹会区',117.353,32.9334,'Yuhui',NULL,NULL),(340311,'淮上区',340300,'淮上',3,'0552','233002','中国,安徽省,蚌埠市,淮上区',117.36,32.9642,'Huaishang',NULL,NULL),(340321,'怀远县',340300,'怀远',3,'0552','233400','中国,安徽省,蚌埠市,怀远县',117.205,32.9701,'Huaiyuan',NULL,NULL),(340322,'五河县',340300,'五河',3,'0552','233300','中国,安徽省,蚌埠市,五河县',117.891,33.1446,'Wuhe',NULL,NULL),(340323,'固镇县',340300,'固镇',3,'0552','233700','中国,安徽省,蚌埠市,固镇县',117.316,33.318,'Guzhen',NULL,NULL),(340400,'淮南市',340000,'淮南',2,'0554','232001','中国,安徽省,淮南市',117.025,32.6459,'Huainan',NULL,NULL),(340402,'大通区',340400,'大通',3,'0554','232033','中国,安徽省,淮南市,大通区',117.053,32.6326,'Datong',NULL,NULL),(340403,'田家庵区',340400,'田家庵',3,'0554','232000','中国,安徽省,淮南市,田家庵区',117.017,32.647,'Tianjiaan',NULL,NULL),(340404,'谢家集区',340400,'谢家集',3,'0554','232052','中国,安徽省,淮南市,谢家集区',116.864,32.5982,'Xiejiaji',NULL,NULL),(340405,'八公山区',340400,'八公山',3,'0554','232072','中国,安徽省,淮南市,八公山区',116.837,32.6294,'Bagongshan',NULL,NULL),(340406,'潘集区',340400,'潘集',3,'0554','232082','中国,安徽省,淮南市,潘集区',116.816,32.7829,'Panji',NULL,NULL),(340421,'凤台县',340400,'凤台',3,'0554','232100','中国,安徽省,淮南市,凤台县',116.716,32.7075,'Fengtai',NULL,NULL),(340500,'马鞍山市',340000,'马鞍山',2,'0555','243001','中国,安徽省,马鞍山市',118.508,31.6894,'Ma\'anshan',NULL,NULL),(340503,'花山区',340500,'花山',3,'0555','243000','中国,安徽省,马鞍山市,花山区',118.512,31.7001,'Huashan',NULL,NULL),(340504,'雨山区',340500,'雨山',3,'0555','243071','中国,安徽省,马鞍山市,雨山区',118.499,31.6822,'Yushan',NULL,NULL),(340506,'博望区',340500,'博望',3,'0555','243131','中国,安徽省,马鞍山市,博望区',118.844,31.5619,'Bowang',NULL,NULL),(340521,'当涂县',340500,'当涂',3,'0555','243100','中国,安徽省,马鞍山市,当涂县',118.498,31.571,'Dangtu',NULL,NULL),(340522,'含山县',340500,'含山',3,'0555','238100','中国,安徽省,马鞍山市,含山县',118.106,31.7278,'Hanshan ',NULL,NULL),(340523,'和县',340500,'和县',3,'0555','238200','中国,安徽省,马鞍山市,和县',118.351,31.7418,'Hexian',NULL,NULL),(340600,'淮北市',340000,'淮北',2,'0561','235000','中国,安徽省,淮北市',116.795,33.9717,'Huaibei',NULL,NULL),(340602,'杜集区',340600,'杜集',3,'0561','235000','中国,安徽省,淮北市,杜集区',116.83,33.9936,'Duji',NULL,NULL),(340603,'相山区',340600,'相山',3,'0561','235000','中国,安徽省,淮北市,相山区',116.795,33.9598,'Xiangshan',NULL,NULL),(340604,'烈山区',340600,'烈山',3,'0561','235000','中国,安徽省,淮北市,烈山区',116.814,33.8936,'Lieshan',NULL,NULL),(340621,'濉溪县',340600,'濉溪',3,'0561','235100','中国,安徽省,淮北市,濉溪县',116.768,33.9146,'Suixi',NULL,NULL),(340700,'铜陵市',340000,'铜陵',2,'0562','244000','中国,安徽省,铜陵市',117.817,30.9299,'Tongling',NULL,NULL),(340702,'铜官山区',340700,'铜官山',3,'0562','244000','中国,安徽省,铜陵市,铜官山区',117.815,30.9342,'Tongguanshan',NULL,NULL),(340703,'狮子山区',340700,'狮子山',3,'0562','244000','中国,安徽省,铜陵市,狮子山区',117.892,30.9263,'Shizishan',NULL,NULL),(340711,'郊区',340700,'郊区',3,'0562','244000','中国,安徽省,铜陵市,郊区',117.809,30.9198,'Jiaoqu',NULL,NULL),(340721,'铜陵县',340700,'铜陵',3,'0562','244100','中国,安徽省,铜陵市,铜陵县',117.791,30.9536,'Tongling',NULL,NULL),(340800,'安庆市',340000,'安庆',2,'0556','246001','中国,安徽省,安庆市',117.054,30.5248,'Anqing',NULL,NULL),(340802,'迎江区',340800,'迎江',3,'0556','246001','中国,安徽省,安庆市,迎江区',117.049,30.5042,'Yingjiang',NULL,NULL),(340803,'大观区',340800,'大观',3,'0556','246002','中国,安徽省,安庆市,大观区',117.034,30.5122,'Daguan',NULL,NULL),(340811,'宜秀区',340800,'宜秀',3,'0556','246003','中国,安徽省,安庆市,宜秀区',117.061,30.5078,'Yixiu',NULL,NULL),(340822,'怀宁县',340800,'怀宁',3,'0556','246100','中国,安徽省,安庆市,怀宁县',116.83,30.7338,'Huaining',NULL,NULL),(340823,'枞阳县',340800,'枞阳',3,'0556','246700','中国,安徽省,安庆市,枞阳县',117.22,30.6996,'Zongyang',NULL,NULL),(340824,'潜山县',340800,'潜山',3,'0556','246300','中国,安徽省,安庆市,潜山县',116.576,30.6304,'Qianshan',NULL,NULL),(340825,'太湖县',340800,'太湖',3,'0556','246400','中国,安徽省,安庆市,太湖县',116.309,30.4541,'Taihu',NULL,NULL),(340826,'宿松县',340800,'宿松',3,'0556','246500','中国,安徽省,安庆市,宿松县',116.129,30.1536,'Susong',NULL,NULL),(340827,'望江县',340800,'望江',3,'0556','246200','中国,安徽省,安庆市,望江县',116.688,30.1259,'Wangjiang',NULL,NULL),(340828,'岳西县',340800,'岳西',3,'0556','246600','中国,安徽省,安庆市,岳西县',116.36,30.8498,'Yuexi',NULL,NULL),(340881,'桐城市',340800,'桐城',3,'0556','231400','中国,安徽省,安庆市,桐城市',116.951,31.0522,'Tongcheng',NULL,NULL),(341000,'黄山市',340000,'黄山',2,'0559','245000','中国,安徽省,黄山市',118.317,29.7092,'Huangshan',NULL,NULL),(341002,'屯溪区',341000,'屯溪',3,'0559','245000','中国,安徽省,黄山市,屯溪区',118.334,29.7114,'Tunxi',NULL,NULL),(341003,'黄山区',341000,'黄山',3,'0559','242700','中国,安徽省,黄山市,黄山区',118.142,30.2729,'Huangshan',NULL,NULL),(341004,'徽州区',341000,'徽州',3,'0559','245061','中国,安徽省,黄山市,徽州区',118.337,29.8278,'Huizhou',NULL,NULL),(341021,'歙县',341000,'歙县',3,'0559','245200','中国,安徽省,黄山市,歙县',118.437,29.8675,'Shexian',NULL,NULL),(341022,'休宁县',341000,'休宁',3,'0559','245400','中国,安徽省,黄山市,休宁县',118.181,29.7861,'Xiuning',NULL,NULL),(341023,'黟县',341000,'黟县',3,'0559','245500','中国,安徽省,黄山市,黟县',117.941,29.9259,'Yixian',NULL,NULL),(341024,'祁门县',341000,'祁门',3,'0559','245600','中国,安徽省,黄山市,祁门县',117.718,29.8572,'Qimen',NULL,NULL),(341100,'滁州市',340000,'滁州',2,'0550','239000','中国,安徽省,滁州市',118.316,32.3036,'Chuzhou',NULL,NULL),(341102,'琅琊区',341100,'琅琊',3,'0550','239000','中国,安徽省,滁州市,琅琊区',118.305,32.2952,'Langya',NULL,NULL),(341103,'南谯区',341100,'南谯',3,'0550','239000','中国,安徽省,滁州市,南谯区',118.312,32.3186,'Nanqiao',NULL,NULL),(341122,'来安县',341100,'来安',3,'0550','239200','中国,安徽省,滁州市,来安县',118.434,32.4518,'Lai\'an',NULL,NULL),(341124,'全椒县',341100,'全椒',3,'0550','239500','中国,安徽省,滁州市,全椒县',118.273,32.0852,'Quanjiao',NULL,NULL),(341125,'定远县',341100,'定远',3,'0550','233200','中国,安徽省,滁州市,定远县',117.68,32.5249,'Dingyuan',NULL,NULL),(341126,'凤阳县',341100,'凤阳',3,'0550','233100','中国,安徽省,滁州市,凤阳县',117.565,32.8651,'Fengyang',NULL,NULL),(341181,'天长市',341100,'天长',3,'0550','239300','中国,安徽省,滁州市,天长市',118.999,32.6912,'Tianchang',NULL,NULL),(341182,'明光市',341100,'明光',3,'0550','239400','中国,安徽省,滁州市,明光市',117.991,32.7782,'Mingguang',NULL,NULL),(341200,'阜阳市',340000,'阜阳',2,'0558','236033','中国,安徽省,阜阳市',115.82,32.897,'Fuyang',NULL,NULL),(341202,'颍州区',341200,'颍州',3,'0558','236001','中国,安徽省,阜阳市,颍州区',115.807,32.8835,'Yingzhou',NULL,NULL),(341203,'颍东区',341200,'颍东',3,'0558','236058','中国,安徽省,阜阳市,颍东区',115.857,32.913,'Yingdong',NULL,NULL),(341204,'颍泉区',341200,'颍泉',3,'0558','236045','中国,安徽省,阜阳市,颍泉区',115.807,32.9249,'Yingquan',NULL,NULL),(341221,'临泉县',341200,'临泉',3,'0558','236400','中国,安徽省,阜阳市,临泉县',115.262,33.0676,'Linquan',NULL,NULL),(341222,'太和县',341200,'太和',3,'0558','236600','中国,安徽省,阜阳市,太和县',115.622,33.1603,'Taihe',NULL,NULL),(341225,'阜南县',341200,'阜南',3,'0558','236300','中国,安徽省,阜阳市,阜南县',115.586,32.6355,'Funan',NULL,NULL),(341226,'颍上县',341200,'颍上',3,'0558','236200','中国,安徽省,阜阳市,颍上县',116.265,32.63,'Yingshang',NULL,NULL),(341282,'界首市',341200,'界首',3,'0558','236500','中国,安徽省,阜阳市,界首市',115.374,33.2571,'Jieshou',NULL,NULL),(341300,'宿州市',340000,'宿州',2,'0557','234000','中国,安徽省,宿州市',116.984,33.6339,'Suzhou',NULL,NULL),(341302,'埇桥区',341300,'埇桥',3,'0557','234000','中国,安徽省,宿州市,埇桥区',116.977,33.6406,'Yongqiao',NULL,NULL),(341321,'砀山县',341300,'砀山',3,'0557','235300','中国,安徽省,宿州市,砀山县',116.354,34.4236,'Dangshan',NULL,NULL),(341322,'萧县',341300,'萧县',3,'0557','235200','中国,安徽省,宿州市,萧县',116.945,34.1879,'Xiaoxian',NULL,NULL),(341323,'灵璧县',341300,'灵璧',3,'0557','234200','中国,安徽省,宿州市,灵璧县',117.558,33.5434,'Lingbi',NULL,NULL),(341324,'泗县',341300,'泗县',3,'0557','234300','中国,安徽省,宿州市,泗县',117.91,33.4829,'Sixian',NULL,NULL),(341500,'六安市',340000,'六安',2,'0564','237000','中国,安徽省,六安市',116.508,31.7529,'Lu\'an',NULL,NULL),(341502,'金安区',341500,'金安',3,'0564','237005','中国,安徽省,六安市,金安区',116.509,31.7557,'Jin\'an',NULL,NULL),(341503,'裕安区',341500,'裕安',3,'0564','237010','中国,安徽省,六安市,裕安区',116.48,31.7379,'Yu\'an',NULL,NULL),(341521,'寿县',341500,'寿县',3,'0564','232200','中国,安徽省,六安市,寿县',116.785,32.5765,'Shouxian',NULL,NULL),(341522,'霍邱县',341500,'霍邱',3,'0564','237400','中国,安徽省,六安市,霍邱县',116.278,32.353,'Huoqiu',NULL,NULL),(341523,'舒城县',341500,'舒城',3,'0564','231300','中国,安徽省,六安市,舒城县',116.945,31.4641,'Shucheng',NULL,NULL),(341524,'金寨县',341500,'金寨',3,'0564','237300','中国,安徽省,六安市,金寨县',115.935,31.7351,'Jinzhai',NULL,NULL),(341525,'霍山县',341500,'霍山',3,'0564','237200','中国,安徽省,六安市,霍山县',116.333,31.3929,'Huoshan',NULL,NULL),(341600,'亳州市',340000,'亳州',2,'0558','236802','中国,安徽省,亳州市',115.783,33.8693,'Bozhou',NULL,NULL),(341602,'谯城区',341600,'谯城',3,'0558','236800','中国,安徽省,亳州市,谯城区',115.779,33.8753,'Qiaocheng',NULL,NULL),(341621,'涡阳县',341600,'涡阳',3,'0558','233600','中国,安徽省,亳州市,涡阳县',116.217,33.5091,'Guoyang',NULL,NULL),(341622,'蒙城县',341600,'蒙城',3,'0558','233500','中国,安徽省,亳州市,蒙城县',116.565,33.2648,'Mengcheng',NULL,NULL),(341623,'利辛县',341600,'利辛',3,'0558','236700','中国,安徽省,亳州市,利辛县',116.208,33.142,'Lixin',NULL,NULL),(341700,'池州市',340000,'池州',2,'0566','247100','中国,安徽省,池州市',117.489,30.656,'Chizhou',NULL,NULL),(341702,'贵池区',341700,'贵池',3,'0566','247100','中国,安徽省,池州市,贵池区',117.487,30.6528,'Guichi',NULL,NULL),(341721,'东至县',341700,'东至',3,'0566','247200','中国,安徽省,池州市,东至县',117.027,30.0969,'Dongzhi',NULL,NULL),(341722,'石台县',341700,'石台',3,'0566','245100','中国,安徽省,池州市,石台县',117.487,30.2104,'Shitai',NULL,NULL),(341723,'青阳县',341700,'青阳',3,'0566','242800','中国,安徽省,池州市,青阳县',117.847,30.6393,'Qingyang',NULL,NULL),(341800,'宣城市',340000,'宣城',2,'0563','242000','中国,安徽省,宣城市',118.758,30.9457,'Xuancheng',NULL,NULL),(341802,'宣州区',341800,'宣州',3,'0563','242000','中国,安徽省,宣城市,宣州区',118.755,30.9444,'Xuanzhou',NULL,NULL),(341821,'郎溪县',341800,'郎溪',3,'0563','242100','中国,安徽省,宣城市,郎溪县',119.179,31.126,'Langxi',NULL,NULL),(341822,'广德县',341800,'广德',3,'0563','242200','中国,安徽省,宣城市,广德县',119.418,30.8937,'Guangde',NULL,NULL),(341823,'泾县',341800,'泾县',3,'0563','242500','中国,安徽省,宣城市,泾县',118.42,30.695,'Jingxian',NULL,NULL),(341824,'绩溪县',341800,'绩溪',3,'0563','245300','中国,安徽省,宣城市,绩溪县',118.598,30.0707,'Jixi',NULL,NULL),(341825,'旌德县',341800,'旌德',3,'0563','242600','中国,安徽省,宣城市,旌德县',118.543,30.289,'Jingde',NULL,NULL),(341881,'宁国市',341800,'宁国',3,'0563','242300','中国,安徽省,宣城市,宁国市',118.983,30.6238,'Ningguo',NULL,NULL),(350000,'福建省',100000,'福建',1,'','','中国,福建省',119.306,26.0753,'Fujian',NULL,NULL),(350100,'福州市',350000,'福州',2,'0591','350001','中国,福建省,福州市',119.306,26.0753,'Fuzhou',NULL,NULL),(350102,'鼓楼区',350100,'鼓楼',3,'0591','350001','中国,福建省,福州市,鼓楼区',119.304,26.0823,'Gulou',NULL,NULL),(350103,'台江区',350100,'台江',3,'0591','350004','中国,福建省,福州市,台江区',119.309,26.062,'Taijiang',NULL,NULL),(350104,'仓山区',350100,'仓山',3,'0591','350007','中国,福建省,福州市,仓山区',119.315,26.0434,'Cangshan',NULL,NULL),(350105,'马尾区',350100,'马尾',3,'0591','350015','中国,福建省,福州市,马尾区',119.455,25.9894,'Mawei',NULL,NULL),(350111,'晋安区',350100,'晋安',3,'0591','350011','中国,福建省,福州市,晋安区',119.328,26.0818,'Jin\'an',NULL,NULL),(350121,'闽侯县',350100,'闽侯',3,'0591','350100','中国,福建省,福州市,闽侯县',119.134,26.1501,'Minhou',NULL,NULL),(350122,'连江县',350100,'连江',3,'0591','350500','中国,福建省,福州市,连江县',119.534,26.1947,'Lianjiang',NULL,NULL),(350123,'罗源县',350100,'罗源',3,'0591','350600','中国,福建省,福州市,罗源县',119.551,26.4875,'Luoyuan',NULL,NULL),(350124,'闽清县',350100,'闽清',3,'0591','350800','中国,福建省,福州市,闽清县',118.862,26.219,'Minqing',NULL,NULL),(350125,'永泰县',350100,'永泰',3,'0591','350700','中国,福建省,福州市,永泰县',118.936,25.8682,'Yongtai',NULL,NULL),(350128,'平潭县',350100,'平潭',3,'0591','350400','中国,福建省,福州市,平潭县',119.791,25.5037,'Pingtan',NULL,NULL),(350181,'福清市',350100,'福清',3,'0591','350300','中国,福建省,福州市,福清市',119.385,25.7209,'Fuqing',NULL,NULL),(350182,'长乐市',350100,'长乐',3,'0591','350200','中国,福建省,福州市,长乐市',119.523,25.9628,'Changle',NULL,NULL),(350200,'厦门市',350000,'厦门',2,'0592','361003','中国,福建省,厦门市',118.11,24.4905,'Xiamen',NULL,NULL),(350203,'思明区',350200,'思明',3,'0592','361001','中国,福建省,厦门市,思明区',118.082,24.4454,'Siming',NULL,NULL),(350205,'海沧区',350200,'海沧',3,'0592','361026','中国,福建省,厦门市,海沧区',118.033,24.4846,'Haicang',NULL,NULL),(350206,'湖里区',350200,'湖里',3,'0592','361006','中国,福建省,厦门市,湖里区',118.146,24.5125,'Huli',NULL,NULL),(350211,'集美区',350200,'集美',3,'0592','361021','中国,福建省,厦门市,集美区',118.097,24.5758,'Jimei',NULL,NULL),(350212,'同安区',350200,'同安',3,'0592','361100','中国,福建省,厦门市,同安区',118.152,24.7231,'Tong\'an',NULL,NULL),(350213,'翔安区',350200,'翔安',3,'0592','361101','中国,福建省,厦门市,翔安区',118.248,24.6186,'Xiang\'an',NULL,NULL),(350300,'莆田市',350000,'莆田',2,'0594','351100','中国,福建省,莆田市',119.008,25.431,'Putian',NULL,NULL),(350302,'城厢区',350300,'城厢',3,'0594','351100','中国,福建省,莆田市,城厢区',118.995,25.4187,'Chengxiang',NULL,NULL),(350303,'涵江区',350300,'涵江',3,'0594','351111','中国,福建省,莆田市,涵江区',119.116,25.4588,'Hanjiang',NULL,NULL),(350304,'荔城区',350300,'荔城',3,'0594','351100','中国,福建省,莆田市,荔城区',119.013,25.4337,'Licheng',NULL,NULL),(350305,'秀屿区',350300,'秀屿',3,'0594','351152','中国,福建省,莆田市,秀屿区',119.106,25.3183,'Xiuyu',NULL,NULL),(350322,'仙游县',350300,'仙游',3,'0594','351200','中国,福建省,莆田市,仙游县',118.692,25.3621,'Xianyou',NULL,NULL),(350400,'三明市',350000,'三明',2,'0598','365000','中国,福建省,三明市',117.635,26.2654,'Sanming',NULL,NULL),(350402,'梅列区',350400,'梅列',3,'0598','365000','中国,福建省,三明市,梅列区',117.646,26.2717,'Meilie',NULL,NULL),(350403,'三元区',350400,'三元',3,'0598','365001','中国,福建省,三明市,三元区',117.608,26.2337,'Sanyuan',NULL,NULL),(350421,'明溪县',350400,'明溪',3,'0598','365200','中国,福建省,三明市,明溪县',117.205,26.3529,'Mingxi',NULL,NULL),(350423,'清流县',350400,'清流',3,'0598','365300','中国,福建省,三明市,清流县',116.815,26.1714,'Qingliu',NULL,NULL),(350424,'宁化县',350400,'宁化',3,'0598','365400','中国,福建省,三明市,宁化县',116.661,26.2587,'Ninghua',NULL,NULL),(350425,'大田县',350400,'大田',3,'0598','366100','中国,福建省,三明市,大田县',117.847,25.6926,'Datian',NULL,NULL),(350426,'尤溪县',350400,'尤溪',3,'0598','365100','中国,福建省,三明市,尤溪县',118.19,26.17,'Youxi',NULL,NULL),(350427,'沙县',350400,'沙县',3,'0598','365500','中国,福建省,三明市,沙县',117.793,26.3962,'Shaxian',NULL,NULL),(350428,'将乐县',350400,'将乐',3,'0598','353300','中国,福建省,三明市,将乐县',117.473,26.7284,'Jiangle',NULL,NULL),(350429,'泰宁县',350400,'泰宁',3,'0598','354400','中国,福建省,三明市,泰宁县',117.176,26.9001,'Taining',NULL,NULL),(350430,'建宁县',350400,'建宁',3,'0598','354500','中国,福建省,三明市,建宁县',116.846,26.8309,'Jianning',NULL,NULL),(350481,'永安市',350400,'永安',3,'0598','366000','中国,福建省,三明市,永安市',117.365,25.9414,'Yong\'an',NULL,NULL),(350500,'泉州市',350000,'泉州',2,'0595','362000','中国,福建省,泉州市',118.589,24.9089,'Quanzhou',NULL,NULL),(350502,'鲤城区',350500,'鲤城',3,'0595','362000','中国,福建省,泉州市,鲤城区',118.566,24.8874,'Licheng',NULL,NULL),(350503,'丰泽区',350500,'丰泽',3,'0595','362000','中国,福建省,泉州市,丰泽区',118.613,24.8912,'Fengze',NULL,NULL),(350504,'洛江区',350500,'洛江',3,'0595','362011','中国,福建省,泉州市,洛江区',118.671,24.9398,'Luojiang',NULL,NULL),(350505,'泉港区',350500,'泉港',3,'0595','362114','中国,福建省,泉州市,泉港区',118.916,25.1201,'Quangang',NULL,NULL),(350521,'惠安县',350500,'惠安',3,'0595','362100','中国,福建省,泉州市,惠安县',118.797,25.0306,'Hui\'an',NULL,NULL),(350524,'安溪县',350500,'安溪',3,'0595','362400','中国,福建省,泉州市,安溪县',118.187,25.0563,'Anxi',NULL,NULL),(350525,'永春县',350500,'永春',3,'0595','362600','中国,福建省,泉州市,永春县',118.294,25.3218,'Yongchun',NULL,NULL),(350526,'德化县',350500,'德化',3,'0595','362500','中国,福建省,泉州市,德化县',118.242,25.4922,'Dehua',NULL,NULL),(350527,'金门县',350500,'金门',3,'','','中国,福建省,泉州市,金门县',118.323,24.4292,'Jinmen',NULL,NULL),(350581,'石狮市',350500,'石狮',3,'0595','362700','中国,福建省,泉州市,石狮市',118.648,24.7324,'Shishi',NULL,NULL),(350582,'晋江市',350500,'晋江',3,'0595','362200','中国,福建省,泉州市,晋江市',118.552,24.7814,'Jinjiang',NULL,NULL),(350583,'南安市',350500,'南安',3,'0595','362300','中国,福建省,泉州市,南安市',118.386,24.9606,'Nan\'an',NULL,NULL),(350600,'漳州市',350000,'漳州',2,'0596','363005','中国,福建省,漳州市',117.662,24.5109,'Zhangzhou',NULL,NULL),(350602,'芗城区',350600,'芗城',3,'0596','363000','中国,福建省,漳州市,芗城区',117.654,24.5108,'Xiangcheng',NULL,NULL),(350603,'龙文区',350600,'龙文',3,'0596','363005','中国,福建省,漳州市,龙文区',117.71,24.5032,'Longwen',NULL,NULL),(350622,'云霄县',350600,'云霄',3,'0596','363300','中国,福建省,漳州市,云霄县',117.341,23.9553,'Yunxiao',NULL,NULL),(350623,'漳浦县',350600,'漳浦',3,'0596','363200','中国,福建省,漳州市,漳浦县',117.614,24.1171,'Zhangpu',NULL,NULL),(350624,'诏安县',350600,'诏安',3,'0596','363500','中国,福建省,漳州市,诏安县',117.175,23.7115,'Zhao\'an',NULL,NULL),(350625,'长泰县',350600,'长泰',3,'0596','363900','中国,福建省,漳州市,长泰县',117.759,24.6253,'Changtai',NULL,NULL),(350626,'东山县',350600,'东山',3,'0596','363400','中国,福建省,漳州市,东山县',117.428,23.7011,'Dongshan',NULL,NULL),(350627,'南靖县',350600,'南靖',3,'0596','363600','中国,福建省,漳州市,南靖县',117.357,24.5145,'Nanjing',NULL,NULL),(350628,'平和县',350600,'平和',3,'0596','363700','中国,福建省,漳州市,平和县',117.312,24.364,'Pinghe',NULL,NULL),(350629,'华安县',350600,'华安',3,'0596','363800','中国,福建省,漳州市,华安县',117.541,25.0056,'Hua\'an',NULL,NULL),(350681,'龙海市',350600,'龙海',3,'0596','363100','中国,福建省,漳州市,龙海市',117.818,24.4466,'Longhai',NULL,NULL),(350700,'南平市',350000,'南平',2,'0599','353000','中国,福建省,南平市',118.178,26.6356,'Nanping',NULL,NULL),(350702,'延平区',350700,'延平',3,'0600','353000','中国,福建省,南平市,延平区',118.182,26.6374,'Yanping',NULL,NULL),(350703,'建阳区',350700,'建阳',3,'0599','354200','中国,福建省,南平市,建阳区',118.123,27.3321,'Jianyang',NULL,NULL),(350721,'顺昌县',350700,'顺昌',3,'0605','353200','中国,福建省,南平市,顺昌县',117.81,26.793,'Shunchang',NULL,NULL),(350722,'浦城县',350700,'浦城',3,'0606','353400','中国,福建省,南平市,浦城县',118.54,27.9189,'Pucheng',NULL,NULL),(350723,'光泽县',350700,'光泽',3,'0607','354100','中国,福建省,南平市,光泽县',117.333,27.5423,'Guangze',NULL,NULL),(350724,'松溪县',350700,'松溪',3,'0608','353500','中国,福建省,南平市,松溪县',118.785,27.5262,'Songxi',NULL,NULL),(350725,'政和县',350700,'政和',3,'0609','353600','中国,福建省,南平市,政和县',118.856,27.3677,'Zhenghe',NULL,NULL),(350781,'邵武市',350700,'邵武',3,'0601','354000','中国,福建省,南平市,邵武市',117.492,27.3403,'Shaowu',NULL,NULL),(350782,'武夷山市',350700,'武夷山',3,'0602','354300','中国,福建省,南平市,武夷山市',118.037,27.7554,'Wuyishan',NULL,NULL),(350783,'建瓯市',350700,'建瓯',3,'0603','353100','中国,福建省,南平市,建瓯市',118.298,27.023,'Jianou',NULL,NULL),(350800,'龙岩市',350000,'龙岩',2,'0597','364000','中国,福建省,龙岩市',117.03,25.0916,'Longyan',NULL,NULL),(350802,'新罗区',350800,'新罗',3,'0597','364000','中国,福建省,龙岩市,新罗区',117.037,25.0983,'Xinluo',NULL,NULL),(350821,'长汀县',350800,'长汀',3,'0597','366300','中国,福建省,龙岩市,长汀县',116.359,25.8277,'Changting',NULL,NULL),(350822,'永定区',350800,'永定',3,'0597','364100','中国,福建省,龙岩市,永定区',116.732,24.723,'Yongding',NULL,NULL),(350823,'上杭县',350800,'上杭',3,'0597','364200','中国,福建省,龙岩市,上杭县',116.42,25.0494,'Shanghang',NULL,NULL),(350824,'武平县',350800,'武平',3,'0597','364300','中国,福建省,龙岩市,武平县',116.102,25.0924,'Wuping',NULL,NULL),(350825,'连城县',350800,'连城',3,'0597','366200','中国,福建省,龙岩市,连城县',116.755,25.7103,'Liancheng',NULL,NULL),(350881,'漳平市',350800,'漳平',3,'0597','364400','中国,福建省,龙岩市,漳平市',117.42,25.2911,'Zhangping',NULL,NULL),(350900,'宁德市',350000,'宁德',2,'0593','352100','中国,福建省,宁德市',119.527,26.6592,'Ningde',NULL,NULL),(350902,'蕉城区',350900,'蕉城',3,'0593','352100','中国,福建省,宁德市,蕉城区',119.526,26.6605,'Jiaocheng',NULL,NULL),(350921,'霞浦县',350900,'霞浦',3,'0593','355100','中国,福建省,宁德市,霞浦县',119.999,26.8858,'Xiapu',NULL,NULL),(350922,'古田县',350900,'古田',3,'0593','352200','中国,福建省,宁德市,古田县',118.747,26.5768,'Gutian',NULL,NULL),(350923,'屏南县',350900,'屏南',3,'0593','352300','中国,福建省,宁德市,屏南县',118.989,26.911,'Pingnan',NULL,NULL),(350924,'寿宁县',350900,'寿宁',3,'0593','355500','中国,福建省,宁德市,寿宁县',119.504,27.46,'Shouning',NULL,NULL),(350925,'周宁县',350900,'周宁',3,'0593','355400','中国,福建省,宁德市,周宁县',119.338,27.1066,'Zhouning',NULL,NULL),(350926,'柘荣县',350900,'柘荣',3,'0593','355300','中国,福建省,宁德市,柘荣县',119.9,27.2354,'Zherong',NULL,NULL),(350981,'福安市',350900,'福安',3,'0593','355000','中国,福建省,宁德市,福安市',119.649,27.0867,'Fu\'an',NULL,NULL),(350982,'福鼎市',350900,'福鼎',3,'0593','355200','中国,福建省,宁德市,福鼎市',120.217,27.3243,'Fuding',NULL,NULL),(360000,'江西省',100000,'江西',1,'','','中国,江西省',115.892,28.6765,'Jiangxi',NULL,NULL),(360100,'南昌市',360000,'南昌',2,'0791','330008','中国,江西省,南昌市',115.892,28.6765,'Nanchang',NULL,NULL),(360102,'东湖区',360100,'东湖',3,'0791','330006','中国,江西省,南昌市,东湖区',115.899,28.685,'Donghu',NULL,NULL),(360103,'西湖区',360100,'西湖',3,'0791','330009','中国,江西省,南昌市,西湖区',115.877,28.6569,'Xihu',NULL,NULL),(360104,'青云谱区',360100,'青云谱',3,'0791','330001','中国,江西省,南昌市,青云谱区',115.915,28.632,'Qingyunpu',NULL,NULL),(360105,'湾里区',360100,'湾里',3,'0791','330004','中国,江西省,南昌市,湾里区',115.731,28.7153,'Wanli',NULL,NULL),(360111,'青山湖区',360100,'青山湖',3,'0791','330029','中国,江西省,南昌市,青山湖区',115.962,28.6821,'Qingshanhu',NULL,NULL),(360121,'南昌县',360100,'南昌',3,'0791','330200','中国,江西省,南昌市,南昌县',115.944,28.5456,'Nanchang',NULL,NULL),(360122,'新建县',360100,'新建',3,'0791','330100','中国,江西省,南昌市,新建县',115.815,28.6925,'Xinjian',NULL,NULL),(360123,'安义县',360100,'安义',3,'0791','330500','中国,江西省,南昌市,安义县',115.549,28.846,'Anyi',NULL,NULL),(360124,'进贤县',360100,'进贤',3,'0791','331700','中国,江西省,南昌市,进贤县',116.241,28.3768,'Jinxian',NULL,NULL),(360200,'景德镇市',360000,'景德镇',2,'0798','333000','中国,江西省,景德镇市',117.215,29.2926,'Jingdezhen',NULL,NULL),(360202,'昌江区',360200,'昌江',3,'0799','333000','中国,江西省,景德镇市,昌江区',117.184,29.2732,'Changjiang',NULL,NULL),(360203,'珠山区',360200,'珠山',3,'0800','333000','中国,江西省,景德镇市,珠山区',117.202,29.3013,'Zhushan',NULL,NULL),(360222,'浮梁县',360200,'浮梁',3,'0802','333400','中国,江西省,景德镇市,浮梁县',117.215,29.3516,'Fuliang',NULL,NULL),(360281,'乐平市',360200,'乐平',3,'0801','333300','中国,江西省,景德镇市,乐平市',117.129,28.9629,'Leping',NULL,NULL),(360300,'萍乡市',360000,'萍乡',2,'0799','337000','中国,江西省,萍乡市',113.852,27.6229,'Pingxiang',NULL,NULL),(360302,'安源区',360300,'安源',3,'0800','337000','中国,江西省,萍乡市,安源区',113.891,27.6165,'Anyuan',NULL,NULL),(360313,'湘东区',360300,'湘东',3,'0801','337016','中国,江西省,萍乡市,湘东区',113.733,27.6401,'Xiangdong',NULL,NULL),(360321,'莲花县',360300,'莲花',3,'0802','337100','中国,江西省,萍乡市,莲花县',113.961,27.1287,'Lianhua',NULL,NULL),(360322,'上栗县',360300,'上栗',3,'0803','337009','中国,江西省,萍乡市,上栗县',113.794,27.8747,'Shangli',NULL,NULL),(360323,'芦溪县',360300,'芦溪',3,'0804','337053','中国,江西省,萍乡市,芦溪县',114.03,27.6306,'Luxi',NULL,NULL),(360400,'九江市',360000,'九江',2,'0792','332000','中国,江西省,九江市',115.993,29.712,'Jiujiang',NULL,NULL),(360402,'庐山区',360400,'庐山',3,'0792','332005','中国,江西省,九江市,庐山区',115.989,29.6718,'Lushan',NULL,NULL),(360403,'浔阳区',360400,'浔阳',3,'0792','332000','中国,江西省,九江市,浔阳区',115.99,29.7279,'Xunyang',NULL,NULL),(360421,'九江县',360400,'九江',3,'0792','332100','中国,江西省,九江市,九江县',115.911,29.6085,'Jiujiang',NULL,NULL),(360423,'武宁县',360400,'武宁',3,'0792','332300','中国,江西省,九江市,武宁县',115.101,29.2584,'Wuning',NULL,NULL),(360424,'修水县',360400,'修水',3,'0792','332400','中国,江西省,九江市,修水县',114.547,29.0254,'Xiushui',NULL,NULL),(360425,'永修县',360400,'永修',3,'0792','330300','中国,江西省,九江市,永修县',115.809,29.0209,'Yongxiu',NULL,NULL),(360426,'德安县',360400,'德安',3,'0792','330400','中国,江西省,九江市,德安县',115.756,29.3134,'De\'an',NULL,NULL),(360427,'星子县',360400,'星子',3,'0792','332800','中国,江西省,九江市,星子县',116.045,29.4461,'Xingzi',NULL,NULL),(360428,'都昌县',360400,'都昌',3,'0792','332600','中国,江西省,九江市,都昌县',116.204,29.2733,'Duchang',NULL,NULL),(360429,'湖口县',360400,'湖口',3,'0792','332500','中国,江西省,九江市,湖口县',116.219,29.7382,'Hukou',NULL,NULL),(360430,'彭泽县',360400,'彭泽',3,'0792','332700','中国,江西省,九江市,彭泽县',116.55,29.8959,'Pengze',NULL,NULL),(360481,'瑞昌市',360400,'瑞昌',3,'0792','332200','中国,江西省,九江市,瑞昌市',115.667,29.6718,'Ruichang',NULL,NULL),(360482,'共青城市',360400,'共青城',3,'0792','332020','中国,江西省,九江市,共青城市',115.802,29.2388,'Gongqingcheng',NULL,NULL),(360500,'新余市',360000,'新余',2,'0790','338025','中国,江西省,新余市',114.931,27.8108,'Xinyu',NULL,NULL),(360502,'渝水区',360500,'渝水',3,'0790','338025','中国,江西省,新余市,渝水区',114.944,27.801,'Yushui',NULL,NULL),(360521,'分宜县',360500,'分宜',3,'0790','336600','中国,江西省,新余市,分宜县',114.692,27.8148,'Fenyi',NULL,NULL),(360600,'鹰潭市',360000,'鹰潭',2,'0701','335000','中国,江西省,鹰潭市',117.034,28.2386,'Yingtan',NULL,NULL),(360602,'月湖区',360600,'月湖',3,'0701','335000','中国,江西省,鹰潭市,月湖区',117.037,28.2391,'Yuehu',NULL,NULL),(360622,'余江县',360600,'余江',3,'0701','335200','中国,江西省,鹰潭市,余江县',116.819,28.2103,'Yujiang',NULL,NULL),(360681,'贵溪市',360600,'贵溪',3,'0701','335400','中国,江西省,鹰潭市,贵溪市',117.242,28.2926,'Guixi',NULL,NULL),(360700,'赣州市',360000,'赣州',2,'0797','341000','中国,江西省,赣州市',114.94,25.851,'Ganzhou',NULL,NULL),(360702,'章贡区',360700,'章贡',3,'0797','341000','中国,江西省,赣州市,章贡区',114.943,25.8624,'Zhanggong',NULL,NULL),(360703,'南康区',360700,'南康',3,'0797','341400','中国,江西省,赣州市,南康区',114.757,25.6617,'Nankang',NULL,NULL),(360721,'赣县',360700,'赣县',3,'0797','341100','中国,江西省,赣州市,赣县',115.012,25.8615,'Ganxian',NULL,NULL),(360722,'信丰县',360700,'信丰',3,'0797','341600','中国,江西省,赣州市,信丰县',114.923,25.3861,'Xinfeng',NULL,NULL),(360723,'大余县',360700,'大余',3,'0797','341500','中国,江西省,赣州市,大余县',114.358,25.3956,'Dayu',NULL,NULL),(360724,'上犹县',360700,'上犹',3,'0797','341200','中国,江西省,赣州市,上犹县',114.541,25.7957,'Shangyou',NULL,NULL),(360725,'崇义县',360700,'崇义',3,'0797','341300','中国,江西省,赣州市,崇义县',114.308,25.6819,'Chongyi',NULL,NULL),(360726,'安远县',360700,'安远',3,'0797','342100','中国,江西省,赣州市,安远县',115.395,25.1371,'Anyuan',NULL,NULL),(360727,'龙南县',360700,'龙南',3,'0797','341700','中国,江西省,赣州市,龙南县',114.79,24.9109,'Longnan',NULL,NULL),(360728,'定南县',360700,'定南',3,'0797','341900','中国,江西省,赣州市,定南县',115.027,24.784,'Dingnan',NULL,NULL),(360729,'全南县',360700,'全南',3,'0797','341800','中国,江西省,赣州市,全南县',114.529,24.7432,'Quannan',NULL,NULL),(360730,'宁都县',360700,'宁都',3,'0797','342800','中国,江西省,赣州市,宁都县',116.016,26.4723,'Ningdu',NULL,NULL),(360731,'于都县',360700,'于都',3,'0797','342300','中国,江西省,赣州市,于都县',115.414,25.9526,'Yudu',NULL,NULL),(360732,'兴国县',360700,'兴国',3,'0797','342400','中国,江西省,赣州市,兴国县',115.363,26.3378,'Xingguo',NULL,NULL),(360733,'会昌县',360700,'会昌',3,'0797','342600','中国,江西省,赣州市,会昌县',115.786,25.6007,'Huichang',NULL,NULL),(360734,'寻乌县',360700,'寻乌',3,'0797','342200','中国,江西省,赣州市,寻乌县',115.649,24.9551,'Xunwu',NULL,NULL),(360735,'石城县',360700,'石城',3,'0797','342700','中国,江西省,赣州市,石城县',116.344,26.3262,'Shicheng',NULL,NULL),(360781,'瑞金市',360700,'瑞金',3,'0797','342500','中国,江西省,赣州市,瑞金市',116.027,25.8856,'Ruijin',NULL,NULL),(360800,'吉安市',360000,'吉安',2,'0796','343000','中国,江西省,吉安市',114.986,27.1117,'Ji\'an',NULL,NULL),(360802,'吉州区',360800,'吉州',3,'0796','343000','中国,江西省,吉安市,吉州区',114.976,27.1067,'Jizhou',NULL,NULL),(360803,'青原区',360800,'青原',3,'0796','343009','中国,江西省,吉安市,青原区',115.017,27.1058,'Qingyuan',NULL,NULL),(360821,'吉安县',360800,'吉安',3,'0796','343100','中国,江西省,吉安市,吉安县',114.907,27.0405,'Ji\'an',NULL,NULL),(360822,'吉水县',360800,'吉水',3,'0796','331600','中国,江西省,吉安市,吉水县',115.134,27.2107,'Jishui',NULL,NULL),(360823,'峡江县',360800,'峡江',3,'0796','331409','中国,江西省,吉安市,峡江县',115.317,27.576,'Xiajiang',NULL,NULL),(360824,'新干县',360800,'新干',3,'0796','331300','中国,江西省,吉安市,新干县',115.393,27.7409,'Xingan',NULL,NULL),(360825,'永丰县',360800,'永丰',3,'0796','331500','中国,江西省,吉安市,永丰县',115.442,27.3179,'Yongfeng',NULL,NULL),(360826,'泰和县',360800,'泰和',3,'0796','343700','中国,江西省,吉安市,泰和县',114.908,26.7911,'Taihe',NULL,NULL),(360827,'遂川县',360800,'遂川',3,'0796','343900','中国,江西省,吉安市,遂川县',114.516,26.326,'Suichuan',NULL,NULL),(360828,'万安县',360800,'万安',3,'0796','343800','中国,江西省,吉安市,万安县',114.787,26.4593,'Wan\'an',NULL,NULL),(360829,'安福县',360800,'安福',3,'0796','343200','中国,江西省,吉安市,安福县',114.62,27.3928,'Anfu',NULL,NULL),(360830,'永新县',360800,'永新',3,'0796','343400','中国,江西省,吉安市,永新县',114.242,26.9449,'Yongxin',NULL,NULL),(360881,'井冈山市',360800,'井冈山',3,'0796','343600','中国,江西省,吉安市,井冈山市',114.289,26.748,'Jinggangshan',NULL,NULL),(360900,'宜春市',360000,'宜春',2,'0795','336000','中国,江西省,宜春市',114.391,27.8043,'Yichun',NULL,NULL),(360902,'袁州区',360900,'袁州',3,'0795','336000','中国,江西省,宜春市,袁州区',114.382,27.7965,'Yuanzhou',NULL,NULL),(360921,'奉新县',360900,'奉新',3,'0795','330700','中国,江西省,宜春市,奉新县',115.4,28.6879,'Fengxin',NULL,NULL),(360922,'万载县',360900,'万载',3,'0795','336100','中国,江西省,宜春市,万载县',114.446,28.1066,'Wanzai',NULL,NULL),(360923,'上高县',360900,'上高',3,'0795','336400','中国,江西省,宜春市,上高县',114.925,28.2342,'Shanggao',NULL,NULL),(360924,'宜丰县',360900,'宜丰',3,'0795','336300','中国,江西省,宜春市,宜丰县',114.78,28.3855,'Yifeng',NULL,NULL),(360925,'靖安县',360900,'靖安',3,'0795','330600','中国,江西省,宜春市,靖安县',115.363,28.8617,'Jing\'an',NULL,NULL),(360926,'铜鼓县',360900,'铜鼓',3,'0795','336200','中国,江西省,宜春市,铜鼓县',114.37,28.5231,'Tonggu',NULL,NULL),(360981,'丰城市',360900,'丰城',3,'0795','331100','中国,江西省,宜春市,丰城市',115.771,28.1592,'Fengcheng',NULL,NULL),(360982,'樟树市',360900,'樟树',3,'0795','331200','中国,江西省,宜春市,樟树市',115.547,28.0533,'Zhangshu',NULL,NULL),(360983,'高安市',360900,'高安',3,'0795','330800','中国,江西省,宜春市,高安市',115.375,28.4178,'Gao\'an',NULL,NULL),(361000,'抚州市',360000,'抚州',2,'0794','344000','中国,江西省,抚州市',116.358,27.9839,'Fuzhou',NULL,NULL),(361002,'临川区',361000,'临川',3,'0794','344000','中国,江西省,抚州市,临川区',116.359,27.9772,'Linchuan',NULL,NULL),(361021,'南城县',361000,'南城',3,'0794','344700','中国,江西省,抚州市,南城县',116.644,27.5538,'Nancheng',NULL,NULL),(361022,'黎川县',361000,'黎川',3,'0794','344600','中国,江西省,抚州市,黎川县',116.908,27.2823,'Lichuan',NULL,NULL),(361023,'南丰县',361000,'南丰',3,'0794','344500','中国,江西省,抚州市,南丰县',116.526,27.2184,'Nanfeng',NULL,NULL),(361024,'崇仁县',361000,'崇仁',3,'0794','344200','中国,江西省,抚州市,崇仁县',116.06,27.7596,'Chongren',NULL,NULL),(361025,'乐安县',361000,'乐安',3,'0794','344300','中国,江西省,抚州市,乐安县',115.831,27.4281,'Le\'an',NULL,NULL),(361026,'宜黄县',361000,'宜黄',3,'0794','344400','中国,江西省,抚州市,宜黄县',116.236,27.5549,'Yihuang',NULL,NULL),(361027,'金溪县',361000,'金溪',3,'0794','344800','中国,江西省,抚州市,金溪县',116.774,27.9075,'Jinxi',NULL,NULL),(361028,'资溪县',361000,'资溪',3,'0794','335300','中国,江西省,抚州市,资溪县',117.069,27.7049,'Zixi',NULL,NULL),(361029,'东乡县',361000,'东乡',3,'0794','331800','中国,江西省,抚州市,东乡县',116.59,28.2361,'Dongxiang',NULL,NULL),(361030,'广昌县',361000,'广昌',3,'0794','344900','中国,江西省,抚州市,广昌县',116.325,26.8341,'Guangchang',NULL,NULL),(361100,'上饶市',360000,'上饶',2,'0793','334000','中国,江西省,上饶市',117.971,28.4444,'Shangrao',NULL,NULL),(361102,'信州区',361100,'信州',3,'0793','334000','中国,江西省,上饶市,信州区',117.967,28.4312,'Xinzhou',NULL,NULL),(361121,'上饶县',361100,'上饶',3,'0793','334100','中国,江西省,上饶市,上饶县',117.909,28.4486,'Shangrao',NULL,NULL),(361122,'广丰县',361100,'广丰',3,'0793','334600','中国,江西省,上饶市,广丰县',118.192,28.4377,'Guangfeng',NULL,NULL),(361123,'玉山县',361100,'玉山',3,'0793','334700','中国,江西省,上饶市,玉山县',118.245,28.6818,'Yushan',NULL,NULL),(361124,'铅山县',361100,'铅山',3,'0793','334500','中国,江西省,上饶市,铅山县',117.71,28.3155,'Yanshan',NULL,NULL),(361125,'横峰县',361100,'横峰',3,'0793','334300','中国,江西省,上饶市,横峰县',117.596,28.4072,'Hengfeng',NULL,NULL),(361126,'弋阳县',361100,'弋阳',3,'0793','334400','中国,江西省,上饶市,弋阳县',117.459,28.3745,'Yiyang',NULL,NULL),(361127,'余干县',361100,'余干',3,'0793','335100','中国,江西省,上饶市,余干县',116.696,28.7021,'Yugan',NULL,NULL),(361128,'鄱阳县',361100,'鄱阳',3,'0793','333100','中国,江西省,上饶市,鄱阳县',116.7,29.0118,'Poyang',NULL,NULL),(361129,'万年县',361100,'万年',3,'0793','335500','中国,江西省,上饶市,万年县',117.069,28.6954,'Wannian',NULL,NULL),(361130,'婺源县',361100,'婺源',3,'0793','333200','中国,江西省,上饶市,婺源县',117.861,29.2484,'Wuyuan',NULL,NULL),(361181,'德兴市',361100,'德兴',3,'0793','334200','中国,江西省,上饶市,德兴市',117.579,28.9474,'Dexing',NULL,NULL),(370000,'山东省',100000,'山东',1,'','','中国,山东省',117.001,36.6758,'Shandong',NULL,NULL),(370100,'济南市',370000,'济南',2,'0531','250001','中国,山东省,济南市',117.001,36.6758,'Jinan',NULL,NULL),(370102,'历下区',370100,'历下',3,'0531','250014','中国,山东省,济南市,历下区',117.077,36.6666,'Lixia',NULL,NULL),(370103,'市中区',370100,'市中区',3,'0531','250001','中国,山东省,济南市,市中区',116.997,36.651,'Shizhongqu',NULL,NULL),(370104,'槐荫区',370100,'槐荫',3,'0531','250117','中国,山东省,济南市,槐荫区',116.901,36.6514,'Huaiyin',NULL,NULL),(370105,'天桥区',370100,'天桥',3,'0531','250031','中国,山东省,济南市,天桥区',116.987,36.678,'Tianqiao',NULL,NULL),(370112,'历城区',370100,'历城',3,'0531','250100','中国,山东省,济南市,历城区',117.065,36.68,'Licheng',NULL,NULL),(370113,'长清区',370100,'长清',3,'0531','250300','中国,山东省,济南市,长清区',116.752,36.5535,'Changqing',NULL,NULL),(370116,'莱芜区',370100,'莱芜',3,'0634','271100','中国,山东省,济南市,莱芜市',117.678,36.2144,'Laiwu',NULL,NULL),(370124,'平阴县',370100,'平阴',3,'0531','250400','中国,山东省,济南市,平阴县',116.456,36.2896,'Pingyin',NULL,NULL),(370125,'济阳县',370100,'济阳',3,'0531','251400','中国,山东省,济南市,济阳县',117.173,36.9785,'Jiyang',NULL,NULL),(370126,'商河县',370100,'商河',3,'0531','251600','中国,山东省,济南市,商河县',117.157,37.3112,'Shanghe',NULL,NULL),(370181,'章丘市',370100,'章丘',3,'0531','250200','中国,山东省,济南市,章丘市',117.537,36.7139,'Zhangqiu',NULL,NULL),(370200,'青岛市',370000,'青岛',2,'0532','266001','中国,山东省,青岛市',120.37,36.0944,'Qingdao',NULL,NULL),(370202,'市南区',370200,'市南',3,'0532','266001','中国,山东省,青岛市,市南区',120.388,36.0667,'Shinan',NULL,NULL),(370203,'市北区',370200,'市北',3,'0532','266011','中国,山东省,青岛市,市北区',120.375,36.0873,'Shibei',NULL,NULL),(370211,'黄岛区',370200,'黄岛',3,'0532','266500','中国,山东省,青岛市,黄岛区',120.198,35.9607,'Huangdao',NULL,NULL),(370212,'崂山区',370200,'崂山',3,'0532','266100','中国,山东省,青岛市,崂山区',120.469,36.1072,'Laoshan',NULL,NULL),(370213,'李沧区',370200,'李沧',3,'0532','266021','中国,山东省,青岛市,李沧区',120.433,36.145,'Licang',NULL,NULL),(370214,'城阳区',370200,'城阳',3,'0532','266041','中国,山东省,青岛市,城阳区',120.396,36.3074,'Chengyang',NULL,NULL),(370281,'胶州市',370200,'胶州',3,'0532','266300','中国,山东省,青岛市,胶州市',120.034,36.2644,'Jiaozhou',NULL,NULL),(370282,'即墨市',370200,'即墨',3,'0532','266200','中国,山东省,青岛市,即墨市',120.447,36.3891,'Jimo',NULL,NULL),(370283,'平度市',370200,'平度',3,'0532','266700','中国,山东省,青岛市,平度市',119.96,36.7869,'Pingdu',NULL,NULL),(370285,'莱西市',370200,'莱西',3,'0532','266600','中国,山东省,青岛市,莱西市',120.518,36.888,'Laixi',NULL,NULL),(370286,'西海岸新区',370200,'西海岸',3,'0532','266500','中国,山东省,青岛市,西海岸新区',120.198,35.9607,'Xihai\'an',NULL,NULL),(370300,'淄博市',370000,'淄博',2,'0533','255039','中国,山东省,淄博市',118.048,36.8149,'Zibo',NULL,NULL),(370302,'淄川区',370300,'淄川',3,'0533','255100','中国,山东省,淄博市,淄川区',117.967,36.6434,'Zichuan',NULL,NULL),(370303,'张店区',370300,'张店',3,'0533','255022','中国,山东省,淄博市,张店区',118.018,36.8068,'Zhangdian',NULL,NULL),(370304,'博山区',370300,'博山',3,'0533','255200','中国,山东省,淄博市,博山区',117.862,36.4947,'Boshan',NULL,NULL),(370305,'临淄区',370300,'临淄',3,'0533','255400','中国,山东省,淄博市,临淄区',118.31,36.8259,'Linzi',NULL,NULL),(370306,'周村区',370300,'周村',3,'0533','255300','中国,山东省,淄博市,周村区',117.87,36.8032,'Zhoucun',NULL,NULL),(370321,'桓台县',370300,'桓台',3,'0533','256400','中国,山东省,淄博市,桓台县',118.097,36.9604,'Huantai',NULL,NULL),(370322,'高青县',370300,'高青',3,'0533','256300','中国,山东省,淄博市,高青县',117.827,37.172,'Gaoqing',NULL,NULL),(370323,'沂源县',370300,'沂源',3,'0533','256100','中国,山东省,淄博市,沂源县',118.171,36.1854,'Yiyuan',NULL,NULL),(370400,'枣庄市',370000,'枣庄',2,'0632','277101','中国,山东省,枣庄市',117.558,34.8564,'Zaozhuang',NULL,NULL),(370402,'市中区',370400,'市中区',3,'0632','277101','中国,山东省,枣庄市,市中区',117.556,34.8639,'Shizhongqu',NULL,NULL),(370403,'薛城区',370400,'薛城',3,'0632','277000','中国,山东省,枣庄市,薛城区',117.263,34.795,'Xuecheng',NULL,NULL),(370404,'峄城区',370400,'峄城',3,'0632','277300','中国,山东省,枣庄市,峄城区',117.591,34.7723,'Yicheng',NULL,NULL),(370405,'台儿庄区',370400,'台儿庄',3,'0632','277400','中国,山东省,枣庄市,台儿庄区',117.735,34.5636,'Taierzhuang',NULL,NULL),(370406,'山亭区',370400,'山亭',3,'0632','277200','中国,山东省,枣庄市,山亭区',117.466,35.0954,'Shanting',NULL,NULL),(370481,'滕州市',370400,'滕州',3,'0632','277500','中国,山东省,枣庄市,滕州市',117.165,35.1053,'Tengzhou',NULL,NULL),(370500,'东营市',370000,'东营',2,'0546','257093','中国,山东省,东营市',118.496,37.4613,'Dongying',NULL,NULL),(370502,'东营区',370500,'东营',3,'0546','257029','中国,山东省,东营市,东营区',118.582,37.4487,'Dongying',NULL,NULL),(370503,'河口区',370500,'河口',3,'0546','257200','中国,山东省,东营市,河口区',118.525,37.8854,'Hekou',NULL,NULL),(370521,'垦利县',370500,'垦利',3,'0546','257500','中国,山东省,东营市,垦利县',118.548,37.5882,'Kenli',NULL,NULL),(370522,'利津县',370500,'利津',3,'0546','257400','中国,山东省,东营市,利津县',118.256,37.4916,'Lijin',NULL,NULL),(370523,'广饶县',370500,'广饶',3,'0546','257300','中国,山东省,东营市,广饶县',118.407,37.0538,'Guangrao',NULL,NULL),(370600,'烟台市',370000,'烟台',2,'0635','264010','中国,山东省,烟台市',121.391,37.5393,'Yantai',NULL,NULL),(370602,'芝罘区',370600,'芝罘',3,'0635','264001','中国,山东省,烟台市,芝罘区',121.4,37.5406,'Zhifu',NULL,NULL),(370611,'福山区',370600,'福山',3,'0635','265500','中国,山东省,烟台市,福山区',121.268,37.4984,'Fushan',NULL,NULL),(370612,'牟平区',370600,'牟平',3,'0635','264100','中国,山东省,烟台市,牟平区',121.601,37.3885,'Muping',NULL,NULL),(370613,'莱山区',370600,'莱山',3,'0635','264600','中国,山东省,烟台市,莱山区',121.445,37.5117,'Laishan',NULL,NULL),(370634,'长岛县',370600,'长岛',3,'0635','265800','中国,山东省,烟台市,长岛县',120.738,37.9175,'Changdao',NULL,NULL),(370681,'龙口市',370600,'龙口',3,'0635','265700','中国,山东省,烟台市,龙口市',120.506,37.6406,'Longkou',NULL,NULL),(370682,'莱阳市',370600,'莱阳',3,'0635','265200','中国,山东省,烟台市,莱阳市',120.711,36.9801,'Laiyang',NULL,NULL),(370683,'莱州市',370600,'莱州',3,'0635','261400','中国,山东省,烟台市,莱州市',119.941,37.1781,'Laizhou',NULL,NULL),(370684,'蓬莱市',370600,'蓬莱',3,'0635','265600','中国,山东省,烟台市,蓬莱市',120.76,37.8112,'Penglai',NULL,NULL),(370685,'招远市',370600,'招远',3,'0635','265400','中国,山东省,烟台市,招远市',120.405,37.3627,'Zhaoyuan',NULL,NULL),(370686,'栖霞市',370600,'栖霞',3,'0635','265300','中国,山东省,烟台市,栖霞市',120.85,37.3357,'Qixia',NULL,NULL),(370687,'海阳市',370600,'海阳',3,'0635','265100','中国,山东省,烟台市,海阳市',121.16,36.7762,'Haiyang',NULL,NULL),(370700,'潍坊市',370000,'潍坊',2,'0536','261041','中国,山东省,潍坊市',119.107,36.7093,'Weifang',NULL,NULL),(370702,'潍城区',370700,'潍城',3,'0536','261021','中国,山东省,潍坊市,潍城区',119.106,36.7139,'Weicheng',NULL,NULL),(370703,'寒亭区',370700,'寒亭',3,'0536','261100','中国,山东省,潍坊市,寒亭区',119.218,36.775,'Hanting',NULL,NULL),(370704,'坊子区',370700,'坊子',3,'0536','261200','中国,山东省,潍坊市,坊子区',119.165,36.6522,'Fangzi',NULL,NULL),(370705,'奎文区',370700,'奎文',3,'0536','261031','中国,山东省,潍坊市,奎文区',119.125,36.7072,'Kuiwen',NULL,NULL),(370724,'临朐县',370700,'临朐',3,'0536','262600','中国,山东省,潍坊市,临朐县',118.544,36.5122,'Linqu',NULL,NULL),(370725,'昌乐县',370700,'昌乐',3,'0536','262400','中国,山东省,潍坊市,昌乐县',118.83,36.7078,'Changle',NULL,NULL),(370781,'青州市',370700,'青州',3,'0536','262500','中国,山东省,潍坊市,青州市',118.479,36.6851,'Qingzhou',NULL,NULL),(370782,'诸城市',370700,'诸城',3,'0536','262200','中国,山东省,潍坊市,诸城市',119.41,35.9966,'Zhucheng',NULL,NULL),(370783,'寿光市',370700,'寿光',3,'0536','262700','中国,山东省,潍坊市,寿光市',118.74,36.8813,'Shouguang',NULL,NULL),(370784,'安丘市',370700,'安丘',3,'0536','262100','中国,山东省,潍坊市,安丘市',119.219,36.4785,'Anqiu',NULL,NULL),(370785,'高密市',370700,'高密',3,'0536','261500','中国,山东省,潍坊市,高密市',119.757,36.384,'Gaomi',NULL,NULL),(370786,'昌邑市',370700,'昌邑',3,'0536','261300','中国,山东省,潍坊市,昌邑市',119.398,36.8601,'Changyi',NULL,NULL),(370800,'济宁市',370000,'济宁',2,'0537','272119','中国,山东省,济宁市',116.587,35.4154,'Jining',NULL,NULL),(370811,'任城区',370800,'任城',3,'0537','272113','中国,山东省,济宁市,任城区',116.595,35.4066,'Rencheng',NULL,NULL),(370812,'兖州区',370800,'兖州',3,'0537','272000','中国,山东省,济宁市,兖州区',116.827,35.5523,'Yanzhou ',NULL,NULL),(370826,'微山县',370800,'微山',3,'0537','277600','中国,山东省,济宁市,微山县',117.129,34.8071,'Weishan',NULL,NULL),(370827,'鱼台县',370800,'鱼台',3,'0537','272300','中国,山东省,济宁市,鱼台县',116.648,34.9967,'Yutai',NULL,NULL),(370828,'金乡县',370800,'金乡',3,'0537','272200','中国,山东省,济宁市,金乡县',116.311,35.065,'Jinxiang',NULL,NULL),(370829,'嘉祥县',370800,'嘉祥',3,'0537','272400','中国,山东省,济宁市,嘉祥县',116.342,35.4084,'Jiaxiang',NULL,NULL),(370830,'汶上县',370800,'汶上',3,'0537','272501','中国,山东省,济宁市,汶上县',116.487,35.7329,'Wenshang',NULL,NULL),(370831,'泗水县',370800,'泗水',3,'0537','273200','中国,山东省,济宁市,泗水县',117.279,35.6611,'Sishui',NULL,NULL),(370832,'梁山县',370800,'梁山',3,'0537','272600','中国,山东省,济宁市,梁山县',116.097,35.8032,'Liangshan',NULL,NULL),(370881,'曲阜市',370800,'曲阜',3,'0537','273100','中国,山东省,济宁市,曲阜市',116.986,35.5809,'Qufu',NULL,NULL),(370883,'邹城市',370800,'邹城',3,'0537','273500','中国,山东省,济宁市,邹城市',116.973,35.4053,'Zoucheng',NULL,NULL),(370900,'泰安市',370000,'泰安',2,'0538','271000','中国,山东省,泰安市',117.129,36.195,'Tai\'an',NULL,NULL),(370902,'泰山区',370900,'泰山',3,'0538','271000','中国,山东省,泰安市,泰山区',117.134,36.1941,'Taishan',NULL,NULL),(370911,'岱岳区',370900,'岱岳',3,'0538','271000','中国,山东省,泰安市,岱岳区',117.042,36.1875,'Daiyue',NULL,NULL),(370921,'宁阳县',370900,'宁阳',3,'0538','271400','中国,山东省,泰安市,宁阳县',116.805,35.7599,'Ningyang',NULL,NULL),(370923,'东平县',370900,'东平',3,'0538','271500','中国,山东省,泰安市,东平县',116.471,35.9379,'Dongping',NULL,NULL),(370982,'新泰市',370900,'新泰',3,'0538','271200','中国,山东省,泰安市,新泰市',117.77,35.9089,'Xintai',NULL,NULL),(370983,'肥城市',370900,'肥城',3,'0538','271600','中国,山东省,泰安市,肥城市',116.768,36.1825,'Feicheng',NULL,NULL),(371000,'威海市',370000,'威海',2,'0631','264200','中国,山东省,威海市',122.116,37.5097,'Weihai',NULL,NULL),(371002,'环翠区',371000,'环翠',3,'0631','264200','中国,山东省,威海市,环翠区',122.123,37.502,'Huancui',NULL,NULL),(371003,'文登区',371000,'文登',3,'0631','266440','中国,山东省,威海市,文登区',122.057,37.1962,'Wendeng',NULL,NULL),(371082,'荣成市',371000,'荣成',3,'0631','264300','中国,山东省,威海市,荣成市',122.488,37.1652,'Rongcheng',NULL,NULL),(371083,'乳山市',371000,'乳山',3,'0631','264500','中国,山东省,威海市,乳山市',121.538,36.9192,'Rushan',NULL,NULL),(371100,'日照市',370000,'日照',2,'0633','276800','中国,山东省,日照市',119.461,35.4286,'Rizhao',NULL,NULL),(371102,'东港区',371100,'东港',3,'0633','276800','中国,山东省,日照市,东港区',119.462,35.4254,'Donggang',NULL,NULL),(371103,'岚山区',371100,'岚山',3,'0633','276808','中国,山东省,日照市,岚山区',119.319,35.122,'Lanshan',NULL,NULL),(371121,'五莲县',371100,'五莲',3,'0633','262300','中国,山东省,日照市,五莲县',119.207,35.75,'Wulian',NULL,NULL),(371122,'莒县',371100,'莒县',3,'0633','276500','中国,山东省,日照市,莒县',118.838,35.5805,'Juxian',NULL,NULL),(371202,'莱城区',371200,'莱城',3,'0634','271199','中国,山东省,莱芜市,莱城区',117.66,36.2032,'Laicheng',NULL,NULL),(371203,'钢城区',371200,'钢城',3,'0634','271100','中国,山东省,莱芜市,钢城区',117.805,36.0632,'Gangcheng',NULL,NULL),(371300,'临沂市',370000,'临沂',2,'0539','253000','中国,山东省,临沂市',118.326,35.0653,'Linyi',NULL,NULL),(371302,'兰山区',371300,'兰山',3,'0539','276002','中国,山东省,临沂市,兰山区',118.348,35.0687,'Lanshan',NULL,NULL),(371311,'罗庄区',371300,'罗庄',3,'0539','276022','中国,山东省,临沂市,罗庄区',118.285,34.9963,'Luozhuang',NULL,NULL),(371312,'河东区',371300,'河东',3,'0539','276034','中国,山东省,临沂市,河东区',118.411,35.088,'Hedong',NULL,NULL),(371321,'沂南县',371300,'沂南',3,'0539','276300','中国,山东省,临沂市,沂南县',118.471,35.5513,'Yinan',NULL,NULL),(371322,'郯城县',371300,'郯城',3,'0539','276100','中国,山东省,临沂市,郯城县',118.367,34.6135,'Tancheng',NULL,NULL),(371323,'沂水县',371300,'沂水',3,'0539','276400','中国,山东省,临沂市,沂水县',118.63,35.7873,'Yishui',NULL,NULL),(371324,'兰陵县',371300,'兰陵',3,'0539','277700','中国,山东省,临沂市,兰陵县',117.857,34.7383,'Lanling',NULL,NULL),(371325,'费县',371300,'费县',3,'0539','273400','中国,山东省,临沂市,费县',117.978,35.2656,'Feixian',NULL,NULL),(371326,'平邑县',371300,'平邑',3,'0539','273300','中国,山东省,临沂市,平邑县',117.639,35.5057,'Pingyi',NULL,NULL),(371327,'莒南县',371300,'莒南',3,'0539','276600','中国,山东省,临沂市,莒南县',118.832,35.1754,'Junan',NULL,NULL),(371328,'蒙阴县',371300,'蒙阴',3,'0539','276200','中国,山东省,临沂市,蒙阴县',117.946,35.71,'Mengyin',NULL,NULL),(371329,'临沭县',371300,'临沭',3,'0539','276700','中国,山东省,临沂市,临沭县',118.653,34.9209,'Linshu',NULL,NULL),(371400,'德州市',370000,'德州',2,'0534','253000','中国,山东省,德州市',116.307,37.454,'Dezhou',NULL,NULL),(371402,'德城区',371400,'德城',3,'0534','253012','中国,山东省,德州市,德城区',116.299,37.4513,'Decheng',NULL,NULL),(371403,'陵城区',371400,'陵城',3,'0534','253500','中国,山东省,德州市,陵城区',116.576,37.3357,'Lingcheng',NULL,NULL),(371422,'宁津县',371400,'宁津',3,'0534','253400','中国,山东省,德州市,宁津县',116.797,37.653,'Ningjin',NULL,NULL),(371423,'庆云县',371400,'庆云',3,'0534','253700','中国,山东省,德州市,庆云县',117.386,37.7762,'Qingyun',NULL,NULL),(371424,'临邑县',371400,'临邑',3,'0534','251500','中国,山东省,德州市,临邑县',116.865,37.1905,'Linyi',NULL,NULL),(371425,'齐河县',371400,'齐河',3,'0534','251100','中国,山东省,德州市,齐河县',116.755,36.7953,'Qihe',NULL,NULL),(371426,'平原县',371400,'平原',3,'0534','253100','中国,山东省,德州市,平原县',116.434,37.1663,'Pingyuan',NULL,NULL),(371427,'夏津县',371400,'夏津',3,'0534','253200','中国,山东省,德州市,夏津县',116.002,36.9485,'Xiajin',NULL,NULL),(371428,'武城县',371400,'武城',3,'0534','253300','中国,山东省,德州市,武城县',116.07,37.214,'Wucheng',NULL,NULL),(371481,'乐陵市',371400,'乐陵',3,'0534','253600','中国,山东省,德州市,乐陵市',117.231,37.7316,'Leling',NULL,NULL),(371482,'禹城市',371400,'禹城',3,'0534','251200','中国,山东省,德州市,禹城市',116.643,36.9344,'Yucheng',NULL,NULL),(371500,'聊城市',370000,'聊城',2,'0635','252052','中国,山东省,聊城市',115.98,36.456,'Liaocheng',NULL,NULL),(371502,'东昌府区',371500,'东昌府',3,'0635','252000','中国,山东省,聊城市,东昌府区',115.974,36.4446,'Dongchangfu',NULL,NULL),(371521,'阳谷县',371500,'阳谷',3,'0635','252300','中国,山东省,聊城市,阳谷县',115.791,36.1144,'Yanggu',NULL,NULL),(371522,'莘县',371500,'莘县',3,'0635','252400','中国,山东省,聊城市,莘县',115.67,36.2342,'Shenxian',NULL,NULL),(371523,'茌平县',371500,'茌平',3,'0635','252100','中国,山东省,聊城市,茌平县',116.255,36.5797,'Chiping',NULL,NULL),(371524,'东阿县',371500,'东阿',3,'0635','252200','中国,山东省,聊城市,东阿县',116.25,36.3321,'Dong\'e',NULL,NULL),(371525,'冠县',371500,'冠县',3,'0635','252500','中国,山东省,聊城市,冠县',115.442,36.4843,'Guanxian',NULL,NULL),(371526,'高唐县',371500,'高唐',3,'0635','252800','中国,山东省,聊城市,高唐县',116.232,36.8653,'Gaotang',NULL,NULL),(371581,'临清市',371500,'临清',3,'0635','252600','中国,山东省,聊城市,临清市',115.706,36.8395,'Linqing',NULL,NULL),(371600,'滨州市',370000,'滨州',2,'0543','256619','中国,山东省,滨州市',118.017,37.3835,'Binzhou',NULL,NULL),(371602,'滨城区',371600,'滨城',3,'0543','256613','中国,山东省,滨州市,滨城区',118.02,37.3852,'Bincheng',NULL,NULL),(371603,'沾化区',371600,'沾化',3,'0543','256800','中国,山东省,滨州市,沾化区',118.132,37.6983,'Zhanhua',NULL,NULL),(371621,'惠民县',371600,'惠民',3,'0543','251700','中国,山东省,滨州市,惠民县',117.511,37.4901,'Huimin',NULL,NULL),(371622,'阳信县',371600,'阳信',3,'0543','251800','中国,山东省,滨州市,阳信县',117.581,37.642,'Yangxin',NULL,NULL),(371623,'无棣县',371600,'无棣',3,'0543','251900','中国,山东省,滨州市,无棣县',117.614,37.7401,'Wudi',NULL,NULL),(371625,'博兴县',371600,'博兴',3,'0543','256500','中国,山东省,滨州市,博兴县',118.134,37.1432,'Boxing',NULL,NULL),(371626,'邹平县',371600,'邹平',3,'0543','256200','中国,山东省,滨州市,邹平县',117.743,36.8629,'Zouping',NULL,NULL),(371627,'北海新区',371600,'北海新区',3,'0543','256200','中国,山东省,滨州市,北海新区',118.017,37.3835,'Beihaixinqu',NULL,NULL),(371700,'菏泽市',370000,'菏泽',2,'0530','274020','中国,山东省,菏泽市',115.469,35.2465,'Heze',NULL,NULL),(371702,'牡丹区',371700,'牡丹',3,'0530','274009','中国,山东省,菏泽市,牡丹区',115.417,35.2509,'Mudan',NULL,NULL),(371721,'曹县',371700,'曹县',3,'0530','274400','中国,山东省,菏泽市,曹县',115.542,34.8266,'Caoxian',NULL,NULL),(371722,'单县',371700,'单县',3,'0530','273700','中国,山东省,菏泽市,单县',116.087,34.7951,'Shanxian',NULL,NULL),(371723,'成武县',371700,'成武',3,'0530','274200','中国,山东省,菏泽市,成武县',115.89,34.9533,'Chengwu',NULL,NULL),(371724,'巨野县',371700,'巨野',3,'0530','274900','中国,山东省,菏泽市,巨野县',116.095,35.3979,'Juye',NULL,NULL),(371725,'郓城县',371700,'郓城',3,'0530','274700','中国,山东省,菏泽市,郓城县',115.944,35.6004,'Yuncheng',NULL,NULL),(371726,'鄄城县',371700,'鄄城',3,'0530','274600','中国,山东省,菏泽市,鄄城县',115.51,35.5641,'Juancheng',NULL,NULL),(371727,'定陶县',371700,'定陶',3,'0530','274100','中国,山东省,菏泽市,定陶县',115.573,35.0712,'Dingtao',NULL,NULL),(371728,'东明县',371700,'东明',3,'0530','274500','中国,山东省,菏泽市,东明县',115.091,35.2891,'Dongming',NULL,NULL),(410000,'河南省',100000,'河南',1,'','','中国,河南省',113.665,34.758,'Henan',NULL,NULL),(410100,'郑州市',410000,'郑州',2,'0371','450000','中国,河南省,郑州市',113.665,34.758,'Zhengzhou',NULL,NULL),(410102,'中原区',410100,'中原',3,'0371','450007','中国,河南省,郑州市,中原区',113.613,34.7483,'Zhongyuan',NULL,NULL),(410103,'二七区',410100,'二七',3,'0371','450052','中国,河南省,郑州市,二七区',113.639,34.7234,'Erqi',NULL,NULL),(410104,'管城回族区',410100,'管城',3,'0371','450000','中国,河南省,郑州市,管城回族区',113.677,34.7538,'Guancheng',NULL,NULL),(410105,'金水区',410100,'金水',3,'0371','450003','中国,河南省,郑州市,金水区',113.661,34.8003,'Jinshui',NULL,NULL),(410106,'上街区',410100,'上街',3,'0371','450041','中国,河南省,郑州市,上街区',113.309,34.8028,'Shangjie',NULL,NULL),(410108,'惠济区',410100,'惠济',3,'0371','450053','中国,河南省,郑州市,惠济区',113.617,34.8674,'Huiji',NULL,NULL),(410122,'中牟县',410100,'中牟',3,'0371','451450','中国,河南省,郑州市,中牟县',113.976,34.719,'Zhongmu',NULL,NULL),(410181,'巩义市',410100,'巩义',3,'0371','451200','中国,河南省,郑州市,巩义市',113.022,34.7479,'Gongyi',NULL,NULL),(410182,'荥阳市',410100,'荥阳',3,'0371','450100','中国,河南省,郑州市,荥阳市',113.383,34.7876,'Xingyang',NULL,NULL),(410183,'新密市',410100,'新密',3,'0371','452300','中国,河南省,郑州市,新密市',113.387,34.537,'Xinmi',NULL,NULL),(410184,'新郑市',410100,'新郑',3,'0371','451100','中国,河南省,郑州市,新郑市',113.736,34.3955,'Xinzheng',NULL,NULL),(410185,'登封市',410100,'登封',3,'0371','452470','中国,河南省,郑州市,登封市',113.05,34.4534,'Dengfeng',NULL,NULL),(410200,'开封市',410000,'开封',2,'0378','475001','中国,河南省,开封市',114.341,34.7971,'Kaifeng',NULL,NULL),(410202,'龙亭区',410200,'龙亭',3,'0378','475100','中国,河南省,开封市,龙亭区',114.355,34.7999,'Longting',NULL,NULL),(410203,'顺河回族区',410200,'顺河',3,'0378','475000','中国,河南省,开封市,顺河回族区',114.361,34.7959,'Shunhe',NULL,NULL),(410204,'鼓楼区',410200,'鼓楼',3,'0378','475000','中国,河南省,开封市,鼓楼区',114.356,34.7952,'Gulou',NULL,NULL),(410205,'禹王台区',410200,'禹王台',3,'0378','475003','中国,河南省,开封市,禹王台区',114.348,34.7769,'Yuwangtai',NULL,NULL),(410212,'祥符区',410200,'祥符',3,'0378','475100','中国,河南省,开封市,祥符区',114.439,34.7587,'Xiangfu',NULL,NULL),(410221,'杞县',410200,'杞县',3,'0378','475200','中国,河南省,开封市,杞县',114.783,34.5503,'Qixian',NULL,NULL),(410222,'通许县',410200,'通许',3,'0378','475400','中国,河南省,开封市,通许县',114.467,34.4752,'Tongxu',NULL,NULL),(410223,'尉氏县',410200,'尉氏',3,'0378','475500','中国,河南省,开封市,尉氏县',114.193,34.4122,'Weishi',NULL,NULL),(410225,'兰考县',410200,'兰考',3,'0378','475300','中国,河南省,开封市,兰考县',114.82,34.8235,'Lankao',NULL,NULL),(410300,'洛阳市',410000,'洛阳',2,'0379','471000','中国,河南省,洛阳市',112.434,34.663,'Luoyang',NULL,NULL),(410302,'老城区',410300,'老城',3,'0379','471002','中国,河南省,洛阳市,老城区',112.469,34.6836,'Laocheng',NULL,NULL),(410303,'西工区',410300,'西工',3,'0379','471000','中国,河南省,洛阳市,西工区',112.437,34.67,'Xigong',NULL,NULL),(410304,'瀍河回族区',410300,'瀍河',3,'0379','471002','中国,河南省,洛阳市,瀍河回族区',112.5,34.6799,'Chanhe',NULL,NULL),(410305,'涧西区',410300,'涧西',3,'0379','471003','中国,河南省,洛阳市,涧西区',112.396,34.6582,'Jianxi',NULL,NULL),(410306,'吉利区',410300,'吉利',3,'0379','471012','中国,河南省,洛阳市,吉利区',112.589,34.9009,'Jili',NULL,NULL),(410311,'洛龙区',410300,'洛龙',3,'0379','471000','中国,河南省,洛阳市,洛龙区',112.464,34.6187,'Luolong',NULL,NULL),(410322,'孟津县',410300,'孟津',3,'0379','471100','中国,河南省,洛阳市,孟津县',112.444,34.826,'Mengjin',NULL,NULL),(410323,'新安县',410300,'新安',3,'0379','471800','中国,河南省,洛阳市,新安县',112.132,34.7281,'Xin\'an',NULL,NULL),(410324,'栾川县',410300,'栾川',3,'0379','471500','中国,河南省,洛阳市,栾川县',111.618,33.7858,'Luanchuan',NULL,NULL),(410325,'嵩县',410300,'嵩县',3,'0379','471400','中国,河南省,洛阳市,嵩县',112.085,34.1347,'Songxian',NULL,NULL),(410326,'汝阳县',410300,'汝阳',3,'0379','471200','中国,河南省,洛阳市,汝阳县',112.473,34.1539,'Ruyang',NULL,NULL),(410327,'宜阳县',410300,'宜阳',3,'0379','471600','中国,河南省,洛阳市,宜阳县',112.179,34.5152,'Yiyang',NULL,NULL),(410328,'洛宁县',410300,'洛宁',3,'0379','471700','中国,河南省,洛阳市,洛宁县',111.651,34.3891,'Luoning',NULL,NULL),(410329,'伊川县',410300,'伊川',3,'0379','471300','中国,河南省,洛阳市,伊川县',112.429,34.4221,'Yichuan',NULL,NULL),(410381,'偃师市',410300,'偃师',3,'0379','471900','中国,河南省,洛阳市,偃师市',112.792,34.7281,'Yanshi',NULL,NULL),(410400,'平顶山市',410000,'平顶山',2,'0375','467000','中国,河南省,平顶山市',113.308,33.7352,'Pingdingshan',NULL,NULL),(410402,'新华区',410400,'新华',3,'0375','467002','中国,河南省,平顶山市,新华区',113.294,33.7373,'Xinhua',NULL,NULL),(410403,'卫东区',410400,'卫东',3,'0375','467021','中国,河南省,平顶山市,卫东区',113.335,33.7347,'Weidong',NULL,NULL),(410404,'石龙区',410400,'石龙',3,'0375','467045','中国,河南省,平顶山市,石龙区',112.899,33.8988,'Shilong',NULL,NULL),(410411,'湛河区',410400,'湛河',3,'0375','467000','中国,河南省,平顶山市,湛河区',113.293,33.7362,'Zhanhe',NULL,NULL),(410421,'宝丰县',410400,'宝丰',3,'0375','467400','中国,河南省,平顶山市,宝丰县',113.055,33.8692,'Baofeng',NULL,NULL),(410422,'叶县',410400,'叶县',3,'0375','467200','中国,河南省,平顶山市,叶县',113.351,33.6222,'Yexian',NULL,NULL),(410423,'鲁山县',410400,'鲁山',3,'0375','467300','中国,河南省,平顶山市,鲁山县',112.906,33.7388,'Lushan',NULL,NULL),(410425,'郏县',410400,'郏县',3,'0375','467100','中国,河南省,平顶山市,郏县',113.216,33.9707,'Jiaxian',NULL,NULL),(410481,'舞钢市',410400,'舞钢',3,'0375','462500','中国,河南省,平顶山市,舞钢市',113.524,33.2938,'Wugang',NULL,NULL),(410482,'汝州市',410400,'汝州',3,'0375','467500','中国,河南省,平顶山市,汝州市',112.843,34.1614,'Ruzhou',NULL,NULL),(410500,'安阳市',410000,'安阳',2,'0372','455000','中国,河南省,安阳市',114.352,36.1034,'Anyang',NULL,NULL),(410502,'文峰区',410500,'文峰',3,'0372','455000','中国,河南省,安阳市,文峰区',114.357,36.0905,'Wenfeng',NULL,NULL),(410503,'北关区',410500,'北关',3,'0372','455001','中国,河南省,安阳市,北关区',114.357,36.1187,'Beiguan',NULL,NULL),(410505,'殷都区',410500,'殷都',3,'0372','455004','中国,河南省,安阳市,殷都区',114.303,36.1099,'Yindu',NULL,NULL),(410506,'龙安区',410500,'龙安',3,'0372','455001','中国,河南省,安阳市,龙安区',114.348,36.119,'Long\'an',NULL,NULL),(410522,'安阳县',410500,'安阳',3,'0372','455000','中国,河南省,安阳市,安阳县',114.366,36.067,'Anyang',NULL,NULL),(410523,'汤阴县',410500,'汤阴',3,'0372','456150','中国,河南省,安阳市,汤阴县',114.358,35.9215,'Tangyin',NULL,NULL),(410526,'滑县',410500,'滑县',3,'0372','456400','中国,河南省,安阳市,滑县',114.521,35.5807,'Huaxian',NULL,NULL),(410527,'内黄县',410500,'内黄',3,'0372','456350','中国,河南省,安阳市,内黄县',114.907,35.9527,'Neihuang',NULL,NULL),(410581,'林州市',410500,'林州',3,'0372','456550','中国,河南省,安阳市,林州市',113.816,36.078,'Linzhou',NULL,NULL),(410600,'鹤壁市',410000,'鹤壁',2,'0392','458030','中国,河南省,鹤壁市',114.295,35.7482,'Hebi',NULL,NULL),(410602,'鹤山区',410600,'鹤山',3,'0392','458010','中国,河南省,鹤壁市,鹤山区',114.163,35.9546,'Heshan',NULL,NULL),(410603,'山城区',410600,'山城',3,'0392','458000','中国,河南省,鹤壁市,山城区',114.184,35.8977,'Shancheng',NULL,NULL),(410611,'淇滨区',410600,'淇滨',3,'0392','458000','中国,河南省,鹤壁市,淇滨区',114.299,35.7413,'Qibin',NULL,NULL),(410621,'浚县',410600,'浚县',3,'0392','456250','中国,河南省,鹤壁市,浚县',114.549,35.6708,'Xunxian',NULL,NULL),(410622,'淇县',410600,'淇县',3,'0392','456750','中国,河南省,鹤壁市,淇县',114.198,35.6078,'Qixian',NULL,NULL),(410700,'新乡市',410000,'新乡',2,'0373','453000','中国,河南省,新乡市',113.884,35.3026,'Xinxiang',NULL,NULL),(410702,'红旗区',410700,'红旗',3,'0373','453000','中国,河南省,新乡市,红旗区',113.875,35.3037,'Hongqi',NULL,NULL),(410703,'卫滨区',410700,'卫滨',3,'0373','453000','中国,河南省,新乡市,卫滨区',113.866,35.3021,'Weibin',NULL,NULL),(410704,'凤泉区',410700,'凤泉',3,'0373','453011','中国,河南省,新乡市,凤泉区',113.915,35.384,'Fengquan',NULL,NULL),(410711,'牧野区',410700,'牧野',3,'0373','453002','中国,河南省,新乡市,牧野区',113.909,35.3149,'Muye',NULL,NULL),(410721,'新乡县',410700,'新乡',3,'0373','453700','中国,河南省,新乡市,新乡县',113.805,35.1908,'Xinxiang',NULL,NULL),(410724,'获嘉县',410700,'获嘉',3,'0373','453800','中国,河南省,新乡市,获嘉县',113.662,35.2652,'Huojia',NULL,NULL),(410725,'原阳县',410700,'原阳',3,'0373','453500','中国,河南省,新乡市,原阳县',113.94,35.0657,'Yuanyang',NULL,NULL),(410726,'延津县',410700,'延津',3,'0373','453200','中国,河南省,新乡市,延津县',114.203,35.1433,'Yanjin',NULL,NULL),(410727,'封丘县',410700,'封丘',3,'0373','453300','中国,河南省,新乡市,封丘县',114.419,35.0417,'Fengqiu',NULL,NULL),(410728,'长垣县',410700,'长垣',3,'0373','453400','中国,河南省,新乡市,长垣县',114.669,35.2005,'Changyuan',NULL,NULL),(410781,'卫辉市',410700,'卫辉',3,'0373','453100','中国,河南省,新乡市,卫辉市',114.065,35.3984,'Weihui',NULL,NULL),(410782,'辉县市',410700,'辉县',3,'0373','453600','中国,河南省,新乡市,辉县市',113.807,35.4631,'Huixian',NULL,NULL),(410800,'焦作市',410000,'焦作',2,'0391','454002','中国,河南省,焦作市',113.238,35.239,'Jiaozuo',NULL,NULL),(410802,'解放区',410800,'解放',3,'0391','454000','中国,河南省,焦作市,解放区',113.229,35.2402,'Jiefang',NULL,NULL),(410803,'中站区',410800,'中站',3,'0391','454191','中国,河南省,焦作市,中站区',113.183,35.2366,'Zhongzhan',NULL,NULL),(410804,'马村区',410800,'马村',3,'0391','454171','中国,河南省,焦作市,马村区',113.319,35.2691,'Macun',NULL,NULL),(410811,'山阳区',410800,'山阳',3,'0391','454002','中国,河南省,焦作市,山阳区',113.255,35.2144,'Shanyang',NULL,NULL),(410821,'修武县',410800,'修武',3,'0391','454350','中国,河南省,焦作市,修武县',113.448,35.2236,'Xiuwu',NULL,NULL),(410822,'博爱县',410800,'博爱',3,'0391','454450','中国,河南省,焦作市,博爱县',113.067,35.1694,'Boai',NULL,NULL),(410823,'武陟县',410800,'武陟',3,'0391','454950','中国,河南省,焦作市,武陟县',113.397,35.0951,'Wuzhi',NULL,NULL),(410825,'温县',410800,'温县',3,'0391','454850','中国,河南省,焦作市,温县',113.081,34.9402,'Wenxian',NULL,NULL),(410882,'沁阳市',410800,'沁阳',3,'0391','454550','中国,河南省,焦作市,沁阳市',112.945,35.0894,'Qinyang',NULL,NULL),(410883,'孟州市',410800,'孟州',3,'0391','454750','中国,河南省,焦作市,孟州市',112.791,34.9071,'Mengzhou',NULL,NULL),(410900,'濮阳市',410000,'濮阳',2,'0393','457000','中国,河南省,濮阳市',115.041,35.7682,'Puyang',NULL,NULL),(410902,'华龙区',410900,'华龙',3,'0393','457001','中国,河南省,濮阳市,华龙区',115.074,35.7774,'Hualong',NULL,NULL),(410922,'清丰县',410900,'清丰',3,'0393','457300','中国,河南省,濮阳市,清丰县',115.104,35.8851,'Qingfeng',NULL,NULL),(410923,'南乐县',410900,'南乐',3,'0393','457400','中国,河南省,濮阳市,南乐县',115.206,36.0769,'Nanle',NULL,NULL),(410926,'范县',410900,'范县',3,'0393','457500','中国,河南省,濮阳市,范县',115.504,35.8518,'Fanxian',NULL,NULL),(410927,'台前县',410900,'台前',3,'0393','457600','中国,河南省,濮阳市,台前县',115.872,35.9692,'Taiqian',NULL,NULL),(410928,'濮阳县',410900,'濮阳',3,'0393','457100','中国,河南省,濮阳市,濮阳县',115.031,35.7075,'Puyang',NULL,NULL),(411000,'许昌市',410000,'许昌',2,'0374','461000','中国,河南省,许昌市',113.826,34.023,'Xuchang',NULL,NULL),(411002,'魏都区',411000,'魏都',3,'0374','461000','中国,河南省,许昌市,魏都区',113.823,34.0254,'Weidu',NULL,NULL),(411023,'许昌县',411000,'许昌',3,'0374','461100','中国,河南省,许昌市,许昌县',113.847,34.0041,'Xuchang',NULL,NULL),(411024,'鄢陵县',411000,'鄢陵',3,'0374','461200','中国,河南省,许昌市,鄢陵县',114.188,34.1032,'Yanling',NULL,NULL),(411025,'襄城县',411000,'襄城',3,'0374','461700','中国,河南省,许昌市,襄城县',113.482,33.8493,'Xiangcheng',NULL,NULL),(411081,'禹州市',411000,'禹州',3,'0374','461670','中国,河南省,许昌市,禹州市',113.488,34.1405,'Yuzhou',NULL,NULL),(411082,'长葛市',411000,'长葛',3,'0374','461500','中国,河南省,许昌市,长葛市',113.773,34.2185,'Changge',NULL,NULL),(411100,'漯河市',410000,'漯河',2,'0395','462000','中国,河南省,漯河市',114.026,33.5759,'Luohe',NULL,NULL),(411102,'源汇区',411100,'源汇',3,'0395','462000','中国,河南省,漯河市,源汇区',114.006,33.5563,'Yuanhui',NULL,NULL),(411103,'郾城区',411100,'郾城',3,'0395','462300','中国,河南省,漯河市,郾城区',114.007,33.5872,'Yancheng',NULL,NULL),(411104,'召陵区',411100,'召陵',3,'0395','462300','中国,河南省,漯河市,召陵区',114.094,33.586,'Zhaoling',NULL,NULL),(411121,'舞阳县',411100,'舞阳',3,'0395','462400','中国,河南省,漯河市,舞阳县',113.598,33.4324,'Wuyang',NULL,NULL),(411122,'临颍县',411100,'临颍',3,'0395','462600','中国,河南省,漯河市,临颍县',113.937,33.8112,'Linying',NULL,NULL),(411200,'三门峡市',410000,'三门峡',2,'0398','472000','中国,河南省,三门峡市',111.194,34.7773,'Sanmenxia',NULL,NULL),(411202,'湖滨区',411200,'湖滨',3,'0398','472000','中国,河南省,三门峡市,湖滨区',111.2,34.7787,'Hubin',NULL,NULL),(411221,'渑池县',411200,'渑池',3,'0398','472400','中国,河南省,三门峡市,渑池县',111.762,34.7673,'Mianchi',NULL,NULL),(411222,'陕县',411200,'陕县',3,'0398','472100','中国,河南省,三门峡市,陕县',111.103,34.7205,'Shanxian',NULL,NULL),(411224,'卢氏县',411200,'卢氏',3,'0398','472200','中国,河南省,三门峡市,卢氏县',111.048,34.0544,'Lushi',NULL,NULL),(411281,'义马市',411200,'义马',3,'0398','472300','中国,河南省,三门峡市,义马市',111.874,34.7472,'Yima',NULL,NULL),(411282,'灵宝市',411200,'灵宝',3,'0398','472500','中国,河南省,三门峡市,灵宝市',110.895,34.5168,'Lingbao',NULL,NULL),(411300,'南阳市',410000,'南阳',2,'0377','473002','中国,河南省,南阳市',112.541,32.9991,'Nanyang',NULL,NULL),(411302,'宛城区',411300,'宛城',3,'0377','473001','中国,河南省,南阳市,宛城区',112.54,33.0038,'Wancheng',NULL,NULL),(411303,'卧龙区',411300,'卧龙',3,'0377','473003','中国,河南省,南阳市,卧龙区',112.535,32.9861,'Wolong',NULL,NULL),(411321,'南召县',411300,'南召',3,'0377','474650','中国,河南省,南阳市,南召县',112.432,33.491,'Nanzhao',NULL,NULL),(411322,'方城县',411300,'方城',3,'0377','473200','中国,河南省,南阳市,方城县',113.013,33.2545,'Fangcheng',NULL,NULL),(411323,'西峡县',411300,'西峡',3,'0377','474550','中国,河南省,南阳市,西峡县',111.482,33.2977,'Xixia',NULL,NULL),(411324,'镇平县',411300,'镇平',3,'0377','474250','中国,河南省,南阳市,镇平县',112.24,33.0363,'Zhenping',NULL,NULL),(411325,'内乡县',411300,'内乡',3,'0377','474350','中国,河南省,南阳市,内乡县',111.85,33.0467,'Neixiang',NULL,NULL),(411326,'淅川县',411300,'淅川',3,'0377','474450','中国,河南省,南阳市,淅川县',111.487,33.1371,'Xichuan',NULL,NULL),(411327,'社旗县',411300,'社旗',3,'0377','473300','中国,河南省,南阳市,社旗县',112.947,33.055,'Sheqi',NULL,NULL),(411328,'唐河县',411300,'唐河',3,'0377','473400','中国,河南省,南阳市,唐河县',112.836,32.6945,'Tanghe',NULL,NULL),(411329,'新野县',411300,'新野',3,'0377','473500','中国,河南省,南阳市,新野县',112.362,32.517,'Xinye',NULL,NULL),(411330,'桐柏县',411300,'桐柏',3,'0377','474750','中国,河南省,南阳市,桐柏县',113.429,32.3792,'Tongbai',NULL,NULL),(411381,'邓州市',411300,'邓州',3,'0377','474150','中国,河南省,南阳市,邓州市',112.09,32.6858,'Dengzhou',NULL,NULL),(411400,'商丘市',410000,'商丘',2,'0370','476000','中国,河南省,商丘市',115.65,34.4371,'Shangqiu',NULL,NULL),(411402,'梁园区',411400,'梁园',3,'0370','476000','中国,河南省,商丘市,梁园区',115.645,34.4434,'Liangyuan',NULL,NULL),(411403,'睢阳区',411400,'睢阳',3,'0370','476100','中国,河南省,商丘市,睢阳区',115.653,34.388,'Suiyang',NULL,NULL),(411421,'民权县',411400,'民权',3,'0370','476800','中国,河南省,商丘市,民权县',115.146,34.6493,'Minquan',NULL,NULL),(411422,'睢县',411400,'睢县',3,'0370','476900','中国,河南省,商丘市,睢县',115.072,34.4454,'Suixian',NULL,NULL),(411423,'宁陵县',411400,'宁陵',3,'0370','476700','中国,河南省,商丘市,宁陵县',115.305,34.4546,'Ningling',NULL,NULL),(411424,'柘城县',411400,'柘城',3,'0370','476200','中国,河南省,商丘市,柘城县',115.305,34.0911,'Zhecheng',NULL,NULL),(411425,'虞城县',411400,'虞城',3,'0370','476300','中国,河南省,商丘市,虞城县',115.863,34.4019,'Yucheng',NULL,NULL),(411426,'夏邑县',411400,'夏邑',3,'0370','476400','中国,河南省,商丘市,夏邑县',116.133,34.2324,'Xiayi',NULL,NULL),(411481,'永城市',411400,'永城',3,'0370','476600','中国,河南省,商丘市,永城市',116.449,33.9291,'Yongcheng',NULL,NULL),(411500,'信阳市',410000,'信阳',2,'0376','464000','中国,河南省,信阳市',114.075,32.1233,'Xinyang',NULL,NULL),(411502,'浉河区',411500,'浉河',3,'0376','464000','中国,河南省,信阳市,浉河区',114.059,32.1168,'Shihe',NULL,NULL),(411503,'平桥区',411500,'平桥',3,'0376','464100','中国,河南省,信阳市,平桥区',114.124,32.1009,'Pingqiao',NULL,NULL),(411521,'罗山县',411500,'罗山',3,'0376','464200','中国,河南省,信阳市,罗山县',114.531,32.2028,'Luoshan',NULL,NULL),(411522,'光山县',411500,'光山',3,'0376','465450','中国,河南省,信阳市,光山县',114.919,32.0099,'Guangshan',NULL,NULL),(411523,'新县',411500,'新县',3,'0376','465550','中国,河南省,信阳市,新县',114.879,31.6439,'Xinxian',NULL,NULL),(411524,'商城县',411500,'商城',3,'0376','465350','中国,河南省,信阳市,商城县',115.409,31.7999,'Shangcheng',NULL,NULL),(411525,'固始县',411500,'固始',3,'0376','465250','中国,河南省,信阳市,固始县',115.683,32.1801,'Gushi',NULL,NULL),(411526,'潢川县',411500,'潢川',3,'0376','465150','中国,河南省,信阳市,潢川县',115.047,32.1376,'Huangchuan',NULL,NULL),(411527,'淮滨县',411500,'淮滨',3,'0376','464400','中国,河南省,信阳市,淮滨县',115.421,32.4661,'Huaibin',NULL,NULL),(411528,'息县',411500,'息县',3,'0376','464300','中国,河南省,信阳市,息县',114.74,32.3428,'Xixian',NULL,NULL),(411600,'周口市',410000,'周口',2,'0394','466000','中国,河南省,周口市',114.65,33.6204,'Zhoukou',NULL,NULL),(411602,'川汇区',411600,'川汇',3,'0394','466000','中国,河南省,周口市,川汇区',114.642,33.6256,'Chuanhui',NULL,NULL),(411621,'扶沟县',411600,'扶沟',3,'0394','461300','中国,河南省,周口市,扶沟县',114.395,34.06,'Fugou',NULL,NULL),(411622,'西华县',411600,'西华',3,'0394','466600','中国,河南省,周口市,西华县',114.523,33.7855,'Xihua',NULL,NULL),(411623,'商水县',411600,'商水',3,'0394','466100','中国,河南省,周口市,商水县',114.606,33.5391,'Shangshui',NULL,NULL),(411624,'沈丘县',411600,'沈丘',3,'0394','466300','中国,河南省,周口市,沈丘县',115.099,33.4094,'Shenqiu',NULL,NULL),(411625,'郸城县',411600,'郸城',3,'0394','477150','中国,河南省,周口市,郸城县',115.177,33.6449,'Dancheng',NULL,NULL),(411626,'淮阳县',411600,'淮阳',3,'0394','466700','中国,河南省,周口市,淮阳县',114.888,33.7321,'Huaiyang',NULL,NULL),(411627,'太康县',411600,'太康',3,'0394','461400','中国,河南省,周口市,太康县',114.838,34.0638,'Taikang',NULL,NULL),(411628,'鹿邑县',411600,'鹿邑',3,'0394','477200','中国,河南省,周口市,鹿邑县',115.486,33.8593,'Luyi',NULL,NULL),(411681,'项城市',411600,'项城',3,'0394','466200','中国,河南省,周口市,项城市',114.876,33.4672,'Xiangcheng',NULL,NULL),(411700,'驻马店市',410000,'驻马店',2,'0396','463000','中国,河南省,驻马店市',114.025,32.9802,'Zhumadian',NULL,NULL),(411702,'驿城区',411700,'驿城',3,'0396','463000','中国,河南省,驻马店市,驿城区',113.994,32.9732,'Yicheng',NULL,NULL),(411721,'西平县',411700,'西平',3,'0396','463900','中国,河南省,驻马店市,西平县',114.023,33.3845,'Xiping',NULL,NULL),(411722,'上蔡县',411700,'上蔡',3,'0396','463800','中国,河南省,驻马店市,上蔡县',114.268,33.2682,'Shangcai',NULL,NULL),(411723,'平舆县',411700,'平舆',3,'0396','463400','中国,河南省,驻马店市,平舆县',114.636,32.9573,'Pingyu',NULL,NULL),(411724,'正阳县',411700,'正阳',3,'0396','463600','中国,河南省,驻马店市,正阳县',114.39,32.6039,'Zhengyang',NULL,NULL),(411725,'确山县',411700,'确山',3,'0396','463200','中国,河南省,驻马店市,确山县',114.029,32.8028,'Queshan',NULL,NULL),(411726,'泌阳县',411700,'泌阳',3,'0396','463700','中国,河南省,驻马店市,泌阳县',113.327,32.7178,'Biyang',NULL,NULL),(411727,'汝南县',411700,'汝南',3,'0396','463300','中国,河南省,驻马店市,汝南县',114.361,33.0046,'Runan',NULL,NULL),(411728,'遂平县',411700,'遂平',3,'0396','463100','中国,河南省,驻马店市,遂平县',114.013,33.1457,'Suiping',NULL,NULL),(411729,'新蔡县',411700,'新蔡',3,'0396','463500','中国,河南省,驻马店市,新蔡县',114.982,32.7502,'Xincai',NULL,NULL),(419000,'直辖县级',410000,' ',2,'','','中国,河南省,直辖县级',113.665,34.758,'',NULL,NULL),(419001,'济源市',419000,'济源',3,'0391','454650','中国,河南省,直辖县级,济源市',112.59,35.0904,'Jiyuan',NULL,NULL),(420000,'湖北省',100000,'湖北',1,'','','中国,湖北省',114.299,30.5844,'Hubei',NULL,NULL),(420100,'武汉市',420000,'武汉',2,'','430014','中国,湖北省,武汉市',114.299,30.5844,'Wuhan',NULL,NULL),(420102,'江岸区',420100,'江岸',3,'027','430014','中国,湖北省,武汉市,江岸区',114.309,30.5998,'Jiang\'an',NULL,NULL),(420103,'江汉区',420100,'江汉',3,'027','430021','中国,湖北省,武汉市,江汉区',114.271,30.6015,'Jianghan',NULL,NULL),(420104,'硚口区',420100,'硚口',3,'027','430033','中国,湖北省,武汉市,硚口区',114.264,30.5695,'Qiaokou',NULL,NULL),(420105,'汉阳区',420100,'汉阳',3,'027','430050','中国,湖北省,武汉市,汉阳区',114.275,30.5492,'Hanyang',NULL,NULL),(420106,'武昌区',420100,'武昌',3,'027','430061','中国,湖北省,武汉市,武昌区',114.316,30.5539,'Wuchang',NULL,NULL),(420107,'青山区',420100,'青山',3,'027','430080','中国,湖北省,武汉市,青山区',114.391,30.6343,'Qingshan',NULL,NULL),(420111,'洪山区',420100,'洪山',3,'027','430070','中国,湖北省,武汉市,洪山区',114.344,30.4999,'Hongshan',NULL,NULL),(420112,'东西湖区',420100,'东西湖',3,'027','430040','中国,湖北省,武汉市,东西湖区',114.137,30.6199,'Dongxihu',NULL,NULL),(420113,'汉南区',420100,'汉南',3,'027','430090','中国,湖北省,武汉市,汉南区',114.085,30.3088,'Hannan',NULL,NULL),(420114,'蔡甸区',420100,'蔡甸',3,'027','430100','中国,湖北省,武汉市,蔡甸区',114.029,30.582,'Caidian',NULL,NULL),(420115,'江夏区',420100,'江夏',3,'027','430200','中国,湖北省,武汉市,江夏区',114.313,30.3465,'Jiangxia',NULL,NULL),(420116,'黄陂区',420100,'黄陂',3,'027','432200','中国,湖北省,武汉市,黄陂区',114.375,30.8815,'Huangpi',NULL,NULL),(420117,'新洲区',420100,'新洲',3,'027','431400','中国,湖北省,武汉市,新洲区',114.801,30.8414,'Xinzhou',NULL,NULL),(420200,'黄石市',420000,'黄石',2,'0714','435003','中国,湖北省,黄石市',115.077,30.2201,'Huangshi',NULL,NULL),(420202,'黄石港区',420200,'黄石港',3,'0714','435000','中国,湖北省,黄石市,黄石港区',115.066,30.2228,'Huangshigang',NULL,NULL),(420203,'西塞山区',420200,'西塞山',3,'0714','435001','中国,湖北省,黄石市,西塞山区',115.11,30.2049,'Xisaishan',NULL,NULL),(420204,'下陆区',420200,'下陆',3,'0714','435005','中国,湖北省,黄石市,下陆区',114.961,30.1737,'Xialu',NULL,NULL),(420205,'铁山区',420200,'铁山',3,'0714','435006','中国,湖北省,黄石市,铁山区',114.901,30.2068,'Tieshan',NULL,NULL),(420222,'阳新县',420200,'阳新',3,'0714','435200','中国,湖北省,黄石市,阳新县',115.215,29.8304,'Yangxin',NULL,NULL),(420281,'大冶市',420200,'大冶',3,'0714','435100','中国,湖北省,黄石市,大冶市',114.972,30.0944,'Daye',NULL,NULL),(420300,'十堰市',420000,'十堰',2,'0719','442000','中国,湖北省,十堰市',110.785,32.647,'Shiyan',NULL,NULL),(420302,'茅箭区',420300,'茅箭',3,'0719','442012','中国,湖北省,十堰市,茅箭区',110.813,32.5915,'Maojian',NULL,NULL),(420303,'张湾区',420300,'张湾',3,'0719','442001','中国,湖北省,十堰市,张湾区',110.771,32.652,'Zhangwan',NULL,NULL),(420304,'郧阳区',420300,'郧阳',3,'0719','442500','中国,湖北省,十堰市,郧阳区',110.819,32.8359,'Yunyang',NULL,NULL),(420322,'郧西县',420300,'郧西',3,'0719','442600','中国,湖北省,十堰市,郧西县',110.426,32.9935,'Yunxi',NULL,NULL),(420323,'竹山县',420300,'竹山',3,'0719','442200','中国,湖北省,十堰市,竹山县',110.231,32.2254,'Zhushan',NULL,NULL),(420324,'竹溪县',420300,'竹溪',3,'0719','442300','中国,湖北省,十堰市,竹溪县',109.718,32.319,'Zhuxi',NULL,NULL),(420325,'房县',420300,'房县',3,'0719','442100','中国,湖北省,十堰市,房县',110.744,32.0579,'Fangxian',NULL,NULL),(420381,'丹江口市',420300,'丹江口',3,'0719','442700','中国,湖北省,十堰市,丹江口市',111.515,32.5409,'Danjiangkou',NULL,NULL),(420500,'宜昌市',420000,'宜昌',2,'0717','443000','中国,湖北省,宜昌市',111.291,30.7026,'Yichang',NULL,NULL),(420502,'西陵区',420500,'西陵',3,'0717','443000','中国,湖北省,宜昌市,西陵区',111.286,30.7108,'Xiling',NULL,NULL),(420503,'伍家岗区',420500,'伍家岗',3,'0717','443001','中国,湖北省,宜昌市,伍家岗区',111.361,30.6443,'Wujiagang',NULL,NULL),(420504,'点军区',420500,'点军',3,'0717','443006','中国,湖北省,宜昌市,点军区',111.268,30.6934,'Dianjun',NULL,NULL),(420505,'猇亭区',420500,'猇亭',3,'0717','443007','中国,湖北省,宜昌市,猇亭区',111.441,30.5266,'Xiaoting',NULL,NULL),(420506,'夷陵区',420500,'夷陵',3,'0717','443100','中国,湖北省,宜昌市,夷陵区',111.326,30.7688,'Yiling',NULL,NULL),(420525,'远安县',420500,'远安',3,'0717','444200','中国,湖北省,宜昌市,远安县',111.642,31.0599,'Yuan\'an',NULL,NULL),(420526,'兴山县',420500,'兴山',3,'0717','443711','中国,湖北省,宜昌市,兴山县',110.75,31.3469,'Xingshan',NULL,NULL),(420527,'秭归县',420500,'秭归',3,'0717','443600','中国,湖北省,宜昌市,秭归县',110.982,30.827,'Zigui',NULL,NULL),(420528,'长阳土家族自治县',420500,'长阳',3,'0717','443500','中国,湖北省,宜昌市,长阳土家族自治县',111.201,30.4705,'Changyang',NULL,NULL),(420529,'五峰土家族自治县',420500,'五峰',3,'0717','443413','中国,湖北省,宜昌市,五峰土家族自治县',110.675,30.1986,'Wufeng',NULL,NULL),(420581,'宜都市',420500,'宜都',3,'0717','443300','中国,湖北省,宜昌市,宜都市',111.45,30.3781,'Yidu',NULL,NULL),(420582,'当阳市',420500,'当阳',3,'0717','444100','中国,湖北省,宜昌市,当阳市',111.789,30.8208,'Dangyang',NULL,NULL),(420583,'枝江市',420500,'枝江',3,'0717','443200','中国,湖北省,宜昌市,枝江市',111.769,30.4261,'Zhijiang',NULL,NULL),(420600,'襄阳市',420000,'襄阳',2,'0710','441021','中国,湖北省,襄阳市',112.144,32.0424,'Xiangyang',NULL,NULL),(420602,'襄城区',420600,'襄城',3,'0710','441021','中国,湖北省,襄阳市,襄城区',112.134,32.0102,'Xiangcheng',NULL,NULL),(420606,'樊城区',420600,'樊城',3,'0710','441001','中国,湖北省,襄阳市,樊城区',112.135,32.0448,'Fancheng',NULL,NULL),(420607,'襄州区',420600,'襄州',3,'0710','441100','中国,湖北省,襄阳市,襄州区',112.15,32.0151,'Xiangzhou',NULL,NULL),(420624,'南漳县',420600,'南漳',3,'0710','441500','中国,湖北省,襄阳市,南漳县',111.846,31.7765,'Nanzhang',NULL,NULL),(420625,'谷城县',420600,'谷城',3,'0710','441700','中国,湖北省,襄阳市,谷城县',111.653,32.2638,'Gucheng',NULL,NULL),(420626,'保康县',420600,'保康',3,'0710','441600','中国,湖北省,襄阳市,保康县',111.261,31.8787,'Baokang',NULL,NULL),(420682,'老河口市',420600,'老河口',3,'0710','441800','中国,湖北省,襄阳市,老河口市',111.671,32.3848,'Laohekou',NULL,NULL),(420683,'枣阳市',420600,'枣阳',3,'0710','441200','中国,湖北省,襄阳市,枣阳市',112.774,32.1314,'Zaoyang',NULL,NULL),(420684,'宜城市',420600,'宜城',3,'0710','441400','中国,湖北省,襄阳市,宜城市',112.258,31.7197,'Yicheng',NULL,NULL),(420700,'鄂州市',420000,'鄂州',2,'0711','436000','中国,湖北省,鄂州市',114.891,30.3965,'Ezhou',NULL,NULL),(420702,'梁子湖区',420700,'梁子湖',3,'0711','436064','中国,湖北省,鄂州市,梁子湖区',114.685,30.1,'Liangzihu',NULL,NULL),(420703,'华容区',420700,'华容',3,'0711','436030','中国,湖北省,鄂州市,华容区',114.736,30.5333,'Huarong',NULL,NULL),(420704,'鄂城区',420700,'鄂城',3,'0711','436000','中国,湖北省,鄂州市,鄂城区',114.892,30.4002,'Echeng',NULL,NULL),(420800,'荆门市',420000,'荆门',2,'0724','448000','中国,湖北省,荆门市',112.204,31.0354,'Jingmen',NULL,NULL),(420802,'东宝区',420800,'东宝',3,'0724','448004','中国,湖北省,荆门市,东宝区',112.201,31.0519,'Dongbao',NULL,NULL),(420804,'掇刀区',420800,'掇刀',3,'0724','448124','中国,湖北省,荆门市,掇刀区',112.208,30.9732,'Duodao',NULL,NULL),(420821,'京山县',420800,'京山',3,'0724','431800','中国,湖北省,荆门市,京山县',113.111,31.0224,'Jingshan',NULL,NULL),(420822,'沙洋县',420800,'沙洋',3,'0724','448200','中国,湖北省,荆门市,沙洋县',112.589,30.7092,'Shayang',NULL,NULL),(420881,'钟祥市',420800,'钟祥',3,'0724','431900','中国,湖北省,荆门市,钟祥市',112.589,31.1678,'Zhongxiang',NULL,NULL),(420900,'孝感市',420000,'孝感',2,'0712','432100','中国,湖北省,孝感市',113.927,30.9264,'Xiaogan',NULL,NULL),(420902,'孝南区',420900,'孝南',3,'0712','432100','中国,湖北省,孝感市,孝南区',113.911,30.9168,'Xiaonan',NULL,NULL),(420921,'孝昌县',420900,'孝昌',3,'0712','432900','中国,湖北省,孝感市,孝昌县',113.998,31.258,'Xiaochang',NULL,NULL),(420922,'大悟县',420900,'大悟',3,'0712','432800','中国,湖北省,孝感市,大悟县',114.126,31.5618,'Dawu',NULL,NULL),(420923,'云梦县',420900,'云梦',3,'0712','432500','中国,湖北省,孝感市,云梦县',113.753,31.0209,'Yunmeng',NULL,NULL),(420981,'应城市',420900,'应城',3,'0712','432400','中国,湖北省,孝感市,应城市',113.573,30.9283,'Yingcheng',NULL,NULL),(420982,'安陆市',420900,'安陆',3,'0712','432600','中国,湖北省,孝感市,安陆市',113.686,31.2569,'Anlu',NULL,NULL),(420984,'汉川市',420900,'汉川',3,'0712','432300','中国,湖北省,孝感市,汉川市',113.839,30.6612,'Hanchuan',NULL,NULL),(421000,'荆州市',420000,'荆州',2,'0716','434000','中国,湖北省,荆州市',112.238,30.3269,'Jingzhou',NULL,NULL),(421002,'沙市区',421000,'沙市',3,'0716','434000','中国,湖北省,荆州市,沙市区',112.255,30.3111,'Shashi',NULL,NULL),(421003,'荆州区',421000,'荆州',3,'0716','434020','中国,湖北省,荆州市,荆州区',112.19,30.3526,'Jingzhou',NULL,NULL),(421022,'公安县',421000,'公安',3,'0716','434300','中国,湖北省,荆州市,公安县',112.232,30.059,'Gong\'an',NULL,NULL),(421023,'监利县',421000,'监利',3,'0716','433300','中国,湖北省,荆州市,监利县',112.895,29.8149,'Jianli',NULL,NULL),(421024,'江陵县',421000,'江陵',3,'0716','434101','中国,湖北省,荆州市,江陵县',112.425,30.0417,'Jiangling',NULL,NULL),(421081,'石首市',421000,'石首',3,'0716','434400','中国,湖北省,荆州市,石首市',112.426,29.7213,'Shishou',NULL,NULL),(421083,'洪湖市',421000,'洪湖',3,'0716','433200','中国,湖北省,荆州市,洪湖市',113.476,29.827,'Honghu',NULL,NULL),(421087,'松滋市',421000,'松滋',3,'0716','434200','中国,湖北省,荆州市,松滋市',111.767,30.1696,'Songzi',NULL,NULL),(421100,'黄冈市',420000,'黄冈',2,'0713','438000','中国,湖北省,黄冈市',114.879,30.4477,'Huanggang',NULL,NULL),(421102,'黄州区',421100,'黄州',3,'0713','438000','中国,湖北省,黄冈市,黄州区',114.88,30.4344,'Huangzhou',NULL,NULL),(421121,'团风县',421100,'团风',3,'0713','438800','中国,湖北省,黄冈市,团风县',114.872,30.6436,'Tuanfeng',NULL,NULL),(421122,'红安县',421100,'红安',3,'0713','438401','中国,湖北省,黄冈市,红安县',114.622,31.2867,'Hong\'an',NULL,NULL),(421123,'罗田县',421100,'罗田',3,'0713','438600','中国,湖北省,黄冈市,罗田县',115.4,30.7826,'Luotian',NULL,NULL),(421124,'英山县',421100,'英山',3,'0713','438700','中国,湖北省,黄冈市,英山县',115.681,30.7352,'Yingshan',NULL,NULL),(421125,'浠水县',421100,'浠水',3,'0713','438200','中国,湖北省,黄冈市,浠水县',115.269,30.4527,'Xishui',NULL,NULL),(421126,'蕲春县',421100,'蕲春',3,'0713','435300','中国,湖北省,黄冈市,蕲春县',115.436,30.2261,'Qichun',NULL,NULL),(421127,'黄梅县',421100,'黄梅',3,'0713','435500','中国,湖北省,黄冈市,黄梅县',115.944,30.0703,'Huangmei',NULL,NULL),(421181,'麻城市',421100,'麻城',3,'0713','438300','中国,湖北省,黄冈市,麻城市',115.01,31.1723,'Macheng',NULL,NULL),(421182,'武穴市',421100,'武穴',3,'0713','435400','中国,湖北省,黄冈市,武穴市',115.56,29.8445,'Wuxue',NULL,NULL),(421200,'咸宁市',420000,'咸宁',2,'0715','437000','中国,湖北省,咸宁市',114.329,29.8328,'Xianning',NULL,NULL),(421202,'咸安区',421200,'咸安',3,'0715','437000','中国,湖北省,咸宁市,咸安区',114.299,29.8529,'Xian\'an',NULL,NULL),(421221,'嘉鱼县',421200,'嘉鱼',3,'0715','437200','中国,湖北省,咸宁市,嘉鱼县',113.939,29.9705,'Jiayu',NULL,NULL),(421222,'通城县',421200,'通城',3,'0715','437400','中国,湖北省,咸宁市,通城县',113.816,29.2457,'Tongcheng',NULL,NULL),(421223,'崇阳县',421200,'崇阳',3,'0715','437500','中国,湖北省,咸宁市,崇阳县',114.04,29.5556,'Chongyang',NULL,NULL),(421224,'通山县',421200,'通山',3,'0715','437600','中国,湖北省,咸宁市,通山县',114.482,29.6063,'Tongshan',NULL,NULL),(421281,'赤壁市',421200,'赤壁',3,'0715','437300','中国,湖北省,咸宁市,赤壁市',113.9,29.7245,'Chibi',NULL,NULL),(421300,'随州市',420000,'随州',2,'0722','441300','中国,湖北省,随州市',113.374,31.7175,'Suizhou',NULL,NULL),(421303,'曾都区',421300,'曾都',3,'0722','441300','中国,湖北省,随州市,曾都区',113.371,31.7161,'Zengdu',NULL,NULL),(421321,'随县',421300,'随县',3,'0722','441309','中国,湖北省,随州市,随县',113.827,31.6179,'Suixian',NULL,NULL),(421381,'广水市',421300,'广水',3,'0722','432700','中国,湖北省,随州市,广水市',113.827,31.6179,'Guangshui',NULL,NULL),(422800,'恩施土家族苗族自治州',420000,'恩施',2,'0718','445000','中国,湖北省,恩施土家族苗族自治州',109.487,30.2831,'Enshi',NULL,NULL),(422801,'恩施市',422800,'恩施',3,'0718','445000','中国,湖北省,恩施土家族苗族自治州,恩施市',109.479,30.295,'Enshi',NULL,NULL),(422802,'利川市',422800,'利川',3,'0718','445400','中国,湖北省,恩施土家族苗族自治州,利川市',108.936,30.2912,'Lichuan',NULL,NULL),(422822,'建始县',422800,'建始',3,'0718','445300','中国,湖北省,恩施土家族苗族自治州,建始县',109.722,30.6021,'Jianshi',NULL,NULL),(422823,'巴东县',422800,'巴东',3,'0718','444300','中国,湖北省,恩施土家族苗族自治州,巴东县',110.341,31.0423,'Badong',NULL,NULL),(422825,'宣恩县',422800,'宣恩',3,'0718','445500','中国,湖北省,恩施土家族苗族自治州,宣恩县',109.492,29.9871,'Xuanen',NULL,NULL),(422826,'咸丰县',422800,'咸丰',3,'0718','445600','中国,湖北省,恩施土家族苗族自治州,咸丰县',109.152,29.6798,'Xianfeng',NULL,NULL),(422827,'来凤县',422800,'来凤',3,'0718','445700','中国,湖北省,恩施土家族苗族自治州,来凤县',109.407,29.4937,'Laifeng',NULL,NULL),(422828,'鹤峰县',422800,'鹤峰',3,'0718','445800','中国,湖北省,恩施土家族苗族自治州,鹤峰县',110.031,29.8907,'Hefeng',NULL,NULL),(429000,'直辖县级',420000,' ',2,'','','中国,湖北省,直辖县级',114.299,30.5844,'',NULL,NULL),(429004,'仙桃市',429000,'仙桃',3,'0728','433000','中国,湖北省,直辖县级,仙桃市',113.454,30.365,'Xiantao',NULL,NULL),(429005,'潜江市',429000,'潜江',3,'0728','433100','中国,湖北省,直辖县级,潜江市',112.897,30.4212,'Qianjiang',NULL,NULL),(429006,'天门市',429000,'天门',3,'0728','431700','中国,湖北省,直辖县级,天门市',113.166,30.6531,'Tianmen',NULL,NULL),(429021,'神农架林区',429000,'神农架',3,'0719','442400','中国,湖北省,直辖县级,神农架林区',110.672,31.7444,'Shennongjia',NULL,NULL),(430000,'湖南省',100000,'湖南',1,'','','中国,湖南省',112.982,28.1941,'Hunan',NULL,NULL),(430100,'长沙市',430000,'长沙',2,'0731','410005','中国,湖南省,长沙市',112.982,28.1941,'Changsha',NULL,NULL),(430102,'芙蓉区',430100,'芙蓉',3,'0731','410011','中国,湖南省,长沙市,芙蓉区',113.032,28.1844,'Furong',NULL,NULL),(430103,'天心区',430100,'天心',3,'0731','410004','中国,湖南省,长沙市,天心区',112.99,28.1127,'Tianxin',NULL,NULL),(430104,'岳麓区',430100,'岳麓',3,'0731','410013','中国,湖南省,长沙市,岳麓区',112.931,28.2351,'Yuelu',NULL,NULL),(430105,'开福区',430100,'开福',3,'0731','410008','中国,湖南省,长沙市,开福区',112.986,28.2558,'Kaifu',NULL,NULL),(430111,'雨花区',430100,'雨花',3,'0731','410011','中国,湖南省,长沙市,雨花区',113.036,28.1354,'Yuhua',NULL,NULL),(430112,'望城区',430100,'望城',3,'0731','410200','中国,湖南省,长沙市,望城区',112.82,28.3475,'Wangcheng',NULL,NULL),(430121,'长沙县',430100,'长沙',3,'0731','410100','中国,湖南省,长沙市,长沙县',113.081,28.246,'Changsha',NULL,NULL),(430124,'宁乡县',430100,'宁乡',3,'0731','410600','中国,湖南省,长沙市,宁乡县',112.557,28.2536,'Ningxiang',NULL,NULL),(430181,'浏阳市',430100,'浏阳',3,'0731','410300','中国,湖南省,长沙市,浏阳市',113.643,28.1637,'Liuyang',NULL,NULL),(430200,'株洲市',430000,'株洲',2,'0731','412000','中国,湖南省,株洲市',113.152,27.8358,'Zhuzhou',NULL,NULL),(430202,'荷塘区',430200,'荷塘',3,'0731','412000','中国,湖南省,株洲市,荷塘区',113.173,27.8557,'Hetang',NULL,NULL),(430203,'芦淞区',430200,'芦淞',3,'0731','412000','中国,湖南省,株洲市,芦淞区',113.156,27.7852,'Lusong',NULL,NULL),(430204,'石峰区',430200,'石峰',3,'0731','412005','中国,湖南省,株洲市,石峰区',113.118,27.8755,'Shifeng',NULL,NULL),(430211,'天元区',430200,'天元',3,'0731','412007','中国,湖南省,株洲市,天元区',113.123,27.831,'Tianyuan',NULL,NULL),(430221,'株洲县',430200,'株洲',3,'0731','412100','中国,湖南省,株洲市,株洲县',113.144,27.6983,'Zhuzhou',NULL,NULL),(430223,'攸县',430200,'攸县',3,'0731','412300','中国,湖南省,株洲市,攸县',113.344,27.0035,'Youxian',NULL,NULL),(430224,'茶陵县',430200,'茶陵',3,'0731','412400','中国,湖南省,株洲市,茶陵县',113.544,26.7915,'Chaling',NULL,NULL),(430225,'炎陵县',430200,'炎陵',3,'0731','412500','中国,湖南省,株洲市,炎陵县',113.772,26.4882,'Yanling',NULL,NULL),(430281,'醴陵市',430200,'醴陵',3,'0731','412200','中国,湖南省,株洲市,醴陵市',113.497,27.6462,'Liling',NULL,NULL),(430300,'湘潭市',430000,'湘潭',2,'0731','411100','中国,湖南省,湘潭市',112.925,27.8467,'Xiangtan',NULL,NULL),(430302,'雨湖区',430300,'雨湖',3,'0731','411100','中国,湖南省,湘潭市,雨湖区',112.904,27.8686,'Yuhu',NULL,NULL),(430304,'岳塘区',430300,'岳塘',3,'0731','411101','中国,湖南省,湘潭市,岳塘区',112.961,27.8578,'Yuetang',NULL,NULL),(430321,'湘潭县',430300,'湘潭',3,'0731','411228','中国,湖南省,湘潭市,湘潭县',112.951,27.7789,'Xiangtan',NULL,NULL),(430381,'湘乡市',430300,'湘乡',3,'0731','411400','中国,湖南省,湘潭市,湘乡市',112.535,27.7354,'Xiangxiang',NULL,NULL),(430382,'韶山市',430300,'韶山',3,'0731','411300','中国,湖南省,湘潭市,韶山市',112.527,27.915,'Shaoshan',NULL,NULL),(430400,'衡阳市',430000,'衡阳',2,'0734','421001','中国,湖南省,衡阳市',112.608,26.9004,'Hengyang',NULL,NULL),(430405,'珠晖区',430400,'珠晖',3,'0734','421002','中国,湖南省,衡阳市,珠晖区',112.621,26.8936,'Zhuhui',NULL,NULL),(430406,'雁峰区',430400,'雁峰',3,'0734','421001','中国,湖南省,衡阳市,雁峰区',112.617,26.8887,'Yanfeng',NULL,NULL),(430407,'石鼓区',430400,'石鼓',3,'0734','421005','中国,湖南省,衡阳市,石鼓区',112.611,26.9023,'Shigu',NULL,NULL),(430408,'蒸湘区',430400,'蒸湘',3,'0734','421001','中国,湖南省,衡阳市,蒸湘区',112.603,26.8965,'Zhengxiang',NULL,NULL),(430412,'南岳区',430400,'南岳',3,'0734','421900','中国,湖南省,衡阳市,南岳区',112.738,27.2326,'Nanyue',NULL,NULL),(430421,'衡阳县',430400,'衡阳',3,'0734','421200','中国,湖南省,衡阳市,衡阳县',112.371,26.9706,'Hengyang',NULL,NULL),(430422,'衡南县',430400,'衡南',3,'0734','421131','中国,湖南省,衡阳市,衡南县',112.678,26.7383,'Hengnan',NULL,NULL),(430423,'衡山县',430400,'衡山',3,'0734','421300','中国,湖南省,衡阳市,衡山县',112.868,27.2313,'Hengshan',NULL,NULL),(430424,'衡东县',430400,'衡东',3,'0734','421400','中国,湖南省,衡阳市,衡东县',112.948,27.0809,'Hengdong',NULL,NULL),(430426,'祁东县',430400,'祁东',3,'0734','421600','中国,湖南省,衡阳市,祁东县',112.09,26.7996,'Qidong',NULL,NULL),(430481,'耒阳市',430400,'耒阳',3,'0734','421800','中国,湖南省,衡阳市,耒阳市',112.86,26.4213,'Leiyang',NULL,NULL),(430482,'常宁市',430400,'常宁',3,'0734','421500','中国,湖南省,衡阳市,常宁市',112.401,26.4069,'Changning',NULL,NULL),(430500,'邵阳市',430000,'邵阳',2,'0739','422000','中国,湖南省,邵阳市',111.469,27.2378,'Shaoyang',NULL,NULL),(430502,'双清区',430500,'双清',3,'0739','422001','中国,湖南省,邵阳市,双清区',111.497,27.2329,'Shuangqing',NULL,NULL),(430503,'大祥区',430500,'大祥',3,'0739','422000','中国,湖南省,邵阳市,大祥区',111.454,27.2333,'Daxiang',NULL,NULL),(430511,'北塔区',430500,'北塔',3,'0739','422007','中国,湖南省,邵阳市,北塔区',111.452,27.2465,'Beita',NULL,NULL),(430521,'邵东县',430500,'邵东',3,'0739','422800','中国,湖南省,邵阳市,邵东县',111.744,27.2584,'Shaodong',NULL,NULL),(430522,'新邵县',430500,'新邵',3,'0739','422900','中国,湖南省,邵阳市,新邵县',111.461,27.3217,'Xinshao',NULL,NULL),(430523,'邵阳县',430500,'邵阳',3,'0739','422100','中国,湖南省,邵阳市,邵阳县',111.275,26.9914,'Shaoyang',NULL,NULL),(430524,'隆回县',430500,'隆回',3,'0739','422200','中国,湖南省,邵阳市,隆回县',111.032,27.1094,'Longhui',NULL,NULL),(430525,'洞口县',430500,'洞口',3,'0739','422300','中国,湖南省,邵阳市,洞口县',110.574,27.0546,'Dongkou',NULL,NULL),(430527,'绥宁县',430500,'绥宁',3,'0739','422600','中国,湖南省,邵阳市,绥宁县',110.156,26.5864,'Suining',NULL,NULL),(430528,'新宁县',430500,'新宁',3,'0739','422700','中国,湖南省,邵阳市,新宁县',110.851,26.4294,'Xinning',NULL,NULL),(430529,'城步苗族自治县',430500,'城步',3,'0739','422500','中国,湖南省,邵阳市,城步苗族自治县',110.322,26.3905,'Chengbu',NULL,NULL),(430581,'武冈市',430500,'武冈',3,'0739','422400','中国,湖南省,邵阳市,武冈市',110.633,26.7282,'Wugang',NULL,NULL),(430600,'岳阳市',430000,'岳阳',2,'0730','414000','中国,湖南省,岳阳市',113.133,29.3703,'Yueyang',NULL,NULL),(430602,'岳阳楼区',430600,'岳阳楼',3,'0730','414000','中国,湖南省,岳阳市,岳阳楼区',113.129,29.3719,'Yueyanglou',NULL,NULL),(430603,'云溪区',430600,'云溪',3,'0730','414009','中国,湖南省,岳阳市,云溪区',113.277,29.4736,'Yunxi',NULL,NULL),(430611,'君山区',430600,'君山',3,'0730','414005','中国,湖南省,岳阳市,君山区',113.004,29.4594,'Junshan',NULL,NULL),(430621,'岳阳县',430600,'岳阳',3,'0730','414100','中国,湖南省,岳阳市,岳阳县',113.12,29.1431,'Yueyang',NULL,NULL),(430623,'华容县',430600,'华容',3,'0730','414200','中国,湖南省,岳阳市,华容县',112.541,29.5302,'Huarong',NULL,NULL),(430624,'湘阴县',430600,'湘阴',3,'0730','414600','中国,湖南省,岳阳市,湘阴县',112.909,28.6892,'Xiangyin',NULL,NULL),(430626,'平江县',430600,'平江',3,'0730','414500','中国,湖南省,岳阳市,平江县',113.581,28.7066,'Pingjiang',NULL,NULL),(430681,'汨罗市',430600,'汨罗',3,'0730','414400','中国,湖南省,岳阳市,汨罗市',113.067,28.8063,'Miluo',NULL,NULL),(430682,'临湘市',430600,'临湘',3,'0730','414300','中国,湖南省,岳阳市,临湘市',113.45,29.477,'Linxiang',NULL,NULL),(430700,'常德市',430000,'常德',2,'0736','415000','中国,湖南省,常德市',111.691,29.0402,'Changde',NULL,NULL),(430702,'武陵区',430700,'武陵',3,'0736','415000','中国,湖南省,常德市,武陵区',111.698,29.0288,'Wuling',NULL,NULL),(430703,'鼎城区',430700,'鼎城',3,'0736','415101','中国,湖南省,常德市,鼎城区',111.681,29.0186,'Dingcheng',NULL,NULL),(430721,'安乡县',430700,'安乡',3,'0736','415600','中国,湖南省,常德市,安乡县',112.167,29.4133,'Anxiang',NULL,NULL),(430722,'汉寿县',430700,'汉寿',3,'0736','415900','中国,湖南省,常德市,汉寿县',111.967,28.903,'Hanshou',NULL,NULL),(430723,'澧县',430700,'澧县',3,'0736','415500','中国,湖南省,常德市,澧县',111.759,29.6332,'Lixian',NULL,NULL),(430724,'临澧县',430700,'临澧',3,'0736','415200','中国,湖南省,常德市,临澧县',111.652,29.4416,'Linli',NULL,NULL),(430725,'桃源县',430700,'桃源',3,'0736','415700','中国,湖南省,常德市,桃源县',111.489,28.9047,'Taoyuan',NULL,NULL),(430726,'石门县',430700,'石门',3,'0736','415300','中国,湖南省,常德市,石门县',111.38,29.5842,'Shimen',NULL,NULL),(430781,'津市市',430700,'津市',3,'0736','415400','中国,湖南省,常德市,津市市',111.878,29.6056,'Jinshi',NULL,NULL),(430800,'张家界市',430000,'张家界',2,'0744','427000','中国,湖南省,张家界市',110.48,29.1274,'Zhangjiajie',NULL,NULL),(430802,'永定区',430800,'永定',3,'0744','427000','中国,湖南省,张家界市,永定区',110.475,29.1339,'Yongding',NULL,NULL),(430811,'武陵源区',430800,'武陵源',3,'0744','427400','中国,湖南省,张家界市,武陵源区',110.55,29.3457,'Wulingyuan',NULL,NULL),(430821,'慈利县',430800,'慈利',3,'0744','427200','中国,湖南省,张家界市,慈利县',111.139,29.4299,'Cili',NULL,NULL),(430822,'桑植县',430800,'桑植',3,'0744','427100','中国,湖南省,张家界市,桑植县',110.163,29.3981,'Sangzhi',NULL,NULL),(430900,'益阳市',430000,'益阳',2,'0737','413000','中国,湖南省,益阳市',112.355,28.5701,'Yiyang',NULL,NULL),(430902,'资阳区',430900,'资阳',3,'0737','413001','中国,湖南省,益阳市,资阳区',112.324,28.591,'Ziyang',NULL,NULL),(430903,'赫山区',430900,'赫山',3,'0737','413002','中国,湖南省,益阳市,赫山区',112.373,28.5742,'Heshan',NULL,NULL),(430921,'南县',430900,'南县',3,'0737','413200','中国,湖南省,益阳市,南县',112.396,29.3616,'Nanxian',NULL,NULL),(430922,'桃江县',430900,'桃江',3,'0737','413400','中国,湖南省,益阳市,桃江县',112.156,28.5181,'Taojiang',NULL,NULL),(430923,'安化县',430900,'安化',3,'0737','413500','中国,湖南省,益阳市,安化县',111.213,28.3742,'Anhua',NULL,NULL),(430981,'沅江市',430900,'沅江',3,'0737','413100','中国,湖南省,益阳市,沅江市',112.354,28.844,'Yuanjiang',NULL,NULL),(431000,'郴州市',430000,'郴州',2,'0735','423000','中国,湖南省,郴州市',113.032,25.7936,'Chenzhou',NULL,NULL),(431002,'北湖区',431000,'北湖',3,'0735','423000','中国,湖南省,郴州市,北湖区',113.011,25.784,'Beihu',NULL,NULL),(431003,'苏仙区',431000,'苏仙',3,'0735','423000','中国,湖南省,郴州市,苏仙区',113.042,25.8004,'Suxian',NULL,NULL),(431021,'桂阳县',431000,'桂阳',3,'0735','424400','中国,湖南省,郴州市,桂阳县',112.734,25.7541,'Guiyang',NULL,NULL),(431022,'宜章县',431000,'宜章',3,'0735','424200','中国,湖南省,郴州市,宜章县',112.951,25.3993,'Yizhang',NULL,NULL),(431023,'永兴县',431000,'永兴',3,'0735','423300','中国,湖南省,郴州市,永兴县',113.112,26.1265,'Yongxing',NULL,NULL),(431024,'嘉禾县',431000,'嘉禾',3,'0735','424500','中国,湖南省,郴州市,嘉禾县',112.369,25.5879,'Jiahe',NULL,NULL),(431025,'临武县',431000,'临武',3,'0735','424300','中国,湖南省,郴州市,临武县',112.564,25.276,'Linwu',NULL,NULL),(431026,'汝城县',431000,'汝城',3,'0735','424100','中国,湖南省,郴州市,汝城县',113.686,25.552,'Rucheng',NULL,NULL),(431027,'桂东县',431000,'桂东',3,'0735','423500','中国,湖南省,郴州市,桂东县',113.947,26.0799,'Guidong',NULL,NULL),(431028,'安仁县',431000,'安仁',3,'0735','423600','中国,湖南省,郴州市,安仁县',113.269,26.7093,'Anren',NULL,NULL),(431081,'资兴市',431000,'资兴',3,'0735','423400','中国,湖南省,郴州市,资兴市',113.237,25.9767,'Zixing',NULL,NULL),(431100,'永州市',430000,'永州',2,'0746','425000','中国,湖南省,永州市',111.608,26.4345,'Yongzhou',NULL,NULL),(431102,'零陵区',431100,'零陵',3,'0746','425100','中国,湖南省,永州市,零陵区',111.621,26.2211,'Lingling',NULL,NULL),(431103,'冷水滩区',431100,'冷水滩',3,'0746','425100','中国,湖南省,永州市,冷水滩区',111.592,26.4611,'Lengshuitan',NULL,NULL),(431121,'祁阳县',431100,'祁阳',3,'0746','426100','中国,湖南省,永州市,祁阳县',111.84,26.5801,'Qiyang',NULL,NULL),(431122,'东安县',431100,'东安',3,'0746','425900','中国,湖南省,永州市,东安县',111.316,26.392,'Dong\'an',NULL,NULL),(431123,'双牌县',431100,'双牌',3,'0746','425200','中国,湖南省,永州市,双牌县',111.659,25.9599,'Shuangpai',NULL,NULL),(431124,'道县',431100,'道县',3,'0746','425300','中国,湖南省,永州市,道县',111.602,25.5277,'Daoxian',NULL,NULL),(431125,'江永县',431100,'江永',3,'0746','425400','中国,湖南省,永州市,江永县',111.341,25.2723,'Jiangyong',NULL,NULL),(431126,'宁远县',431100,'宁远',3,'0746','425600','中国,湖南省,永州市,宁远县',111.946,25.5691,'Ningyuan',NULL,NULL),(431127,'蓝山县',431100,'蓝山',3,'0746','425800','中国,湖南省,永州市,蓝山县',112.194,25.3679,'Lanshan',NULL,NULL),(431128,'新田县',431100,'新田',3,'0746','425700','中国,湖南省,永州市,新田县',112.221,25.9095,'Xintian',NULL,NULL),(431129,'江华瑶族自治县',431100,'江华',3,'0746','425500','中国,湖南省,永州市,江华瑶族自治县',111.588,25.1845,'Jianghua',NULL,NULL),(431200,'怀化市',430000,'怀化',2,'0745','418000','中国,湖南省,怀化市',109.978,27.5501,'Huaihua',NULL,NULL),(431202,'鹤城区',431200,'鹤城',3,'0745','418000','中国,湖南省,怀化市,鹤城区',109.965,27.5494,'Hecheng',NULL,NULL),(431221,'中方县',431200,'中方',3,'0745','418005','中国,湖南省,怀化市,中方县',109.945,27.4399,'Zhongfang',NULL,NULL),(431222,'沅陵县',431200,'沅陵',3,'0745','419600','中国,湖南省,怀化市,沅陵县',110.396,28.4555,'Yuanling',NULL,NULL),(431223,'辰溪县',431200,'辰溪',3,'0745','419500','中国,湖南省,怀化市,辰溪县',110.189,28.0041,'Chenxi',NULL,NULL),(431224,'溆浦县',431200,'溆浦',3,'0745','419300','中国,湖南省,怀化市,溆浦县',110.594,27.9084,'Xupu',NULL,NULL),(431225,'会同县',431200,'会同',3,'0745','418300','中国,湖南省,怀化市,会同县',109.736,26.8872,'Huitong',NULL,NULL),(431226,'麻阳苗族自治县',431200,'麻阳',3,'0745','419400','中国,湖南省,怀化市,麻阳苗族自治县',109.802,27.866,'Mayang',NULL,NULL),(431227,'新晃侗族自治县',431200,'新晃',3,'0745','419200','中国,湖南省,怀化市,新晃侗族自治县',109.172,27.3594,'Xinhuang',NULL,NULL),(431228,'芷江侗族自治县',431200,'芷江',3,'0745','419100','中国,湖南省,怀化市,芷江侗族自治县',109.685,27.443,'Zhijiang',NULL,NULL),(431229,'靖州苗族侗族自治县',431200,'靖州',3,'0745','418400','中国,湖南省,怀化市,靖州苗族侗族自治县',109.698,26.5765,'Jingzhou',NULL,NULL),(431230,'通道侗族自治县',431200,'通道',3,'0745','418500','中国,湖南省,怀化市,通道侗族自治县',109.785,26.1571,'Tongdao',NULL,NULL),(431281,'洪江市',431200,'洪江',3,'0745','418100','中国,湖南省,怀化市,洪江市',109.837,27.2092,'Hongjiang',NULL,NULL),(431300,'娄底市',430000,'娄底',2,'0738','417000','中国,湖南省,娄底市',112.008,27.7281,'Loudi',NULL,NULL),(431302,'娄星区',431300,'娄星',3,'0738','417000','中国,湖南省,娄底市,娄星区',112.002,27.7299,'Louxing',NULL,NULL),(431321,'双峰县',431300,'双峰',3,'0738','417700','中国,湖南省,娄底市,双峰县',112.199,27.4542,'Shuangfeng',NULL,NULL),(431322,'新化县',431300,'新化',3,'0738','417600','中国,湖南省,娄底市,新化县',111.327,27.7266,'Xinhua',NULL,NULL),(431381,'冷水江市',431300,'冷水江',3,'0738','417500','中国,湖南省,娄底市,冷水江市',111.436,27.6815,'Lengshuijiang',NULL,NULL),(431382,'涟源市',431300,'涟源',3,'0738','417100','中国,湖南省,娄底市,涟源市',111.672,27.6883,'Lianyuan',NULL,NULL),(433100,'湘西土家族苗族自治州',430000,'湘西',2,'0743','416000','中国,湖南省,湘西土家族苗族自治州',109.74,28.3143,'Xiangxi',NULL,NULL),(433101,'吉首市',433100,'吉首',3,'0743','416000','中国,湖南省,湘西土家族苗族自治州,吉首市',109.698,28.2625,'Jishou',NULL,NULL),(433122,'泸溪县',433100,'泸溪',3,'0743','416100','中国,湖南省,湘西土家族苗族自治州,泸溪县',110.217,28.2205,'Luxi',NULL,NULL),(433123,'凤凰县',433100,'凤凰',3,'0743','416200','中国,湖南省,湘西土家族苗族自治州,凤凰县',109.602,27.9482,'Fenghuang',NULL,NULL),(433124,'花垣县',433100,'花垣',3,'0743','416400','中国,湖南省,湘西土家族苗族自治州,花垣县',109.482,28.5721,'Huayuan',NULL,NULL),(433125,'保靖县',433100,'保靖',3,'0743','416500','中国,湖南省,湘西土家族苗族自治州,保靖县',109.66,28.7,'Baojing',NULL,NULL),(433126,'古丈县',433100,'古丈',3,'0743','416300','中国,湖南省,湘西土家族苗族自治州,古丈县',109.948,28.6194,'Guzhang',NULL,NULL),(433127,'永顺县',433100,'永顺',3,'0743','416700','中国,湖南省,湘西土家族苗族自治州,永顺县',109.853,29.001,'Yongshun',NULL,NULL),(433130,'龙山县',433100,'龙山',3,'0743','416800','中国,湖南省,湘西土家族苗族自治州,龙山县',109.443,29.4569,'Longshan',NULL,NULL),(440000,'广东省',100000,'广东',1,'','','中国,广东省',113.281,23.1252,'Guangdong',NULL,NULL),(440100,'广州市',440000,'广州',2,'020','510032','中国,广东省,广州市',113.281,23.1252,'Guangzhou',NULL,NULL),(440103,'荔湾区',440100,'荔湾',3,'020','510170','中国,广东省,广州市,荔湾区',113.244,23.1259,'Liwan',NULL,NULL),(440104,'越秀区',440100,'越秀',3,'020','510030','中国,广东省,广州市,越秀区',113.267,23.129,'Yuexiu',NULL,NULL),(440105,'海珠区',440100,'海珠',3,'020','510300','中国,广东省,广州市,海珠区',113.262,23.1038,'Haizhu',NULL,NULL),(440106,'天河区',440100,'天河',3,'020','510665','中国,广东省,广州市,天河区',113.361,23.1247,'Tianhe',NULL,NULL),(440111,'白云区',440100,'白云',3,'020','510405','中国,广东省,广州市,白云区',113.273,23.1579,'Baiyun',NULL,NULL),(440112,'黄埔区',440100,'黄埔',3,'020','510700','中国,广东省,广州市,黄埔区',113.459,23.1064,'Huangpu',NULL,NULL),(440113,'番禺区',440100,'番禺',3,'020','511400','中国,广东省,广州市,番禺区',113.384,22.936,'Panyu',NULL,NULL),(440114,'花都区',440100,'花都',3,'020','510800','中国,广东省,广州市,花都区',113.22,23.4036,'Huadu',NULL,NULL),(440115,'南沙区',440100,'南沙',3,'020','511458','中国,广东省,广州市,南沙区',113.608,22.7714,'Nansha',NULL,NULL),(440117,'从化区',440100,'从化',3,'020','510900','中国,广东省,广州市,从化区',113.587,23.5453,'Conghua',NULL,NULL),(440118,'增城区',440100,'增城',3,'020','511300','中国,广东省,广州市,增城区',113.83,23.2905,'Zengcheng',NULL,NULL),(440200,'韶关市',440000,'韶关',2,'0751','512002','中国,广东省,韶关市',113.592,24.8013,'Shaoguan',NULL,NULL),(440203,'武江区',440200,'武江',3,'0751','512026','中国,广东省,韶关市,武江区',113.588,24.7926,'Wujiang',NULL,NULL),(440204,'浈江区',440200,'浈江',3,'0751','512023','中国,广东省,韶关市,浈江区',113.611,24.8044,'Zhenjiang',NULL,NULL),(440205,'曲江区',440200,'曲江',3,'0751','512101','中国,广东省,韶关市,曲江区',113.602,24.6791,'Qujiang',NULL,NULL),(440222,'始兴县',440200,'始兴',3,'0751','512500','中国,广东省,韶关市,始兴县',114.068,24.9476,'Shixing',NULL,NULL),(440224,'仁化县',440200,'仁化',3,'0751','512300','中国,广东省,韶关市,仁化县',113.747,25.0874,'Renhua',NULL,NULL),(440229,'翁源县',440200,'翁源',3,'0751','512600','中国,广东省,韶关市,翁源县',114.134,24.3495,'Wengyuan',NULL,NULL),(440232,'乳源瑶族自治县',440200,'乳源',3,'0751','512700','中国,广东省,韶关市,乳源瑶族自治县',113.277,24.778,'Ruyuan',NULL,NULL),(440233,'新丰县',440200,'新丰',3,'0751','511100','中国,广东省,韶关市,新丰县',114.208,24.0592,'Xinfeng',NULL,NULL),(440281,'乐昌市',440200,'乐昌',3,'0751','512200','中国,广东省,韶关市,乐昌市',113.357,25.128,'Lechang',NULL,NULL),(440282,'南雄市',440200,'南雄',3,'0751','512400','中国,广东省,韶关市,南雄市',114.31,25.1171,'Nanxiong',NULL,NULL),(440300,'深圳市',440000,'深圳',2,'0755','518035','中国,广东省,深圳市',114.086,22.547,'Shenzhen',NULL,NULL),(440303,'罗湖区',440300,'罗湖',3,'0755','518021','中国,广东省,深圳市,罗湖区',114.131,22.5484,'Luohu',NULL,NULL),(440304,'福田区',440300,'福田',3,'0755','518048','中国,广东省,深圳市,福田区',114.056,22.5224,'Futian',NULL,NULL),(440305,'南山区',440300,'南山',3,'0755','518051','中国,广东省,深圳市,南山区',113.93,22.5329,'Nanshan',NULL,NULL),(440306,'宝安区',440300,'宝安',3,'0755','518101','中国,广东省,深圳市,宝安区',113.883,22.5537,'Bao\'an',NULL,NULL),(440307,'龙岗区',440300,'龙岗',3,'0755','518172','中国,广东省,深圳市,龙岗区',114.248,22.7199,'Longgang',NULL,NULL),(440308,'盐田区',440300,'盐田',3,'0755','518081','中国,广东省,深圳市,盐田区',114.237,22.5578,'Yantian',NULL,NULL),(440309,'光明新区',440300,'光明新区',3,'0755','518100','中国,广东省,深圳市,光明新区',113.896,22.7773,'Guangmingxinqu',NULL,NULL),(440310,'坪山新区',440300,'坪山新区',3,'0755','518000','中国,广东省,深圳市,坪山新区',114.346,22.6905,'Pingshanxinqu',NULL,NULL),(440311,'大鹏新区',440300,'大鹏新区',3,'0755','518000','中国,广东省,深圳市,大鹏新区',114.48,22.5879,'Dapengxinqu',NULL,NULL),(440312,'龙华新区',440300,'龙华新区',3,'0755','518100','中国,广东省,深圳市,龙华新区',114.037,22.687,'Longhuaxinqu',NULL,NULL),(440400,'珠海市',440000,'珠海',2,'0756','519000','中国,广东省,珠海市',113.553,22.2559,'Zhuhai',NULL,NULL),(440402,'香洲区',440400,'香洲',3,'0756','519000','中国,广东省,珠海市,香洲区',113.544,22.2665,'Xiangzhou',NULL,NULL),(440403,'斗门区',440400,'斗门',3,'0756','519110','中国,广东省,珠海市,斗门区',113.296,22.209,'Doumen',NULL,NULL),(440404,'金湾区',440400,'金湾',3,'0756','519040','中国,广东省,珠海市,金湾区',113.364,22.1469,'Jinwan',NULL,NULL),(440500,'汕头市',440000,'汕头',2,'0754','515041','中国,广东省,汕头市',116.708,23.371,'Shantou',NULL,NULL),(440507,'龙湖区',440500,'龙湖',3,'0754','515041','中国,广东省,汕头市,龙湖区',116.716,23.3717,'Longhu',NULL,NULL),(440511,'金平区',440500,'金平',3,'0754','515041','中国,广东省,汕头市,金平区',116.704,23.3664,'Jinping',NULL,NULL),(440512,'濠江区',440500,'濠江',3,'0754','515071','中国,广东省,汕头市,濠江区',116.727,23.2859,'Haojiang',NULL,NULL),(440513,'潮阳区',440500,'潮阳',3,'0754','515100','中国,广东省,汕头市,潮阳区',116.602,23.2649,'Chaoyang',NULL,NULL),(440514,'潮南区',440500,'潮南',3,'0754','515144','中国,广东省,汕头市,潮南区',116.432,23.25,'Chaonan',NULL,NULL),(440515,'澄海区',440500,'澄海',3,'0754','515800','中国,广东省,汕头市,澄海区',116.756,23.4673,'Chenghai',NULL,NULL),(440523,'南澳县',440500,'南澳',3,'0754','515900','中国,广东省,汕头市,南澳县',117.019,23.4223,'Nanao',NULL,NULL),(440600,'佛山市',440000,'佛山',2,'0757','528000','中国,广东省,佛山市',113.123,23.0288,'Foshan',NULL,NULL),(440604,'禅城区',440600,'禅城',3,'0757','528000','中国,广东省,佛山市,禅城区',113.123,23.0084,'Chancheng',NULL,NULL),(440605,'南海区',440600,'南海',3,'0757','528251','中国,广东省,佛山市,南海区',113.143,23.0288,'Nanhai',NULL,NULL),(440606,'顺德区',440600,'顺德',3,'0757','528300','中国,广东省,佛山市,顺德区',113.294,22.8045,'Shunde',NULL,NULL),(440607,'三水区',440600,'三水',3,'0757','528133','中国,广东省,佛山市,三水区',112.897,23.1556,'Sanshui',NULL,NULL),(440608,'高明区',440600,'高明',3,'0757','528500','中国,广东省,佛山市,高明区',112.893,22.9002,'Gaoming',NULL,NULL),(440700,'江门市',440000,'江门',2,'0750','529000','中国,广东省,江门市',113.095,22.5904,'Jiangmen',NULL,NULL),(440703,'蓬江区',440700,'蓬江',3,'0750','529000','中国,广东省,江门市,蓬江区',113.078,22.5951,'Pengjiang',NULL,NULL),(440704,'江海区',440700,'江海',3,'0750','529040','中国,广东省,江门市,江海区',113.111,22.5602,'Jianghai',NULL,NULL),(440705,'新会区',440700,'新会',3,'0750','529100','中国,广东省,江门市,新会区',113.032,22.4588,'Xinhui',NULL,NULL),(440781,'台山市',440700,'台山',3,'0750','529200','中国,广东省,江门市,台山市',112.794,22.2515,'Taishan',NULL,NULL),(440783,'开平市',440700,'开平',3,'0750','529337','中国,广东省,江门市,开平市',112.698,22.3762,'Kaiping',NULL,NULL),(440784,'鹤山市',440700,'鹤山',3,'0750','529700','中国,广东省,江门市,鹤山市',112.964,22.7652,'Heshan',NULL,NULL),(440785,'恩平市',440700,'恩平',3,'0750','529400','中国,广东省,江门市,恩平市',112.305,22.1829,'Enping',NULL,NULL),(440800,'湛江市',440000,'湛江',2,'0759','524047','中国,广东省,湛江市',110.406,21.1953,'Zhanjiang',NULL,NULL),(440802,'赤坎区',440800,'赤坎',3,'0759','524033','中国,广东省,湛江市,赤坎区',110.366,21.2661,'Chikan',NULL,NULL),(440803,'霞山区',440800,'霞山',3,'0759','524011','中国,广东省,湛江市,霞山区',110.398,21.1918,'Xiashan',NULL,NULL),(440804,'坡头区',440800,'坡头',3,'0759','524057','中国,广东省,湛江市,坡头区',110.455,21.2447,'Potou',NULL,NULL),(440811,'麻章区',440800,'麻章',3,'0759','524094','中国,广东省,湛江市,麻章区',110.334,21.2633,'Mazhang',NULL,NULL),(440823,'遂溪县',440800,'遂溪',3,'0759','524300','中国,广东省,湛江市,遂溪县',110.25,21.3772,'Suixi',NULL,NULL),(440825,'徐闻县',440800,'徐闻',3,'0759','524100','中国,广东省,湛江市,徐闻县',110.174,20.3281,'Xuwen',NULL,NULL),(440881,'廉江市',440800,'廉江',3,'0759','524400','中国,广东省,湛江市,廉江市',110.284,21.6092,'Lianjiang',NULL,NULL),(440882,'雷州市',440800,'雷州',3,'0759','524200','中国,广东省,湛江市,雷州市',110.101,20.9143,'Leizhou',NULL,NULL),(440883,'吴川市',440800,'吴川',3,'0759','524500','中国,广东省,湛江市,吴川市',110.777,21.4458,'Wuchuan',NULL,NULL),(440900,'茂名市',440000,'茂名',2,'0668','525000','中国,广东省,茂名市',110.919,21.6598,'Maoming',NULL,NULL),(440902,'茂南区',440900,'茂南',3,'0668','525000','中国,广东省,茂名市,茂南区',110.919,21.641,'Maonan',NULL,NULL),(440904,'电白区',440900,'电白',3,'0668','525400','中国,广东省,茂名市,电白区',111.007,21.5072,'Dianbai',NULL,NULL),(440981,'高州市',440900,'高州',3,'0668','525200','中国,广东省,茂名市,高州市',110.855,21.9206,'Gaozhou',NULL,NULL),(440982,'化州市',440900,'化州',3,'0668','525100','中国,广东省,茂名市,化州市',110.639,21.6639,'Huazhou',NULL,NULL),(440983,'信宜市',440900,'信宜',3,'0668','525300','中国,广东省,茂名市,信宜市',110.946,22.3535,'Xinyi',NULL,NULL),(441200,'肇庆市',440000,'肇庆',2,'0758','526040','中国,广东省,肇庆市',112.473,23.0515,'Zhaoqing',NULL,NULL),(441202,'端州区',441200,'端州',3,'0758','526060','中国,广东省,肇庆市,端州区',112.485,23.0519,'Duanzhou',NULL,NULL),(441203,'鼎湖区',441200,'鼎湖',3,'0758','526070','中国,广东省,肇庆市,鼎湖区',112.566,23.1585,'Dinghu',NULL,NULL),(441223,'广宁县',441200,'广宁',3,'0758','526300','中国,广东省,肇庆市,广宁县',112.441,23.6346,'Guangning',NULL,NULL),(441224,'怀集县',441200,'怀集',3,'0758','526400','中国,广东省,肇庆市,怀集县',112.184,23.9092,'Huaiji',NULL,NULL),(441225,'封开县',441200,'封开',3,'0758','526500','中国,广东省,肇庆市,封开县',111.503,23.4357,'Fengkai',NULL,NULL),(441226,'德庆县',441200,'德庆',3,'0758','526600','中国,广东省,肇庆市,德庆县',111.786,23.1437,'Deqing',NULL,NULL),(441283,'高要市',441200,'高要',3,'0758','526100','中国,广东省,肇庆市,高要市',112.458,23.0258,'Gaoyao',NULL,NULL),(441284,'四会市',441200,'四会',3,'0758','526200','中国,广东省,肇庆市,四会市',112.734,23.3269,'Sihui',NULL,NULL),(441300,'惠州市',440000,'惠州',2,'0752','516000','中国,广东省,惠州市',114.413,23.0794,'Huizhou',NULL,NULL),(441302,'惠城区',441300,'惠城',3,'0752','516008','中国,广东省,惠州市,惠城区',114.383,23.0838,'Huicheng',NULL,NULL),(441303,'惠阳区',441300,'惠阳',3,'0752','516211','中国,广东省,惠州市,惠阳区',114.456,22.7885,'Huiyang',NULL,NULL),(441322,'博罗县',441300,'博罗',3,'0752','516100','中国,广东省,惠州市,博罗县',114.29,23.1731,'Boluo',NULL,NULL),(441323,'惠东县',441300,'惠东',3,'0752','516300','中国,广东省,惠州市,惠东县',114.72,22.9848,'Huidong',NULL,NULL),(441324,'龙门县',441300,'龙门',3,'0752','516800','中国,广东省,惠州市,龙门县',114.255,23.7276,'Longmen',NULL,NULL),(441400,'梅州市',440000,'梅州',2,'0753','514021','中国,广东省,梅州市',116.118,24.2991,'Meizhou',NULL,NULL),(441402,'梅江区',441400,'梅江',3,'0753','514000','中国,广东省,梅州市,梅江区',116.117,24.3106,'Meijiang',NULL,NULL),(441403,'梅县区',441400,'梅县',3,'0753','514787','中国,广东省,梅州市,梅县区',116.098,24.2867,'Meixian',NULL,NULL),(441422,'大埔县',441400,'大埔',3,'0753','514200','中国,广东省,梅州市,大埔县',116.697,24.3533,'Dabu',NULL,NULL),(441423,'丰顺县',441400,'丰顺',3,'0753','514300','中国,广东省,梅州市,丰顺县',116.182,23.7409,'Fengshun',NULL,NULL),(441424,'五华县',441400,'五华',3,'0753','514400','中国,广东省,梅州市,五华县',115.779,23.9242,'Wuhua',NULL,NULL),(441426,'平远县',441400,'平远',3,'0753','514600','中国,广东省,梅州市,平远县',115.896,24.5712,'Pingyuan',NULL,NULL),(441427,'蕉岭县',441400,'蕉岭',3,'0753','514100','中国,广东省,梅州市,蕉岭县',116.171,24.6573,'Jiaoling',NULL,NULL),(441481,'兴宁市',441400,'兴宁',3,'0753','514500','中国,广东省,梅州市,兴宁市',115.731,24.14,'Xingning',NULL,NULL),(441500,'汕尾市',440000,'汕尾',2,'0660','516600','中国,广东省,汕尾市',115.364,22.7745,'Shanwei',NULL,NULL),(441502,'城区',441500,'城区',3,'0660','516600','中国,广东省,汕尾市,城区',115.365,22.7789,'Chengqu',NULL,NULL),(441521,'海丰县',441500,'海丰',3,'0660','516400','中国,广东省,汕尾市,海丰县',115.323,22.9665,'Haifeng',NULL,NULL),(441523,'陆河县',441500,'陆河',3,'0660','516700','中国,广东省,汕尾市,陆河县',115.656,23.3036,'Luhe',NULL,NULL),(441581,'陆丰市',441500,'陆丰',3,'0660','516500','中国,广东省,汕尾市,陆丰市',115.648,22.9433,'Lufeng',NULL,NULL),(441600,'河源市',440000,'河源',2,'0762','517000','中国,广东省,河源市',114.698,23.7463,'Heyuan',NULL,NULL),(441602,'源城区',441600,'源城',3,'0762','517000','中国,广东省,河源市,源城区',114.702,23.7341,'Yuancheng',NULL,NULL),(441621,'紫金县',441600,'紫金',3,'0762','517400','中国,广东省,河源市,紫金县',115.184,23.6387,'Zijin',NULL,NULL),(441622,'龙川县',441600,'龙川',3,'0762','517300','中国,广东省,河源市,龙川县',115.26,24.1014,'Longchuan',NULL,NULL),(441623,'连平县',441600,'连平',3,'0762','517100','中国,广东省,河源市,连平县',114.49,24.3716,'Lianping',NULL,NULL),(441624,'和平县',441600,'和平',3,'0762','517200','中国,广东省,河源市,和平县',114.938,24.4432,'Heping',NULL,NULL),(441625,'东源县',441600,'东源',3,'0762','517583','中国,广东省,河源市,东源县',114.746,23.7883,'Dongyuan',NULL,NULL),(441700,'阳江市',440000,'阳江',2,'0662','529500','中国,广东省,阳江市',111.975,21.8592,'Yangjiang',NULL,NULL),(441702,'江城区',441700,'江城',3,'0662','529500','中国,广东省,阳江市,江城区',111.955,21.8619,'Jiangcheng',NULL,NULL),(441704,'阳东区',441700,'阳东',3,'0662','529900','中国,广东省,阳江市,阳东区',112.015,21.874,'Yangdong',NULL,NULL),(441721,'阳西县',441700,'阳西',3,'0662','529800','中国,广东省,阳江市,阳西县',111.618,21.7523,'Yangxi',NULL,NULL),(441781,'阳春市',441700,'阳春',3,'0662','529600','中国,广东省,阳江市,阳春市',111.789,22.1723,'Yangchun',NULL,NULL),(441800,'清远市',440000,'清远',2,'0763','511500','中国,广东省,清远市',113.037,23.7042,'Qingyuan',NULL,NULL),(441802,'清城区',441800,'清城',3,'0763','511515','中国,广东省,清远市,清城区',113.063,23.6978,'Qingcheng',NULL,NULL),(441803,'清新区',441800,'清新',3,'0763','511810','中国,广东省,清远市,清新区',113.015,23.7369,'Qingxin',NULL,NULL),(441821,'佛冈县',441800,'佛冈',3,'0763','511600','中国,广东省,清远市,佛冈县',113.533,23.8723,'Fogang',NULL,NULL),(441823,'阳山县',441800,'阳山',3,'0763','513100','中国,广东省,清远市,阳山县',112.641,24.4652,'Yangshan',NULL,NULL),(441825,'连山壮族瑶族自治县',441800,'连山',3,'0763','513200','中国,广东省,清远市,连山壮族瑶族自治县',112.08,24.5681,'Lianshan',NULL,NULL),(441826,'连南瑶族自治县',441800,'连南',3,'0763','513300','中国,广东省,清远市,连南瑶族自治县',112.288,24.7173,'Liannan',NULL,NULL),(441881,'英德市',441800,'英德',3,'0763','513000','中国,广东省,清远市,英德市',113.415,24.1857,'Yingde',NULL,NULL),(441882,'连州市',441800,'连州',3,'0763','513400','中国,广东省,清远市,连州市',112.382,24.7791,'Lianzhou',NULL,NULL),(441900,'东莞市',440000,'东莞',2,'0769','523888','中国,广东省,东莞市',113.76,23.0489,'Dongguan',NULL,NULL),(441901,'莞城区',441900,'莞城',3,'0769','523128','中国,广东省,东莞市,莞城区',113.751,23.0534,'Guancheng',NULL,NULL),(441902,'南城区',441900,'南城',3,'0769','523617','中国,广东省,东莞市,南城区',113.752,23.0202,'Nancheng',NULL,NULL),(441904,'万江区',441900,'万江',3,'0769','523039','中国,广东省,东莞市,万江区',113.739,23.0438,'Wanjiang',NULL,NULL),(441905,'石碣镇',441900,'石碣',3,'0769','523290','中国,广东省,东莞市,石碣镇',113.802,23.099,'Shijie',NULL,NULL),(441906,'石龙镇',441900,'石龙',3,'0769','523326','中国,广东省,东莞市,石龙镇',113.876,23.1074,'Shilong',NULL,NULL),(441907,'茶山镇',441900,'茶山',3,'0769','523380','中国,广东省,东莞市,茶山镇',113.884,23.0624,'Chashan',NULL,NULL),(441908,'石排镇',441900,'石排',3,'0769','523346','中国,广东省,东莞市,石排镇',113.92,23.0863,'Shipai',NULL,NULL),(441909,'企石镇',441900,'企石',3,'0769','523507','中国,广东省,东莞市,企石镇',114.013,23.066,'Qishi',NULL,NULL),(441910,'横沥镇',441900,'横沥',3,'0769','523471','中国,广东省,东莞市,横沥镇',113.957,23.0257,'Hengli',NULL,NULL),(441911,'桥头镇',441900,'桥头',3,'0769','523520','中国,广东省,东莞市,桥头镇',114.014,22.9397,'Qiaotou',NULL,NULL),(441912,'谢岗镇',441900,'谢岗',3,'0769','523592','中国,广东省,东莞市,谢岗镇',114.141,22.9597,'Xiegang',NULL,NULL),(441913,'东坑镇',441900,'东坑',3,'0769','523451','中国,广东省,东莞市,东坑镇',113.94,22.9928,'Dongkeng',NULL,NULL),(441914,'常平镇',441900,'常平',3,'0769','523560','中国,广东省,东莞市,常平镇',114.03,23.0161,'Changping',NULL,NULL),(441915,'寮步镇',441900,'寮步',3,'0769','523411','中国,广东省,东莞市,寮步镇',113.885,22.9917,'Liaobu',NULL,NULL),(441916,'大朗镇',441900,'大朗',3,'0769','523770','中国,广东省,东莞市,大朗镇',113.927,22.9657,'Dalang',NULL,NULL),(441917,'麻涌镇',441900,'麻涌',3,'0769','523143','中国,广东省,东莞市,麻涌镇',113.546,23.0453,'Machong',NULL,NULL),(441918,'中堂镇',441900,'中堂',3,'0769','523233','中国,广东省,东莞市,中堂镇',113.654,23.0902,'Zhongtang',NULL,NULL),(441919,'高埗镇',441900,'高埗',3,'0769','523282','中国,广东省,东莞市,高埗镇',113.736,23.0684,'Gaobu',NULL,NULL),(441920,'樟木头镇',441900,'樟木头',3,'0769','523619','中国,广东省,东莞市,樟木头镇',114.066,22.9567,'Zhangmutou',NULL,NULL),(441921,'大岭山镇',441900,'大岭山',3,'0769','523835','中国,广东省,东莞市,大岭山镇',113.783,22.8854,'Dalingshan',NULL,NULL),(441922,'望牛墩镇',441900,'望牛墩',3,'0769','523203','中国,广东省,东莞市,望牛墩镇',113.659,23.055,'Wangniudun',NULL,NULL),(441923,'黄江镇',441900,'黄江',3,'0769','523755','中国,广东省,东莞市,黄江镇',113.993,22.8775,'Huangjiang',NULL,NULL),(441924,'洪梅镇',441900,'洪梅',3,'0769','523163','中国,广东省,东莞市,洪梅镇',113.613,22.9927,'Hongmei',NULL,NULL),(441925,'清溪镇',441900,'清溪',3,'0769','523660','中国,广东省,东莞市,清溪镇',114.156,22.8445,'Qingxi',NULL,NULL),(441926,'沙田镇',441900,'沙田',3,'0769','523988','中国,广东省,东莞市,沙田镇',113.76,23.0489,'Shatian',NULL,NULL),(441927,'道滘镇',441900,'道滘',3,'0769','523171','中国,广东省,东莞市,道滘镇',113.76,23.0489,'Daojiao',NULL,NULL),(441928,'塘厦镇',441900,'塘厦',3,'0769','523713','中国,广东省,东莞市,塘厦镇',114.108,22.8229,'Tangxia',NULL,NULL),(441929,'虎门镇',441900,'虎门',3,'0769','523932','中国,广东省,东莞市,虎门镇',113.711,22.8262,'Humen',NULL,NULL),(441930,'厚街镇',441900,'厚街',3,'0769','523960','中国,广东省,东莞市,厚街镇',113.673,22.9408,'Houjie',NULL,NULL),(441931,'凤岗镇',441900,'凤岗',3,'0769','523690','中国,广东省,东莞市,凤岗镇',114.141,22.7446,'Fenggang',NULL,NULL),(441932,'长安镇',441900,'长安',3,'0769','523850','中国,广东省,东莞市,长安镇',113.804,22.8166,'Chang\'an',NULL,NULL),(442000,'中山市',440000,'中山',2,'0760','528403','中国,广东省,中山市',113.382,22.5211,'Zhongshan',NULL,NULL),(442001,'石岐区',442000,'石岐',3,'0760','528400','中国,广东省,中山市,石岐区',113.379,22.5252,'Shiqi',NULL,NULL),(442004,'南区',442000,'南区',3,'0760','528400','中国,广东省,中山市,南区',113.356,22.4866,'Nanqu',NULL,NULL),(442005,'五桂山区',442000,'五桂山',3,'0760','528458','中国,广东省,中山市,五桂山区',113.411,22.5197,'Wuguishan',NULL,NULL),(442006,'火炬开发区',442000,'火炬',3,'0760','528437','中国,广东省,中山市,火炬开发区',113.481,22.5661,'Huoju',NULL,NULL),(442007,'黄圃镇',442000,'黄圃',3,'0760','528429','中国,广东省,中山市,黄圃镇',113.342,22.7151,'Huangpu',NULL,NULL),(442008,'南头镇',442000,'南头',3,'0760','528421','中国,广东省,中山市,南头镇',113.296,22.7139,'Nantou',NULL,NULL),(442009,'东凤镇',442000,'东凤',3,'0760','528425','中国,广东省,中山市,东凤镇',113.261,22.6877,'Dongfeng',NULL,NULL),(442010,'阜沙镇',442000,'阜沙',3,'0760','528434','中国,广东省,中山市,阜沙镇',113.353,22.6664,'Fusha',NULL,NULL),(442011,'小榄镇',442000,'小榄',3,'0760','528415','中国,广东省,中山市,小榄镇',113.244,22.667,'Xiaolan',NULL,NULL),(442012,'东升镇',442000,'东升',3,'0760','528400','中国,广东省,中山市,东升镇',113.296,22.614,'Dongsheng',NULL,NULL),(442013,'古镇镇',442000,'古镇',3,'0760','528422','中国,广东省,中山市,古镇镇',113.18,22.611,'Guzhen',NULL,NULL),(442014,'横栏镇',442000,'横栏',3,'0760','528478','中国,广东省,中山市,横栏镇',113.266,22.5232,'Henglan',NULL,NULL),(442015,'三角镇',442000,'三角',3,'0760','528422','中国,广东省,中山市,三角镇',113.424,22.677,'Sanjiao',NULL,NULL),(442016,'民众镇',442000,'民众',3,'0760','528441','中国,广东省,中山市,民众镇',113.486,22.6235,'Minzhong',NULL,NULL),(442017,'南朗镇',442000,'南朗',3,'0760','528454','中国,广东省,中山市,南朗镇',113.534,22.4924,'Nanlang',NULL,NULL),(442018,'港口镇',442000,'港口',3,'0760','528447','中国,广东省,中山市,港口镇',113.382,22.5211,'Gangkou',NULL,NULL),(442019,'大涌镇',442000,'大涌',3,'0760','528476','中国,广东省,中山市,大涌镇',113.292,22.4677,'Dayong',NULL,NULL),(442020,'沙溪镇',442000,'沙溪',3,'0760','528471','中国,广东省,中山市,沙溪镇',113.328,22.5263,'Shaxi',NULL,NULL),(442021,'三乡镇',442000,'三乡',3,'0760','528463','中国,广东省,中山市,三乡镇',113.433,22.3525,'Sanxiang',NULL,NULL),(442022,'板芙镇',442000,'板芙',3,'0760','528459','中国,广东省,中山市,板芙镇',113.32,22.4157,'Banfu',NULL,NULL),(442023,'神湾镇',442000,'神湾',3,'0760','528462','中国,广东省,中山市,神湾镇',113.359,22.3125,'Shenwan',NULL,NULL),(442024,'坦洲镇',442000,'坦洲',3,'0760','528467','中国,广东省,中山市,坦洲镇',113.486,22.2613,'Tanzhou',NULL,NULL),(445100,'潮州市',440000,'潮州',2,'0768','521000','中国,广东省,潮州市',116.632,23.6617,'Chaozhou',NULL,NULL),(445102,'湘桥区',445100,'湘桥',3,'0768','521000','中国,广东省,潮州市,湘桥区',116.628,23.6745,'Xiangqiao',NULL,NULL),(445103,'潮安区',445100,'潮安',3,'0768','515638','中国,广东省,潮州市,潮安区',116.593,23.6437,'Chao\'an',NULL,NULL),(445122,'饶平县',445100,'饶平',3,'0768','515700','中国,广东省,潮州市,饶平县',117.007,23.6699,'Raoping',NULL,NULL),(445200,'揭阳市',440000,'揭阳',2,'0633','522000','中国,广东省,揭阳市',116.356,23.5438,'Jieyang',NULL,NULL),(445202,'榕城区',445200,'榕城',3,'0633','522000','中国,广东省,揭阳市,榕城区',116.367,23.5251,'Rongcheng',NULL,NULL),(445203,'揭东区',445200,'揭东',3,'0633','515500','中国,广东省,揭阳市,揭东区',116.413,23.5699,'Jiedong',NULL,NULL),(445222,'揭西县',445200,'揭西',3,'0633','515400','中国,广东省,揭阳市,揭西县',115.839,23.4271,'Jiexi',NULL,NULL),(445224,'惠来县',445200,'惠来',3,'0633','515200','中国,广东省,揭阳市,惠来县',116.296,23.0329,'Huilai',NULL,NULL),(445281,'普宁市',445200,'普宁',3,'0633','515300','中国,广东省,揭阳市,普宁市',116.166,23.2973,'Puning',NULL,NULL),(445300,'云浮市',440000,'云浮',2,'0766','527300','中国,广东省,云浮市',112.044,22.9298,'Yunfu',NULL,NULL),(445302,'云城区',445300,'云城',3,'0766','527300','中国,广东省,云浮市,云城区',112.039,22.93,'Yuncheng',NULL,NULL),(445303,'云安区',445300,'云安',3,'0766','527500','中国,广东省,云浮市,云安区',112.009,23.0778,'Yun\'an',NULL,NULL),(445321,'新兴县',445300,'新兴',3,'0766','527400','中国,广东省,云浮市,新兴县',112.23,22.6973,'Xinxing',NULL,NULL),(445322,'郁南县',445300,'郁南',3,'0766','527100','中国,广东省,云浮市,郁南县',111.534,23.2331,'Yunan',NULL,NULL),(445381,'罗定市',445300,'罗定',3,'0766','527200','中国,广东省,云浮市,罗定市',111.57,22.7697,'Luoding',NULL,NULL),(450000,'广西壮族自治区',100000,'广西',1,'','','中国,广西壮族自治区',108.32,22.824,'Guangxi',NULL,NULL),(450100,'南宁市',450000,'南宁',2,'0771','530028','中国,广西壮族自治区,南宁市',108.32,22.824,'Nanning',NULL,NULL),(450102,'兴宁区',450100,'兴宁',3,'0771','530023','中国,广西壮族自治区,南宁市,兴宁区',108.367,22.8535,'Xingning',NULL,NULL),(450103,'青秀区',450100,'青秀',3,'0771','530213','中国,广西壮族自治区,南宁市,青秀区',108.495,22.7851,'Qingxiu',NULL,NULL),(450105,'江南区',450100,'江南',3,'0771','530031','中国,广西壮族自治区,南宁市,江南区',108.273,22.7813,'Jiangnan',NULL,NULL),(450107,'西乡塘区',450100,'西乡塘',3,'0771','530001','中国,广西壮族自治区,南宁市,西乡塘区',108.313,22.8339,'Xixiangtang',NULL,NULL),(450108,'良庆区',450100,'良庆',3,'0771','530219','中国,广西壮族自治区,南宁市,良庆区',108.413,22.7491,'Liangqing',NULL,NULL),(450109,'邕宁区',450100,'邕宁',3,'0771','530200','中国,广西壮族自治区,南宁市,邕宁区',108.487,22.7563,'Yongning',NULL,NULL),(450122,'武鸣县',450100,'武鸣',3,'0771','530100','中国,广西壮族自治区,南宁市,武鸣县',108.277,23.1564,'Wuming',NULL,NULL),(450123,'隆安县',450100,'隆安',3,'0771','532700','中国,广西壮族自治区,南宁市,隆安县',107.692,23.1734,'Long\'an',NULL,NULL),(450124,'马山县',450100,'马山',3,'0771','530600','中国,广西壮族自治区,南宁市,马山县',108.177,23.7093,'Mashan',NULL,NULL),(450125,'上林县',450100,'上林',3,'0771','530500','中国,广西壮族自治区,南宁市,上林县',108.605,23.432,'Shanglin',NULL,NULL),(450126,'宾阳县',450100,'宾阳',3,'0771','530400','中国,广西壮族自治区,南宁市,宾阳县',108.812,23.2196,'Binyang',NULL,NULL),(450127,'横县',450100,'横县',3,'0771','530300','中国,广西壮族自治区,南宁市,横县',109.266,22.6845,'Hengxian',NULL,NULL),(450128,'埌东新区',450100,'埌东',3,'0771','530000','中国,广西壮族自治区,南宁市,埌东新区',108.419,22.813,'Langdong',NULL,NULL),(450200,'柳州市',450000,'柳州',2,'0772','545001','中国,广西壮族自治区,柳州市',109.412,24.3146,'Liuzhou',NULL,NULL),(450202,'城中区',450200,'城中',3,'0772','545001','中国,广西壮族自治区,柳州市,城中区',109.411,24.3154,'Chengzhong',NULL,NULL),(450203,'鱼峰区',450200,'鱼峰',3,'0772','545005','中国,广西壮族自治区,柳州市,鱼峰区',109.453,24.3187,'Yufeng',NULL,NULL),(450204,'柳南区',450200,'柳南',3,'0772','545007','中国,广西壮族自治区,柳州市,柳南区',109.385,24.336,'Liunan',NULL,NULL),(450205,'柳北区',450200,'柳北',3,'0772','545002','中国,广西壮族自治区,柳州市,柳北区',109.402,24.3627,'Liubei',NULL,NULL),(450221,'柳江县',450200,'柳江',3,'0772','545100','中国,广西壮族自治区,柳州市,柳江县',109.333,24.256,'Liujiang',NULL,NULL),(450222,'柳城县',450200,'柳城',3,'0772','545200','中国,广西壮族自治区,柳州市,柳城县',109.239,24.6495,'Liucheng',NULL,NULL),(450223,'鹿寨县',450200,'鹿寨',3,'0772','545600','中国,广西壮族自治区,柳州市,鹿寨县',109.752,24.4731,'Luzhai',NULL,NULL),(450224,'融安县',450200,'融安',3,'0772','545400','中国,广西壮族自治区,柳州市,融安县',109.398,25.2246,'Rong\'an',NULL,NULL),(450225,'融水苗族自治县',450200,'融水',3,'0772','545300','中国,广西壮族自治区,柳州市,融水苗族自治县',109.256,25.0663,'Rongshui',NULL,NULL),(450226,'三江侗族自治县',450200,'三江',3,'0772','545500','中国,广西壮族自治区,柳州市,三江侗族自治县',109.604,25.7843,'Sanjiang',NULL,NULL),(450227,'柳东新区',450200,'柳东',3,'0772','545000','中国,广西壮族自治区,柳州市,柳东新区',109.437,24.3292,'Liudong',NULL,NULL),(450300,'桂林市',450000,'桂林',2,'0773','541100','中国,广西壮族自治区,桂林市',110.299,25.2742,'Guilin',NULL,NULL),(450302,'秀峰区',450300,'秀峰',3,'0773','541001','中国,广西壮族自治区,桂林市,秀峰区',110.289,25.2825,'Xiufeng',NULL,NULL),(450303,'叠彩区',450300,'叠彩',3,'0773','541001','中国,广西壮族自治区,桂林市,叠彩区',110.302,25.3138,'Diecai',NULL,NULL),(450304,'象山区',450300,'象山',3,'0773','541002','中国,广西壮族自治区,桂林市,象山区',110.281,25.2617,'Xiangshan',NULL,NULL),(450305,'七星区',450300,'七星',3,'0773','541004','中国,广西壮族自治区,桂林市,七星区',110.318,25.2525,'Qixing',NULL,NULL),(450311,'雁山区',450300,'雁山',3,'0773','541006','中国,广西壮族自治区,桂林市,雁山区',110.309,25.0604,'Yanshan',NULL,NULL),(450312,'临桂区',450300,'临桂',3,'0773','541100','中国,广西壮族自治区,桂林市,临桂区',110.205,25.2463,'Lingui',NULL,NULL),(450321,'阳朔县',450300,'阳朔',3,'0773','541900','中国,广西壮族自治区,桂林市,阳朔县',110.495,24.7758,'Yangshuo',NULL,NULL),(450323,'灵川县',450300,'灵川',3,'0773','541200','中国,广西壮族自治区,桂林市,灵川县',110.329,25.4129,'Lingchuan',NULL,NULL),(450324,'全州县',450300,'全州',3,'0773','541503','中国,广西壮族自治区,桂林市,全州县',111.072,25.928,'Quanzhou',NULL,NULL),(450325,'兴安县',450300,'兴安',3,'0773','541300','中国,广西壮族自治区,桂林市,兴安县',110.671,25.6117,'Xing\'an',NULL,NULL),(450326,'永福县',450300,'永福',3,'0773','541800','中国,广西壮族自治区,桂林市,永福县',109.983,24.98,'Yongfu',NULL,NULL),(450327,'灌阳县',450300,'灌阳',3,'0773','541600','中国,广西壮族自治区,桂林市,灌阳县',111.16,25.488,'Guanyang',NULL,NULL),(450328,'龙胜各族自治县',450300,'龙胜',3,'0773','541700','中国,广西壮族自治区,桂林市,龙胜各族自治县',110.012,25.7961,'Longsheng',NULL,NULL),(450329,'资源县',450300,'资源',3,'0773','541400','中国,广西壮族自治区,桂林市,资源县',110.653,26.0424,'Ziyuan',NULL,NULL),(450330,'平乐县',450300,'平乐',3,'0773','542400','中国,广西壮族自治区,桂林市,平乐县',110.642,24.6324,'Pingle',NULL,NULL),(450331,'荔浦县',450300,'荔浦',3,'0773','546600','中国,广西壮族自治区,桂林市,荔浦县',110.397,24.4959,'Lipu',NULL,NULL),(450332,'恭城瑶族自治县',450300,'恭城',3,'0773','542500','中国,广西壮族自治区,桂林市,恭城瑶族自治县',110.83,24.8329,'Gongcheng',NULL,NULL),(450400,'梧州市',450000,'梧州',2,'0774','543002','中国,广西壮族自治区,梧州市',111.316,23.4723,'Wuzhou',NULL,NULL),(450403,'万秀区',450400,'万秀',3,'0774','543000','中国,广西壮族自治区,梧州市,万秀区',111.321,23.473,'Wanxiu',NULL,NULL),(450405,'长洲区',450400,'长洲',3,'0774','543003','中国,广西壮族自治区,梧州市,长洲区',111.275,23.4857,'Changzhou',NULL,NULL),(450406,'龙圩区',450400,'龙圩',3,'0774','543002','中国,广西壮族自治区,梧州市,龙圩区',111.316,23.4723,'Longxu',NULL,NULL),(450421,'苍梧县',450400,'苍梧',3,'0774','543100','中国,广西壮族自治区,梧州市,苍梧县',111.245,23.4205,'Cangwu',NULL,NULL),(450422,'藤县',450400,'藤县',3,'0774','543300','中国,广西壮族自治区,梧州市,藤县',110.914,23.3761,'Tengxian',NULL,NULL),(450423,'蒙山县',450400,'蒙山',3,'0774','546700','中国,广西壮族自治区,梧州市,蒙山县',110.522,24.2017,'Mengshan',NULL,NULL),(450481,'岑溪市',450400,'岑溪',3,'0774','543200','中国,广西壮族自治区,梧州市,岑溪市',110.996,22.9191,'Cenxi',NULL,NULL),(450500,'北海市',450000,'北海',2,'0779','536000','中国,广西壮族自治区,北海市',109.119,21.4733,'Beihai',NULL,NULL),(450502,'海城区',450500,'海城',3,'0779','536000','中国,广西壮族自治区,北海市,海城区',109.117,21.475,'Haicheng',NULL,NULL),(450503,'银海区',450500,'银海',3,'0779','536000','中国,广西壮族自治区,北海市,银海区',109.13,21.4783,'Yinhai',NULL,NULL),(450512,'铁山港区',450500,'铁山港',3,'0779','536017','中国,广西壮族自治区,北海市,铁山港区',109.456,21.5966,'Tieshangang',NULL,NULL),(450521,'合浦县',450500,'合浦',3,'0779','536100','中国,广西壮族自治区,北海市,合浦县',109.201,21.666,'Hepu',NULL,NULL),(450600,'防城港市',450000,'防城港',2,'0770','538001','中国,广西壮族自治区,防城港市',108.345,21.6146,'Fangchenggang',NULL,NULL),(450602,'港口区',450600,'港口',3,'0770','538001','中国,广西壮族自治区,防城港市,港口区',108.38,21.6434,'Gangkou',NULL,NULL),(450603,'防城区',450600,'防城',3,'0770','538021','中国,广西壮族自治区,防城港市,防城区',108.357,21.7646,'Fangcheng',NULL,NULL),(450621,'上思县',450600,'上思',3,'0770','535500','中国,广西壮族自治区,防城港市,上思县',107.982,22.1496,'Shangsi',NULL,NULL),(450681,'东兴市',450600,'东兴',3,'0770','538100','中国,广西壮族自治区,防城港市,东兴市',107.972,21.5471,'Dongxing',NULL,NULL),(450700,'钦州市',450000,'钦州',2,'0777','535099','中国,广西壮族自治区,钦州市',108.624,21.9671,'Qinzhou',NULL,NULL),(450702,'钦南区',450700,'钦南',3,'0777','535099','中国,广西壮族自治区,钦州市,钦南区',108.618,21.9514,'Qinnan',NULL,NULL),(450703,'钦北区',450700,'钦北',3,'0777','535099','中国,广西壮族自治区,钦州市,钦北区',108.63,21.9513,'Qinbei',NULL,NULL),(450721,'灵山县',450700,'灵山',3,'0777','535099','中国,广西壮族自治区,钦州市,灵山县',109.292,22.4165,'Lingshan',NULL,NULL),(450722,'浦北县',450700,'浦北',3,'0777','535099','中国,广西壮族自治区,钦州市,浦北县',109.556,22.2689,'Pubei',NULL,NULL),(450800,'贵港市',450000,'贵港',2,'0775','537100','中国,广西壮族自治区,贵港市',109.602,23.0936,'Guigang',NULL,NULL),(450802,'港北区',450800,'港北',3,'0775','537100','中国,广西壮族自治区,贵港市,港北区',109.572,23.1115,'Gangbei',NULL,NULL),(450803,'港南区',450800,'港南',3,'0775','537100','中国,广西壮族自治区,贵港市,港南区',109.606,23.0723,'Gangnan',NULL,NULL),(450804,'覃塘区',450800,'覃塘',3,'0775','537121','中国,广西壮族自治区,贵港市,覃塘区',109.443,23.1268,'Qintang',NULL,NULL),(450821,'平南县',450800,'平南',3,'0775','537300','中国,广西壮族自治区,贵港市,平南县',110.391,23.542,'Pingnan',NULL,NULL),(450881,'桂平市',450800,'桂平',3,'0775','537200','中国,广西壮族自治区,贵港市,桂平市',110.081,23.3934,'Guiping',NULL,NULL),(450900,'玉林市',450000,'玉林',2,'0775','537000','中国,广西壮族自治区,玉林市',110.154,22.6314,'Yulin',NULL,NULL),(450902,'玉州区',450900,'玉州',3,'0775','537000','中国,广西壮族自治区,玉林市,玉州区',110.151,22.6281,'Yuzhou',NULL,NULL),(450903,'福绵区',450900,'福绵',3,'0775','537023','中国,广西壮族自治区,玉林市,福绵区',110.065,22.5831,'Fumian',NULL,NULL),(450904,'玉东新区',450900,'玉东',3,'0775','537000','中国,广西壮族自治区,玉林市,玉东新区',110.154,22.6314,'Yudong',NULL,NULL),(450921,'容县',450900,'容县',3,'0775','537500','中国,广西壮族自治区,玉林市,容县',110.556,22.857,'Rongxian',NULL,NULL),(450922,'陆川县',450900,'陆川',3,'0775','537700','中国,广西壮族自治区,玉林市,陆川县',110.264,22.3245,'Luchuan',NULL,NULL),(450923,'博白县',450900,'博白',3,'0775','537600','中国,广西壮族自治区,玉林市,博白县',109.977,22.2729,'Bobai',NULL,NULL),(450924,'兴业县',450900,'兴业',3,'0775','537800','中国,广西壮族自治区,玉林市,兴业县',109.876,22.7424,'Xingye',NULL,NULL),(450981,'北流市',450900,'北流',3,'0775','537400','中国,广西壮族自治区,玉林市,北流市',110.353,22.7082,'Beiliu',NULL,NULL),(451000,'百色市',450000,'百色',2,'0776','533000','中国,广西壮族自治区,百色市',106.616,23.8977,'Baise',NULL,NULL),(451002,'右江区',451000,'右江',3,'0776','533000','中国,广西壮族自治区,百色市,右江区',106.618,23.9009,'Youjiang',NULL,NULL),(451021,'田阳县',451000,'田阳',3,'0776','533600','中国,广西壮族自治区,百色市,田阳县',106.916,23.7353,'Tianyang',NULL,NULL),(451022,'田东县',451000,'田东',3,'0776','531500','中国,广西壮族自治区,百色市,田东县',107.124,23.6,'Tiandong',NULL,NULL),(451023,'平果县',451000,'平果',3,'0776','531400','中国,广西壮族自治区,百色市,平果县',107.59,23.3297,'Pingguo',NULL,NULL),(451024,'德保县',451000,'德保',3,'0776','533700','中国,广西壮族自治区,百色市,德保县',106.619,23.3251,'Debao',NULL,NULL),(451025,'靖西县',451000,'靖西',3,'0776','533800','中国,广西壮族自治区,百色市,靖西县',106.418,23.1343,'Jingxi',NULL,NULL),(451026,'那坡县',451000,'那坡',3,'0776','533900','中国,广西壮族自治区,百色市,那坡县',105.842,23.4065,'Napo',NULL,NULL),(451027,'凌云县',451000,'凌云',3,'0776','533100','中国,广西壮族自治区,百色市,凌云县',106.562,24.3475,'Lingyun',NULL,NULL),(451028,'乐业县',451000,'乐业',3,'0776','533200','中国,广西壮族自治区,百色市,乐业县',106.561,24.7829,'Leye',NULL,NULL),(451029,'田林县',451000,'田林',3,'0776','533300','中国,广西壮族自治区,百色市,田林县',106.229,24.2921,'Tianlin',NULL,NULL),(451030,'西林县',451000,'西林',3,'0776','533500','中国,广西壮族自治区,百色市,西林县',105.097,24.4897,'Xilin',NULL,NULL),(451031,'隆林各族自治县',451000,'隆林',3,'0776','533400','中国,广西壮族自治区,百色市,隆林各族自治县',105.343,24.7704,'Longlin',NULL,NULL),(451100,'贺州市',450000,'贺州',2,'0774','542800','中国,广西壮族自治区,贺州市',111.552,24.4141,'Hezhou',NULL,NULL),(451102,'八步区',451100,'八步',3,'0774','542800','中国,广西壮族自治区,贺州市,八步区',111.552,24.4118,'Babu',NULL,NULL),(451121,'昭平县',451100,'昭平',3,'0774','546800','中国,广西壮族自治区,贺州市,昭平县',110.811,24.1701,'Zhaoping',NULL,NULL),(451122,'钟山县',451100,'钟山',3,'0774','542600','中国,广西壮族自治区,贺州市,钟山县',111.305,24.5248,'Zhongshan',NULL,NULL),(451123,'富川瑶族自治县',451100,'富川',3,'0774','542700','中国,广西壮族自治区,贺州市,富川瑶族自治县',111.278,24.8143,'Fuchuan',NULL,NULL),(451124,'平桂管理区',451100,'平桂',3,'0774','542800','中国,广西壮族自治区,贺州市,平桂管理区',111.486,24.458,'Pingui',NULL,NULL),(451200,'河池市',450000,'河池',2,'0778','547000','中国,广西壮族自治区,河池市',108.062,24.6959,'Hechi',NULL,NULL),(451202,'金城江区',451200,'金城江',3,'0779','547000','中国,广西壮族自治区,河池市,金城江区',108.037,24.6897,'Jinchengjiang',NULL,NULL),(451221,'南丹县',451200,'南丹',3,'0781','547200','中国,广西壮族自治区,河池市,南丹县',107.546,24.9776,'Nandan',NULL,NULL),(451222,'天峨县',451200,'天峨',3,'0782','547300','中国,广西壮族自治区,河池市,天峨县',107.172,24.9959,'Tiane',NULL,NULL),(451223,'凤山县',451200,'凤山',3,'0783','547600','中国,广西壮族自治区,河池市,凤山县',107.049,24.5422,'Fengshan',NULL,NULL),(451224,'东兰县',451200,'东兰',3,'0784','547400','中国,广西壮族自治区,河池市,东兰县',107.375,24.5105,'Donglan',NULL,NULL),(451225,'罗城仫佬族自治县',451200,'罗城',3,'0785','546400','中国,广西壮族自治区,河池市,罗城仫佬族自治县',108.908,24.7792,'Luocheng',NULL,NULL),(451226,'环江毛南族自治县',451200,'环江',3,'0786','547100','中国,广西壮族自治区,河池市,环江毛南族自治县',108.261,24.8292,'Huanjiang',NULL,NULL),(451227,'巴马瑶族自治县',451200,'巴马',3,'0787','547500','中国,广西壮族自治区,河池市,巴马瑶族自治县',107.253,24.1413,'Bama',NULL,NULL),(451228,'都安瑶族自治县',451200,'都安',3,'0788','530700','中国,广西壮族自治区,河池市,都安瑶族自治县',108.101,23.9324,'Du\'an',NULL,NULL),(451229,'大化瑶族自治县',451200,'大化',3,'0789','530800','中国,广西壮族自治区,河池市,大化瑶族自治县',107.998,23.7449,'Dahua',NULL,NULL),(451281,'宜州市',451200,'宜州',3,'0780','546300','中国,广西壮族自治区,河池市,宜州市',108.653,24.4939,'Yizhou',NULL,NULL),(451300,'来宾市',450000,'来宾',2,'0772','546100','中国,广西壮族自治区,来宾市',109.23,23.7338,'Laibin',NULL,NULL),(451302,'兴宾区',451300,'兴宾',3,'0772','546100','中国,广西壮族自治区,来宾市,兴宾区',109.235,23.7273,'Xingbin',NULL,NULL),(451321,'忻城县',451300,'忻城',3,'0772','546200','中国,广西壮族自治区,来宾市,忻城县',108.664,24.0686,'Xincheng',NULL,NULL),(451322,'象州县',451300,'象州',3,'0772','545800','中国,广西壮族自治区,来宾市,象州县',109.699,23.9736,'Xiangzhou',NULL,NULL),(451323,'武宣县',451300,'武宣',3,'0772','545900','中国,广西壮族自治区,来宾市,武宣县',109.663,23.5947,'Wuxuan',NULL,NULL),(451324,'金秀瑶族自治县',451300,'金秀',3,'0772','545799','中国,广西壮族自治区,来宾市,金秀瑶族自治县',110.191,24.1293,'Jinxiu',NULL,NULL),(451381,'合山市',451300,'合山',3,'0772','546500','中国,广西壮族自治区,来宾市,合山市',108.886,23.8062,'Heshan',NULL,NULL),(451400,'崇左市',450000,'崇左',2,'0771','532299','中国,广西壮族自治区,崇左市',107.354,22.4041,'Chongzuo',NULL,NULL),(451402,'江州区',451400,'江州',3,'0771','532299','中国,广西壮族自治区,崇左市,江州区',107.347,22.4114,'Jiangzhou',NULL,NULL),(451421,'扶绥县',451400,'扶绥',3,'0771','532199','中国,广西壮族自治区,崇左市,扶绥县',107.904,22.6341,'Fusui',NULL,NULL),(451422,'宁明县',451400,'宁明',3,'0771','532599','中国,广西壮族自治区,崇左市,宁明县',107.073,22.1366,'Ningming',NULL,NULL),(451423,'龙州县',451400,'龙州',3,'0771','532499','中国,广西壮族自治区,崇左市,龙州县',106.854,22.3394,'Longzhou',NULL,NULL),(451424,'大新县',451400,'大新',3,'0771','532399','中国,广西壮族自治区,崇左市,大新县',107.198,22.8341,'Daxin',NULL,NULL),(451425,'天等县',451400,'天等',3,'0771','532899','中国,广西壮族自治区,崇左市,天等县',107.14,23.077,'Tiandeng',NULL,NULL),(451481,'凭祥市',451400,'凭祥',3,'0771','532699','中国,广西壮族自治区,崇左市,凭祥市',106.755,22.1057,'Pingxiang',NULL,NULL),(460000,'海南省',100000,'海南',1,'','','中国,海南省',110.331,20.032,'Hainan',NULL,NULL),(460100,'海口市',460000,'海口',2,'0898','570000','中国,海南省,海口市',110.331,20.032,'Haikou',NULL,NULL),(460105,'秀英区',460100,'秀英',3,'0898','570311','中国,海南省,海口市,秀英区',110.293,20.0075,'Xiuying',NULL,NULL),(460106,'龙华区',460100,'龙华',3,'0898','570145','中国,海南省,海口市,龙华区',110.302,20.0287,'Longhua',NULL,NULL),(460107,'琼山区',460100,'琼山',3,'0898','571100','中国,海南省,海口市,琼山区',110.354,20.0032,'Qiongshan',NULL,NULL),(460108,'美兰区',460100,'美兰',3,'0898','570203','中国,海南省,海口市,美兰区',110.369,20.0286,'Meilan',NULL,NULL),(460200,'三亚市',460000,'三亚',2,'0898','572000','中国,海南省,三亚市',109.508,18.2479,'Sanya',NULL,NULL),(460202,'海棠区',460200,'海棠',3,'0898','572000','中国,海南省,三亚市,海棠区',109.508,18.2479,'Haitang',NULL,NULL),(460203,'吉阳区',460200,'吉阳',3,'0898','572000','中国,海南省,三亚市,吉阳区',109.508,18.2479,'Jiyang',NULL,NULL),(460204,'天涯区',460200,'天涯',3,'0898','572000','中国,海南省,三亚市,天涯区',109.508,18.2479,'Tianya',NULL,NULL),(460205,'崖州区',460200,'崖州',3,'0898','572000','中国,海南省,三亚市,崖州区',109.508,18.2479,'Yazhou',NULL,NULL),(460300,'三沙市',460000,'三沙',2,'0898','573199','中国,海南省,三沙市',112.349,16.831,'Sansha',NULL,NULL),(460321,'西沙群岛',460300,'西沙',3,'0898','572000','中国,海南省,三沙市,西沙群岛',112.026,16.3313,'Xisha Islands',NULL,NULL),(460322,'南沙群岛',460300,'南沙',3,'0898','573100','中国,海南省,三沙市,南沙群岛',116.75,11.4719,'Nansha Islands',NULL,NULL),(460323,'中沙群岛',460300,'中沙',3,'0898','573100','中国,海南省,三沙市,中沙群岛',117.74,15.1129,'Zhongsha Islands',NULL,NULL),(469000,'直辖县级',460000,' ',2,'','','中国,海南省,直辖县级',109.503,18.7399,'',NULL,NULL),(469001,'五指山市',469000,'五指山',3,'0898','572200','中国,海南省,直辖县级,五指山市',109.517,18.7769,'Wuzhishan',NULL,NULL),(469002,'琼海市',469000,'琼海',3,'0898','571400','中国,海南省,直辖县级,琼海市',110.467,19.246,'Qionghai',NULL,NULL),(469003,'儋州市',469000,'儋州',3,'0898','571700','中国,海南省,直辖县级,儋州市',109.577,19.5175,'Danzhou',NULL,NULL),(469005,'文昌市',469000,'文昌',3,'0898','571339','中国,海南省,直辖县级,文昌市',110.754,19.613,'Wenchang',NULL,NULL),(469006,'万宁市',469000,'万宁',3,'0898','571500','中国,海南省,直辖县级,万宁市',110.389,18.7962,'Wanning',NULL,NULL),(469007,'东方市',469000,'东方',3,'0898','572600','中国,海南省,直辖县级,东方市',108.654,19.102,'Dongfang',NULL,NULL),(469021,'定安县',469000,'定安',3,'0898','571200','中国,海南省,直辖县级,定安县',110.324,19.6992,'Ding\'an',NULL,NULL),(469022,'屯昌县',469000,'屯昌',3,'0898','571600','中国,海南省,直辖县级,屯昌县',110.103,19.3629,'Tunchang',NULL,NULL),(469023,'澄迈县',469000,'澄迈',3,'0898','571900','中国,海南省,直辖县级,澄迈县',110.007,19.7371,'Chengmai',NULL,NULL),(469024,'临高县',469000,'临高',3,'0898','571800','中国,海南省,直辖县级,临高县',109.688,19.9083,'Lingao',NULL,NULL),(469025,'白沙黎族自治县',469000,'白沙',3,'0898','572800','中国,海南省,直辖县级,白沙黎族自治县',109.453,19.2246,'Baisha',NULL,NULL),(469026,'昌江黎族自治县',469000,'昌江',3,'0898','572700','中国,海南省,直辖县级,昌江黎族自治县',109.053,19.261,'Changjiang',NULL,NULL),(469027,'乐东黎族自治县',469000,'乐东',3,'0898','572500','中国,海南省,直辖县级,乐东黎族自治县',109.175,18.7476,'Ledong',NULL,NULL),(469028,'陵水黎族自治县',469000,'陵水',3,'0898','572400','中国,海南省,直辖县级,陵水黎族自治县',110.037,18.505,'Lingshui',NULL,NULL),(469029,'保亭黎族苗族自治县',469000,'保亭',3,'0898','572300','中国,海南省,直辖县级,保亭黎族苗族自治县',109.702,18.6364,'Baoting',NULL,NULL),(469030,'琼中黎族苗族自治县',469000,'琼中',3,'0898','572900','中国,海南省,直辖县级,琼中黎族苗族自治县',109.84,19.0356,'Qiongzhong',NULL,NULL),(500000,'重庆',100000,'重庆',1,'','','中国,重庆',106.505,29.5332,'Chongqing',NULL,NULL),(500100,'重庆市',500000,'重庆',2,'023','400000','中国,重庆,重庆市',106.505,29.5332,'Chongqing',NULL,NULL),(500101,'万州区',500100,'万州',3,'023','404000','中国,重庆,重庆市,万州区',108.409,30.8079,'Wanzhou',NULL,NULL),(500102,'涪陵区',500100,'涪陵',3,'023','408000','中国,重庆,重庆市,涪陵区',107.39,29.7029,'Fuling',NULL,NULL),(500103,'渝中区',500100,'渝中',3,'023','400010','中国,重庆,重庆市,渝中区',106.569,29.5528,'Yuzhong',NULL,NULL),(500104,'大渡口区',500100,'大渡口',3,'023','400080','中国,重庆,重庆市,大渡口区',106.483,29.4845,'Dadukou',NULL,NULL),(500105,'江北区',500100,'江北',3,'023','400020','中国,重庆,重庆市,江北区',106.574,29.6066,'Jiangbei',NULL,NULL),(500106,'沙坪坝区',500100,'沙坪坝',3,'023','400030','中国,重庆,重庆市,沙坪坝区',106.458,29.5411,'Shapingba',NULL,NULL),(500107,'九龙坡区',500100,'九龙坡',3,'023','400050','中国,重庆,重庆市,九龙坡区',106.511,29.502,'Jiulongpo',NULL,NULL),(500108,'南岸区',500100,'南岸',3,'023','400064','中国,重庆,重庆市,南岸区',106.563,29.5231,'Nan\'an',NULL,NULL),(500109,'北碚区',500100,'北碚',3,'023','400700','中国,重庆,重庆市,北碚区',106.396,29.8057,'Beibei',NULL,NULL),(500110,'綦江区',500100,'綦江',3,'023','400800','中国,重庆,重庆市,綦江区',106.927,28.9607,'Qijiang',NULL,NULL),(500111,'大足区',500100,'大足',3,'023','400900','中国,重庆,重庆市,大足区',105.768,29.484,'Dazu',NULL,NULL),(500112,'渝北区',500100,'渝北',3,'023','401120','中国,重庆,重庆市,渝北区',106.631,29.7182,'Yubei',NULL,NULL),(500113,'巴南区',500100,'巴南',3,'023','401320','中国,重庆,重庆市,巴南区',106.524,29.3831,'Banan',NULL,NULL),(500114,'黔江区',500100,'黔江',3,'023','409700','中国,重庆,重庆市,黔江区',108.771,29.5332,'Qianjiang',NULL,NULL),(500115,'长寿区',500100,'长寿',3,'023','401220','中国,重庆,重庆市,长寿区',107.082,29.8536,'Changshou',NULL,NULL),(500116,'江津区',500100,'江津',3,'023','402260','中国,重庆,重庆市,江津区',106.259,29.2901,'Jiangjin',NULL,NULL),(500117,'合川区',500100,'合川',3,'023','401520','中国,重庆,重庆市,合川区',106.276,29.9723,'Hechuan',NULL,NULL),(500118,'永川区',500100,'永川',3,'023','402160','中国,重庆,重庆市,永川区',105.927,29.3559,'Yongchuan',NULL,NULL),(500119,'南川区',500100,'南川',3,'023','408400','中国,重庆,重庆市,南川区',107.099,29.1575,'Nanchuan',NULL,NULL),(500120,'璧山区',500100,'璧山',3,'023','402760','中国,重庆,重庆市,璧山区',106.231,29.5936,'Bishan',NULL,NULL),(500151,'铜梁区',500100,'铜梁',3,'023','402560','中国,重庆,重庆市,铜梁区',106.055,29.8399,'Tongliang',NULL,NULL),(500223,'潼南县',500100,'潼南',3,'023','402660','中国,重庆,重庆市,潼南县',105.84,30.1912,'Tongnan',NULL,NULL),(500226,'荣昌县',500100,'荣昌',3,'023','402460','中国,重庆,重庆市,荣昌县',105.594,29.4049,'Rongchang',NULL,NULL),(500228,'梁平县',500100,'梁平',3,'023','405200','中国,重庆,重庆市,梁平县',107.8,30.6754,'Liangping',NULL,NULL),(500229,'城口县',500100,'城口',3,'023','405900','中国,重庆,重庆市,城口县',108.665,31.948,'Chengkou',NULL,NULL),(500230,'丰都县',500100,'丰都',3,'023','408200','中国,重庆,重庆市,丰都县',107.731,29.8635,'Fengdu',NULL,NULL),(500231,'垫江县',500100,'垫江',3,'023','408300','中国,重庆,重庆市,垫江县',107.354,30.3336,'Dianjiang',NULL,NULL),(500232,'武隆县',500100,'武隆',3,'023','408500','中国,重庆,重庆市,武隆县',107.76,29.3255,'Wulong',NULL,NULL),(500233,'忠县',500100,'忠县',3,'023','404300','中国,重庆,重庆市,忠县',108.037,30.289,'Zhongxian',NULL,NULL),(500234,'开县',500100,'开县',3,'023','405400','中国,重庆,重庆市,开县',108.393,31.1609,'Kaixian',NULL,NULL),(500235,'云阳县',500100,'云阳',3,'023','404500','中国,重庆,重庆市,云阳县',108.697,30.9306,'Yunyang',NULL,NULL),(500236,'奉节县',500100,'奉节',3,'023','404600','中国,重庆,重庆市,奉节县',109.465,31.0182,'Fengjie',NULL,NULL),(500237,'巫山县',500100,'巫山',3,'023','404700','中国,重庆,重庆市,巫山县',109.878,31.0746,'Wushan',NULL,NULL),(500238,'巫溪县',500100,'巫溪',3,'023','405800','中国,重庆,重庆市,巫溪县',109.631,31.3976,'Wuxi',NULL,NULL),(500240,'石柱土家族自治县',500100,'石柱',3,'023','409100','中国,重庆,重庆市,石柱土家族自治县',108.114,30.0005,'Shizhu',NULL,NULL),(500241,'秀山土家族苗族自治县',500100,'秀山',3,'023','409900','中国,重庆,重庆市,秀山土家族苗族自治县',108.989,28.4506,'Xiushan',NULL,NULL),(500242,'酉阳土家族苗族自治县',500100,'酉阳',3,'023','409800','中国,重庆,重庆市,酉阳土家族苗族自治县',108.772,28.8446,'Youyang',NULL,NULL),(500243,'彭水苗族土家族自治县',500100,'彭水',3,'023','409600','中国,重庆,重庆市,彭水苗族土家族自治县',108.166,29.2952,'Pengshui',NULL,NULL),(500300,'两江新区',500000,'两江新区',2,'023','400000','中国,重庆,两江新区',106.463,29.7292,'Liangjiangxinqu',NULL,NULL),(500301,'北部新区',500300,'北部新区',3,'023','400000','中国,重庆,两江新区,北部新区',106.489,29.6671,'Beibuxinqu',NULL,NULL),(500302,'保税港区',500300,'保税港区',3,'023','400000','中国,重庆,两江新区,保税港区',106.638,29.7163,'Baoshuigangqu',NULL,NULL),(500303,'工业园区',500300,'工业园区',3,'023','400000','中国,重庆,两江新区,工业园区',106.626,29.5555,'Gongyeyuanqu',NULL,NULL),(510000,'四川省',100000,'四川',1,'','','中国,四川省',104.066,30.6595,'Sichuan',NULL,NULL),(510100,'成都市',510000,'成都',2,'028','610015','中国,四川省,成都市',104.066,30.6595,'Chengdu',NULL,NULL),(510104,'锦江区',510100,'锦江',3,'028','610021','中国,四川省,成都市,锦江区',104.083,30.6561,'Jinjiang',NULL,NULL),(510105,'青羊区',510100,'青羊',3,'028','610031','中国,四川省,成都市,青羊区',104.062,30.6739,'Qingyang',NULL,NULL),(510106,'金牛区',510100,'金牛',3,'028','610036','中国,四川省,成都市,金牛区',104.051,30.6913,'Jinniu',NULL,NULL),(510107,'武侯区',510100,'武侯',3,'028','610041','中国,四川省,成都市,武侯区',104.043,30.6423,'Wuhou',NULL,NULL),(510108,'成华区',510100,'成华',3,'028','610066','中国,四川省,成都市,成华区',104.102,30.6599,'Chenghua',NULL,NULL),(510112,'龙泉驿区',510100,'龙泉驿',3,'028','610100','中国,四川省,成都市,龙泉驿区',104.275,30.5566,'Longquanyi',NULL,NULL),(510113,'青白江区',510100,'青白江',3,'028','610300','中国,四川省,成都市,青白江区',104.251,30.8784,'Qingbaijiang',NULL,NULL),(510114,'新都区',510100,'新都',3,'028','610500','中国,四川省,成都市,新都区',104.159,30.8231,'Xindu',NULL,NULL),(510115,'温江区',510100,'温江',3,'028','611130','中国,四川省,成都市,温江区',103.849,30.6844,'Wenjiang',NULL,NULL),(510121,'金堂县',510100,'金堂',3,'028','610400','中国,四川省,成都市,金堂县',104.412,30.8619,'Jintang',NULL,NULL),(510122,'双流县',510100,'双流',3,'028','610200','中国,四川省,成都市,双流县',103.924,30.5744,'Shuangliu',NULL,NULL),(510124,'郫县',510100,'郫县',3,'028','611730','中国,四川省,成都市,郫县',103.887,30.8105,'Pixian',NULL,NULL),(510129,'大邑县',510100,'大邑',3,'028','611330','中国,四川省,成都市,大邑县',103.521,30.5874,'Dayi',NULL,NULL),(510131,'蒲江县',510100,'蒲江',3,'028','611630','中国,四川省,成都市,蒲江县',103.506,30.1967,'Pujiang',NULL,NULL),(510132,'新津县',510100,'新津',3,'028','611430','中国,四川省,成都市,新津县',103.811,30.4098,'Xinjin',NULL,NULL),(510181,'都江堰市',510100,'都江堰',3,'028','611830','中国,四川省,成都市,都江堰市',103.619,30.9982,'Dujiangyan',NULL,NULL),(510182,'彭州市',510100,'彭州',3,'028','611930','中国,四川省,成都市,彭州市',103.958,30.9901,'Pengzhou',NULL,NULL),(510183,'邛崃市',510100,'邛崃',3,'028','611530','中国,四川省,成都市,邛崃市',103.463,30.4149,'Qionglai',NULL,NULL),(510184,'崇州市',510100,'崇州',3,'028','611230','中国,四川省,成都市,崇州市',103.673,30.6301,'Chongzhou',NULL,NULL),(510300,'自贡市',510000,'自贡',2,'0813','643000','中国,四川省,自贡市',104.773,29.3528,'Zigong',NULL,NULL),(510302,'自流井区',510300,'自流井',3,'0813','643000','中国,四川省,自贡市,自流井区',104.777,29.3375,'Ziliujing',NULL,NULL),(510303,'贡井区',510300,'贡井',3,'0813','643020','中国,四川省,自贡市,贡井区',104.715,29.3458,'Gongjing',NULL,NULL),(510304,'大安区',510300,'大安',3,'0813','643010','中国,四川省,自贡市,大安区',104.774,29.3636,'Da\'an',NULL,NULL),(510311,'沿滩区',510300,'沿滩',3,'0813','643030','中国,四川省,自贡市,沿滩区',104.88,29.2661,'Yantan',NULL,NULL),(510321,'荣县',510300,'荣县',3,'0813','643100','中国,四川省,自贡市,荣县',104.418,29.4445,'Rongxian',NULL,NULL),(510322,'富顺县',510300,'富顺',3,'0813','643200','中国,四川省,自贡市,富顺县',104.975,29.1812,'Fushun',NULL,NULL),(510400,'攀枝花市',510000,'攀枝花',2,'0812','617000','中国,四川省,攀枝花市',101.716,26.5804,'Panzhihua',NULL,NULL),(510402,'东区',510400,'东区',3,'0812','617067','中国,四川省,攀枝花市,东区',101.705,26.5468,'Dongqu',NULL,NULL),(510403,'西区',510400,'西区',3,'0812','617068','中国,四川省,攀枝花市,西区',101.631,26.5975,'Xiqu',NULL,NULL),(510411,'仁和区',510400,'仁和',3,'0812','617061','中国,四川省,攀枝花市,仁和区',101.738,26.4984,'Renhe',NULL,NULL),(510421,'米易县',510400,'米易',3,'0812','617200','中国,四川省,攀枝花市,米易县',102.111,26.8872,'Miyi',NULL,NULL),(510422,'盐边县',510400,'盐边',3,'0812','617100','中国,四川省,攀枝花市,盐边县',101.854,26.6885,'Yanbian',NULL,NULL),(510500,'泸州市',510000,'泸州',2,'0830','646000','中国,四川省,泸州市',105.443,28.8891,'Luzhou',NULL,NULL),(510502,'江阳区',510500,'江阳',3,'0830','646000','中国,四川省,泸州市,江阳区',105.453,28.8893,'Jiangyang',NULL,NULL),(510503,'纳溪区',510500,'纳溪',3,'0830','646300','中国,四川省,泸州市,纳溪区',105.373,28.7734,'Naxi',NULL,NULL),(510504,'龙马潭区',510500,'龙马潭',3,'0830','646000','中国,四川省,泸州市,龙马潭区',105.438,28.9131,'Longmatan',NULL,NULL),(510521,'泸县',510500,'泸县',3,'0830','646106','中国,四川省,泸州市,泸县',105.382,29.1504,'Luxian',NULL,NULL),(510522,'合江县',510500,'合江',3,'0830','646200','中国,四川省,泸州市,合江县',105.835,28.81,'Hejiang',NULL,NULL),(510524,'叙永县',510500,'叙永',3,'0830','646400','中国,四川省,泸州市,叙永县',105.445,28.1559,'Xuyong',NULL,NULL),(510525,'古蔺县',510500,'古蔺',3,'0830','646500','中国,四川省,泸州市,古蔺县',105.813,28.0387,'Gulin',NULL,NULL),(510600,'德阳市',510000,'德阳',2,'0838','618000','中国,四川省,德阳市',104.399,31.128,'Deyang',NULL,NULL),(510603,'旌阳区',510600,'旌阳',3,'0838','618000','中国,四川省,德阳市,旌阳区',104.394,31.1391,'Jingyang',NULL,NULL),(510623,'中江县',510600,'中江',3,'0838','618100','中国,四川省,德阳市,中江县',104.679,31.033,'Zhongjiang',NULL,NULL),(510626,'罗江县',510600,'罗江',3,'0838','618500','中国,四川省,德阳市,罗江县',104.51,31.3167,'Luojiang',NULL,NULL),(510681,'广汉市',510600,'广汉',3,'0838','618300','中国,四川省,德阳市,广汉市',104.282,30.9769,'Guanghan',NULL,NULL),(510682,'什邡市',510600,'什邡',3,'0838','618400','中国,四川省,德阳市,什邡市',104.168,31.1264,'Shifang',NULL,NULL),(510683,'绵竹市',510600,'绵竹',3,'0838','618200','中国,四川省,德阳市,绵竹市',104.221,31.3377,'Mianzhu',NULL,NULL),(510700,'绵阳市',510000,'绵阳',2,'0816','621000','中国,四川省,绵阳市',104.742,31.464,'Mianyang',NULL,NULL),(510703,'涪城区',510700,'涪城',3,'0816','621000','中国,四川省,绵阳市,涪城区',104.757,31.4552,'Fucheng',NULL,NULL),(510704,'游仙区',510700,'游仙',3,'0816','621022','中国,四川省,绵阳市,游仙区',104.771,31.4657,'Youxian',NULL,NULL),(510722,'三台县',510700,'三台',3,'0816','621100','中国,四川省,绵阳市,三台县',105.091,31.0918,'Santai',NULL,NULL),(510723,'盐亭县',510700,'盐亭',3,'0816','621600','中国,四川省,绵阳市,盐亭县',105.39,31.2218,'Yanting',NULL,NULL),(510724,'安县',510700,'安县',3,'0816','622650','中国,四川省,绵阳市,安县',104.567,31.5349,'Anxian',NULL,NULL),(510725,'梓潼县',510700,'梓潼',3,'0816','622150','中国,四川省,绵阳市,梓潼县',105.162,31.6359,'Zitong',NULL,NULL),(510726,'北川羌族自治县',510700,'北川',3,'0816','622750','中国,四川省,绵阳市,北川羌族自治县',104.464,31.8329,'Beichuan',NULL,NULL),(510727,'平武县',510700,'平武',3,'0816','622550','中国,四川省,绵阳市,平武县',104.529,32.4079,'Pingwu',NULL,NULL),(510781,'江油市',510700,'江油',3,'0816','621700','中国,四川省,绵阳市,江油市',104.745,31.7778,'Jiangyou',NULL,NULL),(510800,'广元市',510000,'广元',2,'0839','628000','中国,四川省,广元市',105.83,32.4337,'Guangyuan',NULL,NULL),(510802,'利州区',510800,'利州',3,'0839','628017','中国,四川省,广元市,利州区',105.826,32.4323,'Lizhou',NULL,NULL),(510811,'昭化区',510800,'昭化',3,'0839','628017','中国,四川省,广元市,昭化区',105.64,32.3865,'Zhaohua',NULL,NULL),(510812,'朝天区',510800,'朝天',3,'0839','628017','中国,四川省,广元市,朝天区',105.893,32.644,'Chaotian',NULL,NULL),(510821,'旺苍县',510800,'旺苍',3,'0839','628200','中国,四川省,广元市,旺苍县',106.29,32.2285,'Wangcang',NULL,NULL),(510822,'青川县',510800,'青川',3,'0839','628100','中国,四川省,广元市,青川县',105.239,32.5856,'Qingchuan',NULL,NULL),(510823,'剑阁县',510800,'剑阁',3,'0839','628300','中国,四川省,广元市,剑阁县',105.525,32.2884,'Jiange',NULL,NULL),(510824,'苍溪县',510800,'苍溪',3,'0839','628400','中国,四川省,广元市,苍溪县',105.936,31.7321,'Cangxi',NULL,NULL),(510900,'遂宁市',510000,'遂宁',2,'0825','629000','中国,四川省,遂宁市',105.571,30.5133,'Suining',NULL,NULL),(510903,'船山区',510900,'船山',3,'0825','629000','中国,四川省,遂宁市,船山区',105.581,30.4999,'Chuanshan',NULL,NULL),(510904,'安居区',510900,'安居',3,'0825','629000','中国,四川省,遂宁市,安居区',105.464,30.3578,'Anju',NULL,NULL),(510921,'蓬溪县',510900,'蓬溪',3,'0825','629100','中国,四川省,遂宁市,蓬溪县',105.708,30.7577,'Pengxi',NULL,NULL),(510922,'射洪县',510900,'射洪',3,'0825','629200','中国,四川省,遂宁市,射洪县',105.389,30.872,'Shehong',NULL,NULL),(510923,'大英县',510900,'大英',3,'0825','629300','中国,四川省,遂宁市,大英县',105.243,30.5943,'Daying',NULL,NULL),(511000,'内江市',510000,'内江',2,'0832','641000','中国,四川省,内江市',105.066,29.5871,'Neijiang',NULL,NULL),(511002,'市中区',511000,'市中区',3,'0832','641000','中国,四川省,内江市,市中区',105.068,29.5871,'Shizhongqu',NULL,NULL),(511011,'东兴区',511000,'东兴',3,'0832','641100','中国,四川省,内江市,东兴区',105.076,29.5928,'Dongxing',NULL,NULL),(511024,'威远县',511000,'威远',3,'0832','642450','中国,四川省,内江市,威远县',104.67,29.5282,'Weiyuan',NULL,NULL),(511025,'资中县',511000,'资中',3,'0832','641200','中国,四川省,内江市,资中县',104.852,29.7641,'Zizhong',NULL,NULL),(511028,'隆昌县',511000,'隆昌',3,'0832','642150','中国,四川省,内江市,隆昌县',105.287,29.3394,'Longchang',NULL,NULL),(511100,'乐山市',510000,'乐山',2,'0833','614000','中国,四川省,乐山市',103.761,29.582,'Leshan',NULL,NULL),(511102,'市中区',511100,'市中区',3,'0833','614000','中国,四川省,乐山市,市中区',103.762,29.5554,'Shizhongqu',NULL,NULL),(511111,'沙湾区',511100,'沙湾',3,'0833','614900','中国,四川省,乐山市,沙湾区',103.549,29.4119,'Shawan',NULL,NULL),(511112,'五通桥区',511100,'五通桥',3,'0833','614800','中国,四川省,乐山市,五通桥区',103.823,29.4034,'Wutongqiao',NULL,NULL),(511113,'金口河区',511100,'金口河',3,'0833','614700','中国,四川省,乐山市,金口河区',103.079,29.2458,'Jinkouhe',NULL,NULL),(511123,'犍为县',511100,'犍为',3,'0833','614400','中国,四川省,乐山市,犍为县',103.95,29.2097,'Qianwei',NULL,NULL),(511124,'井研县',511100,'井研',3,'0833','613100','中国,四川省,乐山市,井研县',104.07,29.6523,'Jingyan',NULL,NULL),(511126,'夹江县',511100,'夹江',3,'0833','614100','中国,四川省,乐山市,夹江县',103.572,29.7387,'Jiajiang',NULL,NULL),(511129,'沐川县',511100,'沐川',3,'0833','614500','中国,四川省,乐山市,沐川县',103.904,28.9565,'Muchuan',NULL,NULL),(511132,'峨边彝族自治县',511100,'峨边',3,'0833','614300','中国,四川省,乐山市,峨边彝族自治县',103.263,29.23,'Ebian',NULL,NULL),(511133,'马边彝族自治县',511100,'马边',3,'0833','614600','中国,四川省,乐山市,马边彝族自治县',103.546,28.8359,'Mabian',NULL,NULL),(511181,'峨眉山市',511100,'峨眉山',3,'0833','614200','中国,四川省,乐山市,峨眉山市',103.484,29.6012,'Emeishan',NULL,NULL),(511300,'南充市',510000,'南充',2,'0817','637000','中国,四川省,南充市',106.083,30.7953,'Nanchong',NULL,NULL),(511302,'顺庆区',511300,'顺庆',3,'0817','637000','中国,四川省,南充市,顺庆区',106.092,30.7964,'Shunqing',NULL,NULL),(511303,'高坪区',511300,'高坪',3,'0817','637100','中国,四川省,南充市,高坪区',106.119,30.7816,'Gaoping',NULL,NULL),(511304,'嘉陵区',511300,'嘉陵',3,'0817','637100','中国,四川省,南充市,嘉陵区',106.071,30.7585,'Jialing',NULL,NULL),(511321,'南部县',511300,'南部',3,'0817','637300','中国,四川省,南充市,南部县',106.067,31.3545,'Nanbu',NULL,NULL),(511322,'营山县',511300,'营山',3,'0817','637700','中国,四川省,南充市,营山县',106.566,31.0775,'Yingshan',NULL,NULL),(511323,'蓬安县',511300,'蓬安',3,'0817','637800','中国,四川省,南充市,蓬安县',106.413,31.0296,'Peng\'an',NULL,NULL),(511324,'仪陇县',511300,'仪陇',3,'0817','637600','中国,四川省,南充市,仪陇县',106.3,31.2763,'Yilong',NULL,NULL),(511325,'西充县',511300,'西充',3,'0817','637200','中国,四川省,南充市,西充县',105.9,30.9969,'Xichong',NULL,NULL),(511381,'阆中市',511300,'阆中',3,'0817','637400','中国,四川省,南充市,阆中市',106.005,31.5583,'Langzhong',NULL,NULL),(511400,'眉山市',510000,'眉山',2,'028','620020','中国,四川省,眉山市',103.832,30.0483,'Meishan',NULL,NULL),(511402,'东坡区',511400,'东坡',3,'028','620010','中国,四川省,眉山市,东坡区',103.832,30.0422,'Dongpo',NULL,NULL),(511403,'彭山区',511400,'彭山',3,'028','620860','中国,四川省,眉山市,彭山区',103.873,30.1928,'Pengshan',NULL,NULL),(511421,'仁寿县',511400,'仁寿',3,'028','620500','中国,四川省,眉山市,仁寿县',104.134,29.996,'Renshou',NULL,NULL),(511423,'洪雅县',511400,'洪雅',3,'028','620360','中国,四川省,眉山市,洪雅县',103.373,29.9066,'Hongya',NULL,NULL),(511424,'丹棱县',511400,'丹棱',3,'028','620200','中国,四川省,眉山市,丹棱县',103.513,30.0156,'Danling',NULL,NULL),(511425,'青神县',511400,'青神',3,'028','620460','中国,四川省,眉山市,青神县',103.848,29.8323,'Qingshen',NULL,NULL),(511500,'宜宾市',510000,'宜宾',2,'0831','644000','中国,四川省,宜宾市',104.631,28.7602,'Yibin',NULL,NULL),(511502,'翠屏区',511500,'翠屏',3,'0831','644000','中国,四川省,宜宾市,翠屏区',104.62,28.7657,'Cuiping',NULL,NULL),(511503,'南溪区',511500,'南溪',3,'0831','644100','中国,四川省,宜宾市,南溪区',104.981,28.8398,'Nanxi',NULL,NULL),(511521,'宜宾县',511500,'宜宾',3,'0831','644600','中国,四川省,宜宾市,宜宾县',104.533,28.69,'Yibin',NULL,NULL),(511523,'江安县',511500,'江安',3,'0831','644200','中国,四川省,宜宾市,江安县',105.067,28.7239,'Jiang\'an',NULL,NULL),(511524,'长宁县',511500,'长宁',3,'0831','644300','中国,四川省,宜宾市,长宁县',104.925,28.5778,'Changning',NULL,NULL),(511525,'高县',511500,'高县',3,'0831','645150','中国,四川省,宜宾市,高县',104.518,28.4362,'Gaoxian',NULL,NULL),(511526,'珙县',511500,'珙县',3,'0831','644500','中国,四川省,宜宾市,珙县',104.714,28.4451,'Gongxian',NULL,NULL),(511527,'筠连县',511500,'筠连',3,'0831','645250','中国,四川省,宜宾市,筠连县',104.512,28.1649,'Junlian',NULL,NULL),(511528,'兴文县',511500,'兴文',3,'0831','644400','中国,四川省,宜宾市,兴文县',105.237,28.3044,'Xingwen',NULL,NULL),(511529,'屏山县',511500,'屏山',3,'0831','645350','中国,四川省,宜宾市,屏山县',104.163,28.6437,'Pingshan',NULL,NULL),(511600,'广安市',510000,'广安',2,'0826','638000','中国,四川省,广安市',106.633,30.4564,'Guang\'an',NULL,NULL),(511602,'广安区',511600,'广安',3,'0826','638000','中国,四川省,广安市,广安区',106.642,30.4739,'Guang\'an',NULL,NULL),(511603,'前锋区',511600,'前锋',3,'0826','638019','中国,四川省,广安市,前锋区',106.894,30.4946,'Qianfeng',NULL,NULL),(511621,'岳池县',511600,'岳池',3,'0826','638300','中国,四川省,广安市,岳池县',106.441,30.5392,'Yuechi',NULL,NULL),(511622,'武胜县',511600,'武胜',3,'0826','638400','中国,四川省,广安市,武胜县',106.296,30.3493,'Wusheng',NULL,NULL),(511623,'邻水县',511600,'邻水',3,'0826','638500','中国,四川省,广安市,邻水县',106.93,30.3345,'Linshui',NULL,NULL),(511681,'华蓥市',511600,'华蓥',3,'0826','638600','中国,四川省,广安市,华蓥市',106.785,30.3901,'Huaying',NULL,NULL),(511700,'达州市',510000,'达州',2,'0818','635000','中国,四川省,达州市',107.502,31.2095,'Dazhou',NULL,NULL),(511702,'通川区',511700,'通川',3,'0818','635000','中国,四川省,达州市,通川区',107.505,31.2147,'Tongchuan',NULL,NULL),(511703,'达川区',511700,'达川',3,'0818','635000','中国,四川省,达州市,达川区',107.502,31.2095,'Dachuan',NULL,NULL),(511722,'宣汉县',511700,'宣汉',3,'0818','636150','中国,四川省,达州市,宣汉县',107.728,31.3552,'Xuanhan',NULL,NULL),(511723,'开江县',511700,'开江',3,'0818','636250','中国,四川省,达州市,开江县',107.869,31.0841,'Kaijiang',NULL,NULL),(511724,'大竹县',511700,'大竹',3,'0818','635100','中国,四川省,达州市,大竹县',107.209,30.7415,'Dazhu',NULL,NULL),(511725,'渠县',511700,'渠县',3,'0818','635200','中国,四川省,达州市,渠县',106.974,30.8376,'Quxian',NULL,NULL),(511781,'万源市',511700,'万源',3,'0818','636350','中国,四川省,达州市,万源市',108.036,32.0809,'Wanyuan',NULL,NULL),(511800,'雅安市',510000,'雅安',2,'0835','625000','中国,四川省,雅安市',103.001,29.9877,'Ya\'an',NULL,NULL),(511802,'雨城区',511800,'雨城',3,'0835','625000','中国,四川省,雅安市,雨城区',103.033,30.0053,'Yucheng',NULL,NULL),(511803,'名山区',511800,'名山',3,'0835','625100','中国,四川省,雅安市,名山区',103.112,30.0847,'Mingshan',NULL,NULL),(511822,'荥经县',511800,'荥经',3,'0835','625200','中国,四川省,雅安市,荥经县',102.847,29.794,'Yingjing',NULL,NULL),(511823,'汉源县',511800,'汉源',3,'0835','625300','中国,四川省,雅安市,汉源县',102.678,29.3517,'Hanyuan',NULL,NULL),(511824,'石棉县',511800,'石棉',3,'0835','625400','中国,四川省,雅安市,石棉县',102.359,29.228,'Shimian',NULL,NULL),(511825,'天全县',511800,'天全',3,'0835','625500','中国,四川省,雅安市,天全县',102.759,30.0675,'Tianquan',NULL,NULL),(511826,'芦山县',511800,'芦山',3,'0835','625600','中国,四川省,雅安市,芦山县',102.928,30.1437,'Lushan',NULL,NULL),(511827,'宝兴县',511800,'宝兴',3,'0835','625700','中国,四川省,雅安市,宝兴县',102.816,30.3684,'Baoxing',NULL,NULL),(511900,'巴中市',510000,'巴中',2,'0827','636000','中国,四川省,巴中市',106.754,31.8588,'Bazhong',NULL,NULL),(511902,'巴州区',511900,'巴州',3,'0827','636001','中国,四川省,巴中市,巴州区',106.769,31.8512,'Bazhou',NULL,NULL),(511903,'恩阳区',511900,'恩阳',3,'0827','636064','中国,四川省,巴中市,恩阳区',106.754,31.8588,'Enyang',NULL,NULL),(511921,'通江县',511900,'通江',3,'0827','636700','中国,四川省,巴中市,通江县',107.244,31.9129,'Tongjiang',NULL,NULL),(511922,'南江县',511900,'南江',3,'0827','636600','中国,四川省,巴中市,南江县',106.842,32.3534,'Nanjiang',NULL,NULL),(511923,'平昌县',511900,'平昌',3,'0827','636400','中国,四川省,巴中市,平昌县',107.104,31.5594,'Pingchang',NULL,NULL),(512000,'资阳市',510000,'资阳',2,'028','641300','中国,四川省,资阳市',104.642,30.1222,'Ziyang',NULL,NULL),(512002,'雁江区',512000,'雁江',3,'028','641300','中国,四川省,资阳市,雁江区',104.652,30.1152,'Yanjiang',NULL,NULL),(512021,'安岳县',512000,'安岳',3,'028','642350','中国,四川省,资阳市,安岳县',105.336,30.0979,'Anyue',NULL,NULL),(512022,'乐至县',512000,'乐至',3,'028','641500','中国,四川省,资阳市,乐至县',105.032,30.2723,'Lezhi',NULL,NULL),(512081,'简阳市',512000,'简阳',3,'028','641400','中国,四川省,资阳市,简阳市',104.549,30.3904,'Jianyang',NULL,NULL),(513200,'阿坝藏族羌族自治州',510000,'阿坝',2,'0837','624000','中国,四川省,阿坝藏族羌族自治州',102.221,31.8998,'Aba',NULL,NULL),(513221,'汶川县',513200,'汶川',3,'0837','623000','中国,四川省,阿坝藏族羌族自治州,汶川县',103.591,31.4733,'Wenchuan',NULL,NULL),(513222,'理县',513200,'理县',3,'0837','623100','中国,四川省,阿坝藏族羌族自治州,理县',103.172,31.436,'Lixian',NULL,NULL),(513223,'茂县',513200,'茂县',3,'0837','623200','中国,四川省,阿坝藏族羌族自治州,茂县',103.854,31.682,'Maoxian',NULL,NULL),(513224,'松潘县',513200,'松潘',3,'0837','623300','中国,四川省,阿坝藏族羌族自治州,松潘县',103.599,32.6387,'Songpan',NULL,NULL),(513225,'九寨沟县',513200,'九寨沟',3,'0837','623400','中国,四川省,阿坝藏族羌族自治州,九寨沟县',104.237,33.2632,'Jiuzhaigou',NULL,NULL),(513226,'金川县',513200,'金川',3,'0837','624100','中国,四川省,阿坝藏族羌族自治州,金川县',102.066,31.4762,'Jinchuan',NULL,NULL),(513227,'小金县',513200,'小金',3,'0837','624200','中国,四川省,阿坝藏族羌族自治州,小金县',102.365,30.9993,'Xiaojin',NULL,NULL),(513228,'黑水县',513200,'黑水',3,'0837','623500','中国,四川省,阿坝藏族羌族自治州,黑水县',102.992,32.0618,'Heishui',NULL,NULL),(513229,'马尔康县',513200,'马尔康',3,'0837','624000','中国,四川省,阿坝藏族羌族自治州,马尔康县',102.206,31.9058,'Maerkang',NULL,NULL),(513230,'壤塘县',513200,'壤塘',3,'0837','624300','中国,四川省,阿坝藏族羌族自治州,壤塘县',100.978,32.2658,'Rangtang',NULL,NULL),(513231,'阿坝县',513200,'阿坝',3,'0837','624600','中国,四川省,阿坝藏族羌族自治州,阿坝县',101.706,32.903,'Aba',NULL,NULL),(513232,'若尔盖县',513200,'若尔盖',3,'0837','624500','中国,四川省,阿坝藏族羌族自治州,若尔盖县',102.96,33.5743,'Ruoergai',NULL,NULL),(513233,'红原县',513200,'红原',3,'0837','624400','中国,四川省,阿坝藏族羌族自治州,红原县',102.545,32.7899,'Hongyuan',NULL,NULL),(513300,'甘孜藏族自治州',510000,'甘孜',2,'0836','626000','中国,四川省,甘孜藏族自治州',101.964,30.0507,'Garze',NULL,NULL),(513321,'康定县',513300,'康定',3,'0836','626000','中国,四川省,甘孜藏族自治州,康定县',101.965,30.0553,'Kangding',NULL,NULL),(513322,'泸定县',513300,'泸定',3,'0836','626100','中国,四川省,甘孜藏族自治州,泸定县',102.235,29.9147,'Luding',NULL,NULL),(513323,'丹巴县',513300,'丹巴',3,'0836','626300','中国,四川省,甘孜藏族自治州,丹巴县',101.884,30.8766,'Danba',NULL,NULL),(513324,'九龙县',513300,'九龙',3,'0836','626200','中国,四川省,甘孜藏族自治州,九龙县',101.508,29.0009,'Jiulong',NULL,NULL),(513325,'雅江县',513300,'雅江',3,'0836','627450','中国,四川省,甘孜藏族自治州,雅江县',101.015,30.0328,'Yajiang',NULL,NULL),(513326,'道孚县',513300,'道孚',3,'0836','626400','中国,四川省,甘孜藏族自治州,道孚县',101.126,30.9805,'Daofu',NULL,NULL),(513327,'炉霍县',513300,'炉霍',3,'0836','626500','中国,四川省,甘孜藏族自治州,炉霍县',100.677,31.3917,'Luhuo',NULL,NULL),(513328,'甘孜县',513300,'甘孜',3,'0836','626700','中国,四川省,甘孜藏族自治州,甘孜县',99.9931,31.6267,'Ganzi',NULL,NULL),(513329,'新龙县',513300,'新龙',3,'0836','626800','中国,四川省,甘孜藏族自治州,新龙县',100.312,30.9407,'Xinlong',NULL,NULL),(513330,'德格县',513300,'德格',3,'0836','627250','中国,四川省,甘孜藏族自治州,德格县',98.5808,31.8062,'Dege',NULL,NULL),(513331,'白玉县',513300,'白玉',3,'0836','627150','中国,四川省,甘孜藏族自治州,白玉县',98.8257,31.209,'Baiyu',NULL,NULL),(513332,'石渠县',513300,'石渠',3,'0836','627350','中国,四川省,甘孜藏族自治州,石渠县',98.1016,32.9788,'Shiqu',NULL,NULL),(513333,'色达县',513300,'色达',3,'0836','626600','中国,四川省,甘孜藏族自治州,色达县',100.332,32.2684,'Seda',NULL,NULL),(513334,'理塘县',513300,'理塘',3,'0836','627550','中国,四川省,甘孜藏族自治州,理塘县',100.27,29.9967,'Litang',NULL,NULL),(513335,'巴塘县',513300,'巴塘',3,'0836','627650','中国,四川省,甘孜藏族自治州,巴塘县',99.1041,30.0042,'Batang',NULL,NULL),(513336,'乡城县',513300,'乡城',3,'0836','627850','中国,四川省,甘孜藏族自治州,乡城县',99.7994,28.9355,'Xiangcheng',NULL,NULL),(513337,'稻城县',513300,'稻城',3,'0836','627750','中国,四川省,甘孜藏族自治州,稻城县',100.298,29.0379,'Daocheng',NULL,NULL),(513338,'得荣县',513300,'得荣',3,'0836','627950','中国,四川省,甘孜藏族自治州,得荣县',99.2863,28.713,'Derong',NULL,NULL),(513400,'凉山彝族自治州',510000,'凉山',2,'0834','615000','中国,四川省,凉山彝族自治州',102.259,27.8868,'Liangshan',NULL,NULL),(513401,'西昌市',513400,'西昌',3,'0835','615000','中国,四川省,凉山彝族自治州,西昌市',102.264,27.8952,'Xichang',NULL,NULL),(513422,'木里藏族自治县',513400,'木里',3,'0851','615800','中国,四川省,凉山彝族自治州,木里藏族自治县',101.28,27.9287,'Muli',NULL,NULL),(513423,'盐源县',513400,'盐源',3,'0836','615700','中国,四川省,凉山彝族自治州,盐源县',101.51,27.4218,'Yanyuan',NULL,NULL),(513424,'德昌县',513400,'德昌',3,'0837','615500','中国,四川省,凉山彝族自治州,德昌县',102.18,27.4048,'Dechang',NULL,NULL),(513425,'会理县',513400,'会理',3,'0838','615100','中国,四川省,凉山彝族自治州,会理县',102.245,26.6563,'Huili',NULL,NULL),(513426,'会东县',513400,'会东',3,'0839','615200','中国,四川省,凉山彝族自治州,会东县',102.578,26.6343,'Huidong',NULL,NULL),(513427,'宁南县',513400,'宁南',3,'0840','615400','中国,四川省,凉山彝族自治州,宁南县',102.761,27.0657,'Ningnan',NULL,NULL),(513428,'普格县',513400,'普格',3,'0841','615300','中国,四川省,凉山彝族自治州,普格县',102.541,27.3748,'Puge',NULL,NULL),(513429,'布拖县',513400,'布拖',3,'0842','616350','中国,四川省,凉山彝族自治州,布拖县',102.812,27.7079,'Butuo',NULL,NULL),(513430,'金阳县',513400,'金阳',3,'0843','616250','中国,四川省,凉山彝族自治州,金阳县',103.248,27.697,'Jinyang',NULL,NULL),(513431,'昭觉县',513400,'昭觉',3,'0844','616150','中国,四川省,凉山彝族自治州,昭觉县',102.847,28.0116,'Zhaojue',NULL,NULL),(513432,'喜德县',513400,'喜德',3,'0845','616750','中国,四川省,凉山彝族自治州,喜德县',102.413,28.3074,'Xide',NULL,NULL),(513433,'冕宁县',513400,'冕宁',3,'0846','615600','中国,四川省,凉山彝族自治州,冕宁县',102.171,28.5516,'Mianning',NULL,NULL),(513434,'越西县',513400,'越西',3,'0847','616650','中国,四川省,凉山彝族自治州,越西县',102.508,28.6413,'Yuexi',NULL,NULL),(513435,'甘洛县',513400,'甘洛',3,'0848','616850','中国,四川省,凉山彝族自治州,甘洛县',102.772,28.9662,'Ganluo',NULL,NULL),(513436,'美姑县',513400,'美姑',3,'0849','616450','中国,四川省,凉山彝族自治州,美姑县',103.131,28.326,'Meigu',NULL,NULL),(513437,'雷波县',513400,'雷波',3,'0850','616550','中国,四川省,凉山彝族自治州,雷波县',103.573,28.2641,'Leibo',NULL,NULL),(520000,'贵州省',100000,'贵州',1,'','','中国,贵州省',106.713,26.5783,'Guizhou',NULL,NULL),(520100,'贵阳市',520000,'贵阳',2,'0851','550001','中国,贵州省,贵阳市',106.713,26.5783,'Guiyang',NULL,NULL),(520102,'南明区',520100,'南明',3,'0851','550001','中国,贵州省,贵阳市,南明区',106.715,26.5682,'Nanming',NULL,NULL),(520103,'云岩区',520100,'云岩',3,'0851','550001','中国,贵州省,贵阳市,云岩区',106.725,26.6048,'Yunyan',NULL,NULL),(520111,'花溪区',520100,'花溪',3,'0851','550025','中国,贵州省,贵阳市,花溪区',106.677,26.4334,'Huaxi',NULL,NULL),(520112,'乌当区',520100,'乌当',3,'0851','550018','中国,贵州省,贵阳市,乌当区',106.752,26.6302,'Wudang',NULL,NULL),(520113,'白云区',520100,'白云',3,'0851','550014','中国,贵州省,贵阳市,白云区',106.631,26.6828,'Baiyun',NULL,NULL),(520115,'观山湖区',520100,'观山湖',3,'0851','550009','中国,贵州省,贵阳市,观山湖区',106.625,26.6182,'Guanshanhu',NULL,NULL),(520121,'开阳县',520100,'开阳',3,'0851','550300','中国,贵州省,贵阳市,开阳县',106.969,27.0553,'Kaiyang',NULL,NULL),(520122,'息烽县',520100,'息烽',3,'0851','551100','中国,贵州省,贵阳市,息烽县',106.738,27.0935,'Xifeng',NULL,NULL),(520123,'修文县',520100,'修文',3,'0851','550200','中国,贵州省,贵阳市,修文县',106.595,26.8378,'Xiuwen',NULL,NULL),(520181,'清镇市',520100,'清镇',3,'0851','551400','中国,贵州省,贵阳市,清镇市',106.469,26.5526,'Qingzhen',NULL,NULL),(520200,'六盘水市',520000,'六盘水',2,'0858','553400','中国,贵州省,六盘水市',104.847,26.5846,'Liupanshui',NULL,NULL),(520201,'钟山区',520200,'钟山',3,'0858','553000','中国,贵州省,六盘水市,钟山区',104.878,26.577,'Zhongshan',NULL,NULL),(520203,'六枝特区',520200,'六枝',3,'0858','553400','中国,贵州省,六盘水市,六枝特区',105.481,26.2012,'Liuzhi',NULL,NULL),(520221,'水城县',520200,'水城',3,'0858','553000','中国,贵州省,六盘水市,水城县',104.958,26.5478,'Shuicheng',NULL,NULL),(520222,'盘县',520200,'盘县',3,'0858','561601','中国,贵州省,六盘水市,盘县',104.471,25.7136,'Panxian',NULL,NULL),(520300,'遵义市',520000,'遵义',2,'0852','563000','中国,贵州省,遵义市',106.937,27.7066,'Zunyi',NULL,NULL),(520302,'红花岗区',520300,'红花岗',3,'0852','563000','中国,贵州省,遵义市,红花岗区',106.894,27.6447,'Honghuagang',NULL,NULL),(520303,'汇川区',520300,'汇川',3,'0852','563000','中国,贵州省,遵义市,汇川区',106.939,27.7062,'Huichuan',NULL,NULL),(520321,'遵义县',520300,'遵义',3,'0852','563100','中国,贵州省,遵义市,遵义县',106.833,27.5377,'Zunyi',NULL,NULL),(520322,'桐梓县',520300,'桐梓',3,'0852','563200','中国,贵州省,遵义市,桐梓县',106.826,28.1381,'Tongzi',NULL,NULL),(520323,'绥阳县',520300,'绥阳',3,'0852','563300','中国,贵州省,遵义市,绥阳县',107.191,27.947,'Suiyang',NULL,NULL),(520324,'正安县',520300,'正安',3,'0852','563400','中国,贵州省,遵义市,正安县',107.444,28.5512,'Zheng\'an',NULL,NULL),(520325,'道真仡佬族苗族自治县',520300,'道真',3,'0852','563500','中国,贵州省,遵义市,道真仡佬族苗族自治县',107.612,28.864,'Daozhen',NULL,NULL),(520326,'务川仡佬族苗族自治县',520300,'务川',3,'0852','564300','中国,贵州省,遵义市,务川仡佬族苗族自治县',107.889,28.5223,'Wuchuan',NULL,NULL),(520327,'凤冈县',520300,'凤冈',3,'0852','564200','中国,贵州省,遵义市,凤冈县',107.717,27.9546,'Fenggang',NULL,NULL),(520328,'湄潭县',520300,'湄潭',3,'0852','564100','中国,贵州省,遵义市,湄潭县',107.488,27.7668,'Meitan',NULL,NULL),(520329,'余庆县',520300,'余庆',3,'0852','564400','中国,贵州省,遵义市,余庆县',107.888,27.2253,'Yuqing',NULL,NULL),(520330,'习水县',520300,'习水',3,'0852','564600','中国,贵州省,遵义市,习水县',106.213,28.3198,'Xishui',NULL,NULL),(520381,'赤水市',520300,'赤水',3,'0852','564700','中国,贵州省,遵义市,赤水市',105.698,28.5892,'Chishui',NULL,NULL),(520382,'仁怀市',520300,'仁怀',3,'0852','564500','中国,贵州省,遵义市,仁怀市',106.402,27.7923,'Renhuai',NULL,NULL),(520400,'安顺市',520000,'安顺',2,'0853','561000','中国,贵州省,安顺市',105.932,26.2455,'Anshun',NULL,NULL),(520402,'西秀区',520400,'西秀',3,'0853','561000','中国,贵州省,安顺市,西秀区',105.966,26.2449,'Xixiu',NULL,NULL),(520421,'平坝区',520400,'平坝',3,'0853','561100','中国,贵州省,安顺市,平坝区',106.257,26.4054,'Pingba',NULL,NULL),(520422,'普定县',520400,'普定',3,'0853','562100','中国,贵州省,安顺市,普定县',105.743,26.3014,'Puding',NULL,NULL),(520423,'镇宁布依族苗族自治县',520400,'镇宁',3,'0853','561200','中国,贵州省,安顺市,镇宁布依族苗族自治县',105.765,26.0553,'Zhenning',NULL,NULL),(520424,'关岭布依族苗族自治县',520400,'关岭',3,'0853','561300','中国,贵州省,安顺市,关岭布依族苗族自治县',105.619,25.9425,'Guanling',NULL,NULL),(520425,'紫云苗族布依族自治县',520400,'紫云',3,'0853','550800','中国,贵州省,安顺市,紫云苗族布依族自治县',106.084,25.7526,'Ziyun',NULL,NULL),(520500,'毕节市',520000,'毕节',2,'0857','551700','中国,贵州省,毕节市',105.285,27.3017,'Bijie',NULL,NULL),(520502,'七星关区',520500,'七星关',3,'0857','551700','中国,贵州省,毕节市,七星关区',104.95,27.1536,'Qixingguan',NULL,NULL),(520521,'大方县',520500,'大方',3,'0857','551600','中国,贵州省,毕节市,大方县',105.609,27.1435,'Dafang',NULL,NULL),(520522,'黔西县',520500,'黔西',3,'0857','551500','中国,贵州省,毕节市,黔西县',106.038,27.0249,'Qianxi',NULL,NULL),(520523,'金沙县',520500,'金沙',3,'0857','551800','中国,贵州省,毕节市,金沙县',106.222,27.4597,'Jinsha',NULL,NULL),(520524,'织金县',520500,'织金',3,'0857','552100','中国,贵州省,毕节市,织金县',105.769,26.6685,'Zhijin',NULL,NULL),(520525,'纳雍县',520500,'纳雍',3,'0857','553300','中国,贵州省,毕节市,纳雍县',105.375,26.7699,'Nayong',NULL,NULL),(520526,'威宁彝族回族苗族自治县',520500,'威宁',3,'0857','553100','中国,贵州省,毕节市,威宁彝族回族苗族自治县',104.287,26.8591,'Weining',NULL,NULL),(520527,'赫章县',520500,'赫章',3,'0857','553200','中国,贵州省,毕节市,赫章县',104.726,27.1192,'Hezhang',NULL,NULL),(520600,'铜仁市',520000,'铜仁',2,'0856','554300','中国,贵州省,铜仁市',109.192,27.7183,'Tongren',NULL,NULL),(520602,'碧江区',520600,'碧江',3,'0856','554300','中国,贵州省,铜仁市,碧江区',109.192,27.7183,'Bijiang',NULL,NULL),(520603,'万山区',520600,'万山',3,'0856','554200','中国,贵州省,铜仁市,万山区',109.212,27.519,'Wanshan',NULL,NULL),(520621,'江口县',520600,'江口',3,'0856','554400','中国,贵州省,铜仁市,江口县',108.848,27.6919,'Jiangkou',NULL,NULL),(520622,'玉屏侗族自治县',520600,'玉屏',3,'0856','554004','中国,贵州省,铜仁市,玉屏侗族自治县',108.918,27.238,'Yuping',NULL,NULL),(520623,'石阡县',520600,'石阡',3,'0856','555100','中国,贵州省,铜仁市,石阡县',108.23,27.5194,'Shiqian',NULL,NULL),(520624,'思南县',520600,'思南',3,'0856','565100','中国,贵州省,铜仁市,思南县',108.256,27.9413,'Sinan',NULL,NULL),(520625,'印江土家族苗族自治县',520600,'印江',3,'0856','555200','中国,贵州省,铜仁市,印江土家族苗族自治县',108.406,27.998,'Yinjiang',NULL,NULL),(520626,'德江县',520600,'德江',3,'0856','565200','中国,贵州省,铜仁市,德江县',108.117,28.2609,'Dejiang',NULL,NULL),(520627,'沿河土家族自治县',520600,'沿河',3,'0856','565300','中国,贵州省,铜仁市,沿河土家族自治县',108.496,28.5605,'Yuanhe',NULL,NULL),(520628,'松桃苗族自治县',520600,'松桃',3,'0856','554100','中国,贵州省,铜仁市,松桃苗族自治县',109.203,28.1654,'Songtao',NULL,NULL),(522300,'黔西南布依族苗族自治州',520000,'黔西南',2,'0859','562400','中国,贵州省,黔西南布依族苗族自治州',104.898,25.0881,'Qianxinan',NULL,NULL),(522301,'兴义市 ',522300,'兴义',3,'0859','562400','中国,贵州省,黔西南布依族苗族自治州,兴义市 ',104.895,25.0921,'Xingyi',NULL,NULL),(522322,'兴仁县',522300,'兴仁',3,'0859','562300','中国,贵州省,黔西南布依族苗族自治州,兴仁县',105.187,25.4328,'Xingren',NULL,NULL),(522323,'普安县',522300,'普安',3,'0859','561500','中国,贵州省,黔西南布依族苗族自治州,普安县',104.955,25.786,'Pu\'an',NULL,NULL),(522324,'晴隆县',522300,'晴隆',3,'0859','561400','中国,贵州省,黔西南布依族苗族自治州,晴隆县',105.219,25.8352,'Qinglong',NULL,NULL),(522325,'贞丰县',522300,'贞丰',3,'0859','562200','中国,贵州省,黔西南布依族苗族自治州,贞丰县',105.655,25.3846,'Zhenfeng',NULL,NULL),(522326,'望谟县',522300,'望谟',3,'0859','552300','中国,贵州省,黔西南布依族苗族自治州,望谟县',106.1,25.1782,'Wangmo',NULL,NULL),(522327,'册亨县',522300,'册亨',3,'0859','552200','中国,贵州省,黔西南布依族苗族自治州,册亨县',105.812,24.9838,'Ceheng',NULL,NULL),(522328,'安龙县',522300,'安龙',3,'0859','552400','中国,贵州省,黔西南布依族苗族自治州,安龙县',105.443,25.0982,'Anlong',NULL,NULL),(522600,'黔东南苗族侗族自治州',520000,'黔东南',2,'0855','556000','中国,贵州省,黔东南苗族侗族自治州',107.977,26.5834,'Qiandongnan',NULL,NULL),(522601,'凯里市',522600,'凯里',3,'0855','556000','中国,贵州省,黔东南苗族侗族自治州,凯里市',107.981,26.5669,'Kaili',NULL,NULL),(522622,'黄平县',522600,'黄平',3,'0855','556100','中国,贵州省,黔东南苗族侗族自治州,黄平县',107.902,26.8957,'Huangping',NULL,NULL),(522623,'施秉县',522600,'施秉',3,'0855','556200','中国,贵州省,黔东南苗族侗族自治州,施秉县',108.126,27.035,'Shibing',NULL,NULL),(522624,'三穗县',522600,'三穗',3,'0855','556500','中国,贵州省,黔东南苗族侗族自治州,三穗县',108.671,26.9477,'Sansui',NULL,NULL),(522625,'镇远县',522600,'镇远',3,'0855','557700','中国,贵州省,黔东南苗族侗族自治州,镇远县',108.427,27.0493,'Zhenyuan',NULL,NULL),(522626,'岑巩县',522600,'岑巩',3,'0855','557800','中国,贵州省,黔东南苗族侗族自治州,岑巩县',108.819,27.1754,'Cengong',NULL,NULL),(522627,'天柱县',522600,'天柱',3,'0855','556600','中国,贵州省,黔东南苗族侗族自治州,天柱县',109.207,26.9078,'Tianzhu',NULL,NULL),(522628,'锦屏县',522600,'锦屏',3,'0855','556700','中国,贵州省,黔东南苗族侗族自治州,锦屏县',109.2,26.6763,'Jinping',NULL,NULL),(522629,'剑河县',522600,'剑河',3,'0855','556400','中国,贵州省,黔东南苗族侗族自治州,剑河县',108.591,26.6525,'Jianhe',NULL,NULL),(522630,'台江县',522600,'台江',3,'0855','556300','中国,贵州省,黔东南苗族侗族自治州,台江县',108.318,26.6692,'Taijiang',NULL,NULL),(522631,'黎平县',522600,'黎平',3,'0855','557300','中国,贵州省,黔东南苗族侗族自治州,黎平县',109.136,26.2311,'Liping',NULL,NULL),(522632,'榕江县',522600,'榕江',3,'0855','557200','中国,贵州省,黔东南苗族侗族自治州,榕江县',108.521,25.9242,'Rongjiang',NULL,NULL),(522633,'从江县',522600,'从江',3,'0855','557400','中国,贵州省,黔东南苗族侗族自治州,从江县',108.905,25.7542,'Congjiang',NULL,NULL),(522634,'雷山县',522600,'雷山',3,'0855','557100','中国,贵州省,黔东南苗族侗族自治州,雷山县',108.077,26.3839,'Leishan',NULL,NULL),(522635,'麻江县',522600,'麻江',3,'0855','557600','中国,贵州省,黔东南苗族侗族自治州,麻江县',107.592,26.4923,'Majiang',NULL,NULL),(522636,'丹寨县',522600,'丹寨',3,'0855','557500','中国,贵州省,黔东南苗族侗族自治州,丹寨县',107.797,26.1982,'Danzhai',NULL,NULL),(522700,'黔南布依族苗族自治州',520000,'黔南',2,'0854','558000','中国,贵州省,黔南布依族苗族自治州',107.517,26.2582,'Qiannan',NULL,NULL),(522701,'都匀市',522700,'都匀',3,'0854','558000','中国,贵州省,黔南布依族苗族自治州,都匀市',107.519,26.2594,'Duyun',NULL,NULL),(522702,'福泉市',522700,'福泉',3,'0854','550500','中国,贵州省,黔南布依族苗族自治州,福泉市',107.517,26.6799,'Fuquan',NULL,NULL),(522722,'荔波县',522700,'荔波',3,'0854','558400','中国,贵州省,黔南布依族苗族自治州,荔波县',107.886,25.4139,'Libo',NULL,NULL),(522723,'贵定县',522700,'贵定',3,'0854','551300','中国,贵州省,黔南布依族苗族自治州,贵定县',107.237,26.5781,'Guiding',NULL,NULL),(522725,'瓮安县',522700,'瓮安',3,'0854','550400','中国,贵州省,黔南布依族苗族自治州,瓮安县',107.476,27.0681,'Weng\'an',NULL,NULL),(522726,'独山县',522700,'独山',3,'0854','558200','中国,贵州省,黔南布依族苗族自治州,独山县',107.541,25.8245,'Dushan',NULL,NULL),(522727,'平塘县',522700,'平塘',3,'0854','558300','中国,贵州省,黔南布依族苗族自治州,平塘县',107.324,25.8329,'Pingtang',NULL,NULL),(522728,'罗甸县',522700,'罗甸',3,'0854','550100','中国,贵州省,黔南布依族苗族自治州,罗甸县',106.752,25.4259,'Luodian',NULL,NULL),(522729,'长顺县',522700,'长顺',3,'0854','550700','中国,贵州省,黔南布依族苗族自治州,长顺县',106.452,26.023,'Changshun',NULL,NULL),(522730,'龙里县',522700,'龙里',3,'0854','551200','中国,贵州省,黔南布依族苗族自治州,龙里县',106.977,26.4508,'Longli',NULL,NULL),(522731,'惠水县',522700,'惠水',3,'0854','550600','中国,贵州省,黔南布依族苗族自治州,惠水县',106.659,26.1339,'Huishui',NULL,NULL),(522732,'三都水族自治县',522700,'三都',3,'0854','558100','中国,贵州省,黔南布依族苗族自治州,三都水族自治县',107.875,25.9856,'Sandu',NULL,NULL),(530000,'云南省',100000,'云南',1,'','','中国,云南省',102.712,25.0406,'Yunnan',NULL,NULL),(530100,'昆明市',530000,'昆明',2,'0871','650500','中国,云南省,昆明市',102.712,25.0406,'Kunming',NULL,NULL),(530102,'五华区',530100,'五华',3,'0871','650021','中国,云南省,昆明市,五华区',102.708,25.0352,'Wuhua',NULL,NULL),(530103,'盘龙区',530100,'盘龙',3,'0871','650051','中国,云南省,昆明市,盘龙区',102.72,25.0405,'Panlong',NULL,NULL),(530111,'官渡区',530100,'官渡',3,'0871','650200','中国,云南省,昆明市,官渡区',102.744,25.015,'Guandu',NULL,NULL),(530112,'西山区',530100,'西山',3,'0871','650118','中国,云南省,昆明市,西山区',102.665,25.038,'Xishan',NULL,NULL),(530113,'东川区',530100,'东川',3,'0871','654100','中国,云南省,昆明市,东川区',103.188,26.083,'Dongchuan',NULL,NULL),(530114,'呈贡区',530100,'呈贡',3,'0871','650500','中国,云南省,昆明市,呈贡区',102.801,24.8893,'Chenggong',NULL,NULL),(530122,'晋宁县',530100,'晋宁',3,'0871','650600','中国,云南省,昆明市,晋宁县',102.594,24.6665,'Jinning',NULL,NULL),(530124,'富民县',530100,'富民',3,'0871','650400','中国,云南省,昆明市,富民县',102.498,25.2212,'Fumin',NULL,NULL),(530125,'宜良县',530100,'宜良',3,'0871','652100','中国,云南省,昆明市,宜良县',103.141,24.917,'Yiliang',NULL,NULL),(530126,'石林彝族自治县',530100,'石林',3,'0871','652200','中国,云南省,昆明市,石林彝族自治县',103.271,24.759,'Shilin',NULL,NULL),(530127,'嵩明县',530100,'嵩明',3,'0871','651700','中国,云南省,昆明市,嵩明县',103.037,25.3399,'Songming',NULL,NULL),(530128,'禄劝彝族苗族自治县',530100,'禄劝',3,'0871','651500','中国,云南省,昆明市,禄劝彝族苗族自治县',102.467,25.5539,'Luquan',NULL,NULL),(530129,'寻甸回族彝族自治县 ',530100,'寻甸',3,'0871','655200','中国,云南省,昆明市,寻甸回族彝族自治县 ',103.256,25.5596,'Xundian',NULL,NULL),(530181,'安宁市',530100,'安宁',3,'0871','650300','中国,云南省,昆明市,安宁市',102.47,24.9165,'Anning',NULL,NULL),(530300,'曲靖市',530000,'曲靖',2,'0874','655000','中国,云南省,曲靖市',103.798,25.5016,'Qujing',NULL,NULL),(530302,'麒麟区',530300,'麒麟',3,'0874','655000','中国,云南省,曲靖市,麒麟区',103.805,25.4951,'Qilin',NULL,NULL),(530321,'马龙县',530300,'马龙',3,'0874','655100','中国,云南省,曲靖市,马龙县',103.579,25.4252,'Malong',NULL,NULL),(530322,'陆良县',530300,'陆良',3,'0874','655600','中国,云南省,曲靖市,陆良县',103.666,25.0233,'Luliang',NULL,NULL),(530323,'师宗县',530300,'师宗',3,'0874','655700','中国,云南省,曲靖市,师宗县',103.991,24.8282,'Shizong',NULL,NULL),(530324,'罗平县',530300,'罗平',3,'0874','655800','中国,云南省,曲靖市,罗平县',104.309,24.8844,'Luoping',NULL,NULL),(530325,'富源县',530300,'富源',3,'0874','655500','中国,云南省,曲靖市,富源县',104.254,25.6659,'Fuyuan',NULL,NULL),(530326,'会泽县',530300,'会泽',3,'0874','654200','中国,云南省,曲靖市,会泽县',103.3,26.4108,'Huize',NULL,NULL),(530328,'沾益县',530300,'沾益',3,'0874','655331','中国,云南省,曲靖市,沾益县',103.821,25.6071,'Zhanyi',NULL,NULL),(530381,'宣威市',530300,'宣威',3,'0874','655400','中国,云南省,曲靖市,宣威市',104.104,26.2173,'Xuanwei',NULL,NULL),(530400,'玉溪市',530000,'玉溪',2,'0877','653100','中国,云南省,玉溪市',102.544,24.3505,'Yuxi',NULL,NULL),(530402,'红塔区',530400,'红塔',3,'0877','653100','中国,云南省,玉溪市,红塔区',102.545,24.3541,'Hongta',NULL,NULL),(530421,'江川县',530400,'江川',3,'0877','652600','中国,云南省,玉溪市,江川县',102.754,24.2886,'Jiangchuan',NULL,NULL),(530422,'澄江县',530400,'澄江',3,'0877','652500','中国,云南省,玉溪市,澄江县',102.908,24.6738,'Chengjiang',NULL,NULL),(530423,'通海县',530400,'通海',3,'0877','652700','中国,云南省,玉溪市,通海县',102.766,24.1136,'Tonghai',NULL,NULL),(530424,'华宁县',530400,'华宁',3,'0877','652800','中国,云南省,玉溪市,华宁县',102.928,24.1926,'Huaning',NULL,NULL),(530425,'易门县',530400,'易门',3,'0877','651100','中国,云南省,玉溪市,易门县',102.164,24.6712,'Yimen',NULL,NULL),(530426,'峨山彝族自治县',530400,'峨山',3,'0877','653200','中国,云南省,玉溪市,峨山彝族自治县',102.406,24.169,'Eshan',NULL,NULL),(530427,'新平彝族傣族自治县',530400,'新平',3,'0877','653400','中国,云南省,玉溪市,新平彝族傣族自治县',101.989,24.0689,'Xinping',NULL,NULL),(530428,'元江哈尼族彝族傣族自治县',530400,'元江',3,'0877','653300','中国,云南省,玉溪市,元江哈尼族彝族傣族自治县',101.998,23.5965,'Yuanjiang',NULL,NULL),(530500,'保山市',530000,'保山',2,'0875','678000','中国,云南省,保山市',99.1671,25.1118,'Baoshan',NULL,NULL),(530502,'隆阳区',530500,'隆阳',3,'0875','678000','中国,云南省,保山市,隆阳区',99.1633,25.1116,'Longyang',NULL,NULL),(530521,'施甸县',530500,'施甸',3,'0875','678200','中国,云南省,保山市,施甸县',99.1877,24.7242,'Shidian',NULL,NULL),(530522,'腾冲县',530500,'腾冲',3,'0875','679100','中国,云南省,保山市,腾冲县',98.4941,25.0254,'Tengchong',NULL,NULL),(530523,'龙陵县',530500,'龙陵',3,'0875','678300','中国,云南省,保山市,龙陵县',98.6902,24.5875,'Longling',NULL,NULL),(530524,'昌宁县',530500,'昌宁',3,'0875','678100','中国,云南省,保山市,昌宁县',99.6036,24.8276,'Changning',NULL,NULL),(530600,'昭通市',530000,'昭通',2,'0870','657000','中国,云南省,昭通市',103.717,27.337,'Zhaotong',NULL,NULL),(530602,'昭阳区',530600,'昭阳',3,'0870','657000','中国,云南省,昭通市,昭阳区',103.707,27.32,'Zhaoyang',NULL,NULL),(530621,'鲁甸县',530600,'鲁甸',3,'0870','657100','中国,云南省,昭通市,鲁甸县',103.547,27.1924,'Ludian',NULL,NULL),(530622,'巧家县',530600,'巧家',3,'0870','654600','中国,云南省,昭通市,巧家县',102.924,26.9124,'Qiaojia',NULL,NULL),(530623,'盐津县',530600,'盐津',3,'0870','657500','中国,云南省,昭通市,盐津县',104.235,28.1086,'Yanjin',NULL,NULL),(530624,'大关县',530600,'大关',3,'0870','657400','中国,云南省,昭通市,大关县',103.893,27.7488,'Daguan',NULL,NULL),(530625,'永善县',530600,'永善',3,'0870','657300','中国,云南省,昭通市,永善县',103.635,28.2279,'Yongshan',NULL,NULL),(530626,'绥江县',530600,'绥江',3,'0870','657700','中国,云南省,昭通市,绥江县',103.949,28.5966,'Suijiang',NULL,NULL),(530627,'镇雄县',530600,'镇雄',3,'0870','657200','中国,云南省,昭通市,镇雄县',104.873,27.4398,'Zhenxiong',NULL,NULL),(530628,'彝良县',530600,'彝良',3,'0870','657600','中国,云南省,昭通市,彝良县',104.05,27.6281,'Yiliang',NULL,NULL),(530629,'威信县',530600,'威信',3,'0870','657900','中国,云南省,昭通市,威信县',105.048,27.8407,'Weixin',NULL,NULL),(530630,'水富县',530600,'水富',3,'0870','657800','中国,云南省,昭通市,水富县',104.416,28.6299,'Shuifu',NULL,NULL),(530700,'丽江市',530000,'丽江',2,'0888','674100','中国,云南省,丽江市',100.233,26.8721,'Lijiang',NULL,NULL),(530702,'古城区',530700,'古城',3,'0888','674100','中国,云南省,丽江市,古城区',100.226,26.877,'Gucheng',NULL,NULL),(530721,'玉龙纳西族自治县',530700,'玉龙',3,'0888','674100','中国,云南省,丽江市,玉龙纳西族自治县',100.237,26.8215,'Yulong',NULL,NULL),(530722,'永胜县',530700,'永胜',3,'0888','674200','中国,云南省,丽江市,永胜县',100.747,26.6859,'Yongsheng',NULL,NULL),(530723,'华坪县',530700,'华坪',3,'0888','674800','中国,云南省,丽江市,华坪县',101.266,26.6297,'Huaping',NULL,NULL),(530724,'宁蒗彝族自治县',530700,'宁蒗',3,'0888','674300','中国,云南省,丽江市,宁蒗彝族自治县',100.851,27.2818,'Ninglang',NULL,NULL),(530800,'普洱市',530000,'普洱',2,'0879','665000','中国,云南省,普洱市',100.972,22.7773,'Pu\'er',NULL,NULL),(530802,'思茅区',530800,'思茅',3,'0879','665000','中国,云南省,普洱市,思茅区',100.977,22.7869,'Simao',NULL,NULL),(530821,'宁洱哈尼族彝族自治县',530800,'宁洱',3,'0879','665100','中国,云南省,普洱市,宁洱哈尼族彝族自治县',101.047,23.0634,'Ninger',NULL,NULL),(530822,'墨江哈尼族自治县',530800,'墨江',3,'0879','654800','中国,云南省,普洱市,墨江哈尼族自治县',101.692,23.4321,'Mojiang',NULL,NULL),(530823,'景东彝族自治县',530800,'景东',3,'0879','676200','中国,云南省,普洱市,景东彝族自治县',100.836,24.4479,'Jingdong',NULL,NULL),(530824,'景谷傣族彝族自治县',530800,'景谷',3,'0879','666400','中国,云南省,普洱市,景谷傣族彝族自治县',100.703,23.497,'Jinggu',NULL,NULL),(530825,'镇沅彝族哈尼族拉祜族自治县',530800,'镇沅',3,'0879','666500','中国,云南省,普洱市,镇沅彝族哈尼族拉祜族自治县',101.107,24.0056,'Zhenyuan',NULL,NULL),(530826,'江城哈尼族彝族自治县',530800,'江城',3,'0879','665900','中国,云南省,普洱市,江城哈尼族彝族自治县',101.858,22.5842,'Jiangcheng',NULL,NULL),(530827,'孟连傣族拉祜族佤族自治县',530800,'孟连',3,'0879','665800','中国,云南省,普洱市,孟连傣族拉祜族佤族自治县',99.5842,22.3292,'Menglian',NULL,NULL),(530828,'澜沧拉祜族自治县',530800,'澜沧',3,'0879','665600','中国,云南省,普洱市,澜沧拉祜族自治县',99.9359,22.5547,'Lancang',NULL,NULL),(530829,'西盟佤族自治县',530800,'西盟',3,'0879','665700','中国,云南省,普洱市,西盟佤族自治县',99.5987,22.6477,'Ximeng',NULL,NULL),(530900,'临沧市',530000,'临沧',2,'0883','677000','中国,云南省,临沧市',100.087,23.8866,'Lincang',NULL,NULL),(530902,'临翔区',530900,'临翔',3,'0883','677000','中国,云南省,临沧市,临翔区',100.082,23.895,'Linxiang',NULL,NULL),(530921,'凤庆县',530900,'凤庆',3,'0883','675900','中国,云南省,临沧市,凤庆县',99.9284,24.5803,'Fengqing',NULL,NULL),(530922,'云县',530900,'云县',3,'0883','675800','中国,云南省,临沧市,云县',100.128,24.4468,'Yunxian',NULL,NULL),(530923,'永德县',530900,'永德',3,'0883','677600','中国,云南省,临沧市,永德县',99.2533,24.0276,'Yongde',NULL,NULL),(530924,'镇康县',530900,'镇康',3,'0883','677704','中国,云南省,临沧市,镇康县',98.8255,23.7624,'Zhenkang',NULL,NULL),(530925,'双江拉祜族佤族布朗族傣族自治县',530900,'双江',3,'0883','677300','中国,云南省,临沧市,双江拉祜族佤族布朗族傣族自治县',99.8277,23.4735,'Shuangjiang',NULL,NULL),(530926,'耿马傣族佤族自治县',530900,'耿马',3,'0883','677500','中国,云南省,临沧市,耿马傣族佤族自治县',99.3979,23.5378,'Gengma',NULL,NULL),(530927,'沧源佤族自治县',530900,'沧源',3,'0883','677400','中国,云南省,临沧市,沧源佤族自治县',99.2455,23.1482,'Cangyuan',NULL,NULL),(532300,'楚雄彝族自治州',530000,'楚雄',2,'0878','675000','中国,云南省,楚雄彝族自治州',101.546,25.042,'Chuxiong',NULL,NULL),(532301,'楚雄市',532300,'楚雄',3,'0878','675000','中国,云南省,楚雄彝族自治州,楚雄市',101.546,25.0329,'Chuxiong',NULL,NULL),(532322,'双柏县',532300,'双柏',3,'0878','675100','中国,云南省,楚雄彝族自治州,双柏县',101.642,24.6888,'Shuangbai',NULL,NULL),(532323,'牟定县',532300,'牟定',3,'0878','675500','中国,云南省,楚雄彝族自治州,牟定县',101.54,25.3155,'Mouding',NULL,NULL),(532324,'南华县',532300,'南华',3,'0878','675200','中国,云南省,楚雄彝族自治州,南华县',101.273,25.1929,'Nanhua',NULL,NULL),(532325,'姚安县',532300,'姚安',3,'0878','675300','中国,云南省,楚雄彝族自治州,姚安县',101.243,25.5047,'Yao\'an',NULL,NULL),(532326,'大姚县',532300,'大姚',3,'0878','675400','中国,云南省,楚雄彝族自治州,大姚县',101.324,25.7222,'Dayao',NULL,NULL),(532327,'永仁县',532300,'永仁',3,'0878','651400','中国,云南省,楚雄彝族自治州,永仁县',101.672,26.0579,'Yongren',NULL,NULL),(532328,'元谋县',532300,'元谋',3,'0878','651300','中国,云南省,楚雄彝族自治州,元谋县',101.877,25.7044,'Yuanmou',NULL,NULL),(532329,'武定县',532300,'武定',3,'0878','651600','中国,云南省,楚雄彝族自治州,武定县',102.404,25.5295,'Wuding',NULL,NULL),(532331,'禄丰县',532300,'禄丰',3,'0878','651200','中国,云南省,楚雄彝族自治州,禄丰县',102.078,25.1481,'Lufeng',NULL,NULL),(532500,'红河哈尼族彝族自治州',530000,'红河',2,'0873','661400','中国,云南省,红河哈尼族彝族自治州',103.384,23.3668,'Honghe',NULL,NULL),(532501,'个旧市',532500,'个旧',3,'0873','661000','中国,云南省,红河哈尼族彝族自治州,个旧市',103.16,23.3589,'Gejiu',NULL,NULL),(532502,'开远市',532500,'开远',3,'0873','661600','中国,云南省,红河哈尼族彝族自治州,开远市',103.27,23.7101,'Kaiyuan',NULL,NULL),(532503,'蒙自市',532500,'蒙自',3,'0873','661101','中国,云南省,红河哈尼族彝族自治州,蒙自市',103.385,23.3668,'Mengzi',NULL,NULL),(532504,'弥勒市',532500,'弥勒',3,'0873','652300','中国,云南省,红河哈尼族彝族自治州,弥勒市',103.437,24.4084,'Mile ',NULL,NULL),(532523,'屏边苗族自治县',532500,'屏边',3,'0873','661200','中国,云南省,红河哈尼族彝族自治州,屏边苗族自治县',103.686,22.9842,'Pingbian',NULL,NULL),(532524,'建水县',532500,'建水',3,'0873','654300','中国,云南省,红河哈尼族彝族自治州,建水县',102.827,23.6347,'Jianshui',NULL,NULL),(532525,'石屏县',532500,'石屏',3,'0873','662200','中国,云南省,红河哈尼族彝族自治州,石屏县',102.494,23.7144,'Shiping',NULL,NULL),(532527,'泸西县',532500,'泸西',3,'0873','652400','中国,云南省,红河哈尼族彝族自治州,泸西县',103.764,24.5285,'Luxi',NULL,NULL),(532528,'元阳县',532500,'元阳',3,'0873','662400','中国,云南省,红河哈尼族彝族自治州,元阳县',102.833,23.2228,'Yuanyang',NULL,NULL),(532529,'红河县',532500,'红河县',3,'0873','654400','中国,云南省,红河哈尼族彝族自治州,红河县',102.421,23.3677,'Honghexian',NULL,NULL),(532530,'金平苗族瑶族傣族自治县',532500,'金平',3,'0873','661500','中国,云南省,红河哈尼族彝族自治州,金平苗族瑶族傣族自治县',103.227,22.7796,'Jinping',NULL,NULL),(532531,'绿春县',532500,'绿春',3,'0873','662500','中国,云南省,红河哈尼族彝族自治州,绿春县',102.397,22.9937,'Lvchun',NULL,NULL),(532532,'河口瑶族自治县',532500,'河口',3,'0873','661300','中国,云南省,红河哈尼族彝族自治州,河口瑶族自治县',103.939,22.5293,'Hekou',NULL,NULL),(532600,'文山壮族苗族自治州',530000,'文山',2,'0876','663000','中国,云南省,文山壮族苗族自治州',104.244,23.3695,'Wenshan',NULL,NULL),(532601,'文山市',532600,'文山',3,'0876','663000','中国,云南省,文山壮族苗族自治州,文山市',104.244,23.3692,'Wenshan',NULL,NULL),(532622,'砚山县',532600,'砚山',3,'0876','663100','中国,云南省,文山壮族苗族自治州,砚山县',104.333,23.6072,'Yanshan',NULL,NULL),(532623,'西畴县',532600,'西畴',3,'0876','663500','中国,云南省,文山壮族苗族自治州,西畴县',104.674,23.4394,'Xichou',NULL,NULL),(532624,'麻栗坡县',532600,'麻栗坡',3,'0876','663600','中国,云南省,文山壮族苗族自治州,麻栗坡县',104.701,23.1203,'Malipo',NULL,NULL),(532625,'马关县',532600,'马关',3,'0876','663700','中国,云南省,文山壮族苗族自治州,马关县',104.395,23.0129,'Maguan',NULL,NULL),(532626,'丘北县',532600,'丘北',3,'0876','663200','中国,云南省,文山壮族苗族自治州,丘北县',104.193,24.0396,'Qiubei',NULL,NULL),(532627,'广南县',532600,'广南',3,'0876','663300','中国,云南省,文山壮族苗族自治州,广南县',105.055,24.0464,'Guangnan',NULL,NULL),(532628,'富宁县',532600,'富宁',3,'0876','663400','中国,云南省,文山壮族苗族自治州,富宁县',105.631,23.6254,'Funing',NULL,NULL),(532800,'西双版纳傣族自治州',530000,'西双版纳',2,'0691','666100','中国,云南省,西双版纳傣族自治州',100.798,22.0017,'Xishuangbanna',NULL,NULL),(532801,'景洪市',532800,'景洪',3,'0691','666100','中国,云南省,西双版纳傣族自治州,景洪市',100.8,22.0107,'Jinghong',NULL,NULL),(532822,'勐海县',532800,'勐海',3,'0691','666200','中国,云南省,西双版纳傣族自治州,勐海县',100.449,21.9618,'Menghai',NULL,NULL),(532823,'勐腊县',532800,'勐腊',3,'0691','666300','中国,云南省,西双版纳傣族自治州,勐腊县',101.565,21.4816,'Mengla',NULL,NULL),(532900,'大理白族自治州',530000,'大理',2,'0872','671000','中国,云南省,大理白族自治州',100.24,25.5928,'Dali',NULL,NULL),(532901,'大理市',532900,'大理',3,'0872','671000','中国,云南省,大理白族自治州,大理市',100.23,25.5916,'Dali',NULL,NULL),(532922,'漾濞彝族自治县',532900,'漾濞',3,'0872','672500','中国,云南省,大理白族自治州,漾濞彝族自治县',99.9547,25.6652,'Yangbi',NULL,NULL),(532923,'祥云县',532900,'祥云',3,'0872','672100','中国,云南省,大理白族自治州,祥云县',100.558,25.4734,'Xiangyun',NULL,NULL),(532924,'宾川县',532900,'宾川',3,'0872','671600','中国,云南省,大理白族自治州,宾川县',100.577,25.8314,'Binchuan',NULL,NULL),(532925,'弥渡县',532900,'弥渡',3,'0872','675600','中国,云南省,大理白族自治州,弥渡县',100.491,25.3418,'Midu',NULL,NULL),(532926,'南涧彝族自治县',532900,'南涧',3,'0872','675700','中国,云南省,大理白族自治州,南涧彝族自治县',100.51,25.0435,'Nanjian',NULL,NULL),(532927,'巍山彝族回族自治县',532900,'巍山',3,'0872','672400','中国,云南省,大理白族自治州,巍山彝族回族自治县',100.306,25.232,'Weishan',NULL,NULL),(532928,'永平县',532900,'永平',3,'0872','672600','中国,云南省,大理白族自治州,永平县',99.5409,25.4645,'Yongping',NULL,NULL),(532929,'云龙县',532900,'云龙',3,'0872','672700','中国,云南省,大理白族自治州,云龙县',99.3726,25.885,'Yunlong',NULL,NULL),(532930,'洱源县',532900,'洱源',3,'0872','671200','中国,云南省,大理白族自治州,洱源县',99.949,26.1083,'Eryuan',NULL,NULL),(532931,'剑川县',532900,'剑川',3,'0872','671300','中国,云南省,大理白族自治州,剑川县',99.9054,26.5369,'Jianchuan',NULL,NULL),(532932,'鹤庆县',532900,'鹤庆',3,'0872','671500','中国,云南省,大理白族自治州,鹤庆县',100.177,26.558,'Heqing',NULL,NULL),(533100,'德宏傣族景颇族自治州',530000,'德宏',2,'0692','678400','中国,云南省,德宏傣族景颇族自治州',98.5784,24.4367,'Dehong',NULL,NULL),(533102,'瑞丽市',533100,'瑞丽',3,'0692','678600','中国,云南省,德宏傣族景颇族自治州,瑞丽市',97.8518,24.0128,'Ruili',NULL,NULL),(533103,'芒市',533100,'芒市',3,'0692','678400','中国,云南省,德宏傣族景颇族自治州,芒市',98.5886,24.4337,'Mangshi',NULL,NULL),(533122,'梁河县',533100,'梁河',3,'0692','679200','中国,云南省,德宏傣族景颇族自治州,梁河县',98.2971,24.8066,'Lianghe',NULL,NULL),(533123,'盈江县',533100,'盈江',3,'0692','679300','中国,云南省,德宏傣族景颇族自治州,盈江县',97.9318,24.7058,'Yingjiang',NULL,NULL),(533124,'陇川县',533100,'陇川',3,'0692','678700','中国,云南省,德宏傣族景颇族自治州,陇川县',97.792,24.183,'Longchuan',NULL,NULL),(533300,'怒江傈僳族自治州',530000,'怒江',2,'0886','673100','中国,云南省,怒江傈僳族自治州',98.8543,25.8509,'Nujiang',NULL,NULL),(533321,'泸水县',533300,'泸水',3,'0886','673100','中国,云南省,怒江傈僳族自治州,泸水县',98.8553,25.8377,'Lushui',NULL,NULL),(533323,'福贡县',533300,'福贡',3,'0886','673400','中国,云南省,怒江傈僳族自治州,福贡县',98.8697,26.9037,'Fugong',NULL,NULL),(533324,'贡山独龙族怒族自治县',533300,'贡山',3,'0886','673500','中国,云南省,怒江傈僳族自治州,贡山独龙族怒族自治县',98.6658,27.7409,'Gongshan',NULL,NULL),(533325,'兰坪白族普米族自治县',533300,'兰坪',3,'0886','671400','中国,云南省,怒江傈僳族自治州,兰坪白族普米族自治县',99.4189,26.4525,'Lanping',NULL,NULL),(533400,'迪庆藏族自治州',530000,'迪庆',2,'0887','674400','中国,云南省,迪庆藏族自治州',99.7065,27.8269,'Deqen',NULL,NULL),(533421,'香格里拉市',533400,'香格里拉',3,'0887','674400','中国,云南省,迪庆藏族自治州,香格里拉市',99.706,27.8231,'Xianggelila',NULL,NULL),(533422,'德钦县',533400,'德钦',3,'0887','674500','中国,云南省,迪庆藏族自治州,德钦县',98.9108,28.4863,'Deqin',NULL,NULL),(533423,'维西傈僳族自治县',533400,'维西',3,'0887','674600','中国,云南省,迪庆藏族自治州,维西傈僳族自治县',99.284,27.1793,'Weixi',NULL,NULL),(540000,'西藏自治区',100000,'西藏',1,'','','中国,西藏自治区',91.1322,29.6604,'Tibet',NULL,NULL),(540100,'拉萨市',540000,'拉萨',2,'0891','850000','中国,西藏自治区,拉萨市',91.1322,29.6604,'Lhasa',NULL,NULL),(540102,'城关区',540100,'城关',3,'0891','850000','中国,西藏自治区,拉萨市,城关区',91.1386,29.6531,'Chengguan',NULL,NULL),(540121,'林周县',540100,'林周',3,'0891','851600','中国,西藏自治区,拉萨市,林周县',91.2586,29.8944,'Linzhou',NULL,NULL),(540122,'当雄县',540100,'当雄',3,'0891','851500','中国,西藏自治区,拉萨市,当雄县',91.1008,30.4831,'Dangxiong',NULL,NULL),(540123,'尼木县',540100,'尼木',3,'0891','851300','中国,西藏自治区,拉萨市,尼木县',90.1638,29.4335,'Nimu',NULL,NULL),(540124,'曲水县',540100,'曲水',3,'0891','850600','中国,西藏自治区,拉萨市,曲水县',90.7319,29.3564,'Qushui',NULL,NULL),(540125,'堆龙德庆县',540100,'堆龙德庆',3,'0891','851400','中国,西藏自治区,拉萨市,堆龙德庆县',91.0003,29.65,'Duilongdeqing',NULL,NULL),(540126,'达孜县',540100,'达孜',3,'0891','850100','中国,西藏自治区,拉萨市,达孜县',91.3576,29.6722,'Dazi',NULL,NULL),(540127,'墨竹工卡县',540100,'墨竹工卡',3,'0891','850200','中国,西藏自治区,拉萨市,墨竹工卡县',91.7281,29.8361,'Mozhugongka',NULL,NULL),(540200,'日喀则市',540000,'日喀则',2,'0892','857000','中国,西藏自治区,日喀则市',88.8849,29.2638,'Rikaze',NULL,NULL),(540202,'桑珠孜区',540200,'桑珠孜',3,'0892','857000','中国,西藏自治区,日喀则市,桑珠孜区',88.8804,29.2696,'Sangzhuzi',NULL,NULL),(540221,'南木林县',540200,'南木林',3,'0892','857100','中国,西藏自治区,日喀则市,南木林县',89.0969,29.6821,'Nanmulin',NULL,NULL),(540222,'江孜县',540200,'江孜',3,'0892','857400','中国,西藏自治区,日喀则市,江孜县',89.6026,28.9174,'Jiangzi',NULL,NULL),(540223,'定日县',540200,'定日',3,'0892','858200','中国,西藏自治区,日喀则市,定日县',87.1218,28.6613,'Dingri',NULL,NULL),(540224,'萨迦县',540200,'萨迦',3,'0892','857800','中国,西藏自治区,日喀则市,萨迦县',88.0219,28.903,'Sajia',NULL,NULL),(540225,'拉孜县',540200,'拉孜',3,'0892','858100','中国,西藏自治区,日喀则市,拉孜县',87.6341,29.085,'Lazi',NULL,NULL),(540226,'昂仁县',540200,'昂仁',3,'0892','858500','中国,西藏自治区,日喀则市,昂仁县',87.2386,29.295,'Angren',NULL,NULL),(540227,'谢通门县',540200,'谢通门',3,'0892','858900','中国,西藏自治区,日喀则市,谢通门县',88.2624,29.4334,'Xietongmen',NULL,NULL),(540228,'白朗县',540200,'白朗',3,'0892','857300','中国,西藏自治区,日喀则市,白朗县',89.262,29.1055,'Bailang',NULL,NULL),(540229,'仁布县',540200,'仁布',3,'0892','857200','中国,西藏自治区,日喀则市,仁布县',89.8423,29.2301,'Renbu',NULL,NULL),(540230,'康马县',540200,'康马',3,'0892','857500','中国,西藏自治区,日喀则市,康马县',89.6853,28.5567,'Kangma',NULL,NULL),(540231,'定结县',540200,'定结',3,'0892','857900','中国,西藏自治区,日喀则市,定结县',87.7726,28.364,'Dingjie',NULL,NULL),(540232,'仲巴县',540200,'仲巴',3,'0892','858800','中国,西藏自治区,日喀则市,仲巴县',84.0295,29.7659,'Zhongba',NULL,NULL),(540233,'亚东县',540200,'亚东',3,'0892','857600','中国,西藏自治区,日喀则市,亚东县',88.908,27.4839,'Yadong',NULL,NULL),(540234,'吉隆县',540200,'吉隆',3,'0892','858700','中国,西藏自治区,日喀则市,吉隆县',85.2985,28.8538,'Jilong',NULL,NULL),(540235,'聂拉木县',540200,'聂拉木',3,'0892','858300','中国,西藏自治区,日喀则市,聂拉木县',85.98,28.1565,'Nielamu',NULL,NULL),(540236,'萨嘎县',540200,'萨嘎',3,'0892','857800','中国,西藏自治区,日喀则市,萨嘎县',85.2341,29.3294,'Saga',NULL,NULL),(540237,'岗巴县',540200,'岗巴',3,'0892','857700','中国,西藏自治区,日喀则市,岗巴县',88.5207,28.275,'Gangba',NULL,NULL),(540300,'昌都市',540000,'昌都',2,'0895','854000','中国,西藏自治区,昌都市',97.1785,31.1369,'Qamdo',NULL,NULL),(540302,'卡若区',540300,'昌都',3,'0895','854000','中国,西藏自治区,昌都市,卡若区',97.1804,31.1385,'Karuo',NULL,NULL),(540321,'江达县',540300,'江达',3,'0895','854100','中国,西藏自治区,昌都市,江达县',98.2187,31.5034,'Jiangda',NULL,NULL),(540322,'贡觉县',540300,'贡觉',3,'0895','854200','中国,西藏自治区,昌都市,贡觉县',98.2716,30.8594,'Gongjue',NULL,NULL),(540323,'类乌齐县',540300,'类乌齐',3,'0895','855600','中国,西藏自治区,昌都市,类乌齐县',96.6002,31.2121,'Leiwuqi',NULL,NULL),(540324,'丁青县',540300,'丁青',3,'0895','855700','中国,西藏自治区,昌都市,丁青县',95.5936,31.4162,'Dingqing',NULL,NULL),(540325,'察雅县',540300,'察雅',3,'0895','854300','中国,西藏自治区,昌都市,察雅县',97.5652,30.6534,'Chaya',NULL,NULL),(540326,'八宿县',540300,'八宿',3,'0895','854600','中国,西藏自治区,昌都市,八宿县',96.9176,30.0535,'Basu',NULL,NULL),(540327,'左贡县',540300,'左贡',3,'0895','854400','中国,西藏自治区,昌都市,左贡县',97.8443,29.6711,'Zuogong',NULL,NULL),(540328,'芒康县',540300,'芒康',3,'0895','854500','中国,西藏自治区,昌都市,芒康县',98.5938,29.6795,'Mangkang',NULL,NULL),(540329,'洛隆县',540300,'洛隆',3,'0895','855400','中国,西藏自治区,昌都市,洛隆县',95.8264,30.7405,'Luolong',NULL,NULL),(540330,'边坝县',540300,'边坝',3,'0895','855500','中国,西藏自治区,昌都市,边坝县',94.7069,30.9343,'Bianba',NULL,NULL),(542200,'山南地区',540000,'山南',2,'0893','856000','中国,西藏自治区,山南地区',91.7665,29.236,'Shannan',NULL,NULL),(542221,'乃东县',542200,'乃东',3,'0893','856100','中国,西藏自治区,山南地区,乃东县',91.7615,29.2249,'Naidong',NULL,NULL),(542222,'扎囊县',542200,'扎囊',3,'0893','850800','中国,西藏自治区,山南地区,扎囊县',91.3329,29.2399,'Zhanang',NULL,NULL),(542223,'贡嘎县',542200,'贡嘎',3,'0893','850700','中国,西藏自治区,山南地区,贡嘎县',90.9887,29.2941,'Gongga',NULL,NULL),(542224,'桑日县',542200,'桑日',3,'0893','856200','中国,西藏自治区,山南地区,桑日县',92.0201,29.2664,'Sangri',NULL,NULL),(542225,'琼结县',542200,'琼结',3,'0893','856800','中国,西藏自治区,山南地区,琼结县',91.6809,29.0263,'Qiongjie',NULL,NULL),(542226,'曲松县',542200,'曲松',3,'0893','856300','中国,西藏自治区,山南地区,曲松县',92.2026,29.0641,'Qusong',NULL,NULL),(542227,'措美县',542200,'措美',3,'0893','856900','中国,西藏自治区,山南地区,措美县',91.4324,28.4379,'Cuomei',NULL,NULL),(542228,'洛扎县',542200,'洛扎',3,'0893','856600','中国,西藏自治区,山南地区,洛扎县',90.8604,28.3872,'Luozha',NULL,NULL),(542229,'加查县',542200,'加查',3,'0893','856400','中国,西藏自治区,山南地区,加查县',92.577,29.1397,'Jiacha',NULL,NULL),(542231,'隆子县',542200,'隆子',3,'0893','856600','中国,西藏自治区,山南地区,隆子县',92.4615,28.408,'Longzi',NULL,NULL),(542232,'错那县',542200,'错那',3,'0893','856700','中国,西藏自治区,山南地区,错那县',91.9575,27.9922,'Cuona',NULL,NULL),(542233,'浪卡子县',542200,'浪卡子',3,'0893','851100','中国,西藏自治区,山南地区,浪卡子县',90.4,28.9695,'Langkazi',NULL,NULL),(542400,'那曲地区',540000,'那曲',2,'0896','852000','中国,西藏自治区,那曲地区',92.0602,31.476,'Nagqu',NULL,NULL),(542421,'那曲县',542400,'那曲',3,'0896','852000','中国,西藏自治区,那曲地区,那曲县',92.0535,31.4696,'Naqu',NULL,NULL),(542422,'嘉黎县',542400,'嘉黎',3,'0896','852400','中国,西藏自治区,那曲地区,嘉黎县',93.2499,30.6423,'Jiali',NULL,NULL),(542423,'比如县',542400,'比如',3,'0896','852300','中国,西藏自治区,那曲地区,比如县',93.6869,31.4779,'Biru',NULL,NULL),(542424,'聂荣县',542400,'聂荣',3,'0896','853500','中国,西藏自治区,那曲地区,聂荣县',92.2957,32.1119,'Nierong',NULL,NULL),(542425,'安多县',542400,'安多',3,'0896','853400','中国,西藏自治区,那曲地区,安多县',91.6795,32.2612,'Anduo',NULL,NULL),(542426,'申扎县',542400,'申扎',3,'0896','853100','中国,西藏自治区,那曲地区,申扎县',88.7078,30.93,'Shenzha',NULL,NULL),(542427,'索县',542400,'索县',3,'0896','852200','中国,西藏自治区,那曲地区,索县',93.783,31.8843,'Suoxian',NULL,NULL),(542428,'班戈县',542400,'班戈',3,'0896','852500','中国,西藏自治区,那曲地区,班戈县',90.0191,31.3615,'Bange',NULL,NULL),(542429,'巴青县',542400,'巴青',3,'0896','852100','中国,西藏自治区,那曲地区,巴青县',94.0532,31.9183,'Baqing',NULL,NULL),(542430,'尼玛县',542400,'尼玛',3,'0896','852600','中国,西藏自治区,那曲地区,尼玛县',87.2526,31.7965,'Nima',NULL,NULL),(542431,'双湖县',542400,'双湖',3,'0896','852600','中国,西藏自治区,那曲地区,双湖县',88.8378,33.189,'Shuanghu',NULL,NULL),(542500,'阿里地区',540000,'阿里',2,'0897','859000','中国,西藏自治区,阿里地区',80.1055,32.5032,'Ngari',NULL,NULL),(542521,'普兰县',542500,'普兰',3,'0897','859500','中国,西藏自治区,阿里地区,普兰县',81.177,30.3,'Pulan',NULL,NULL),(542522,'札达县',542500,'札达',3,'0897','859600','中国,西藏自治区,阿里地区,札达县',79.8026,31.4834,'Zhada',NULL,NULL),(542523,'噶尔县',542500,'噶尔',3,'0897','859400','中国,西藏自治区,阿里地区,噶尔县',80.0958,32.5002,'Gaer',NULL,NULL),(542524,'日土县',542500,'日土',3,'0897','859700','中国,西藏自治区,阿里地区,日土县',79.7131,33.3874,'Ritu',NULL,NULL),(542525,'革吉县',542500,'革吉',3,'0897','859100','中国,西藏自治区,阿里地区,革吉县',81.151,32.3964,'Geji',NULL,NULL),(542526,'改则县',542500,'改则',3,'0897','859200','中国,西藏自治区,阿里地区,改则县',84.063,32.3045,'Gaize',NULL,NULL),(542527,'措勤县',542500,'措勤',3,'0897','859300','中国,西藏自治区,阿里地区,措勤县',85.1662,31.021,'Cuoqin',NULL,NULL),(542600,'林芝地区',540000,'林芝',2,'0894','850400','中国,西藏自治区,林芝地区',94.3624,29.6547,'Nyingchi',NULL,NULL),(542621,'林芝县',542600,'林芝',3,'0894','850400','中国,西藏自治区,林芝地区,林芝县',94.4839,29.5756,'Linzhi',NULL,NULL),(542622,'工布江达县',542600,'工布江达',3,'0894','850300','中国,西藏自治区,林芝地区,工布江达县',93.2452,29.8858,'Gongbujiangda',NULL,NULL),(542623,'米林县',542600,'米林',3,'0894','850500','中国,西藏自治区,林芝地区,米林县',94.2132,29.2153,'Milin',NULL,NULL),(542624,'墨脱县',542600,'墨脱',3,'0894','855300','中国,西藏自治区,林芝地区,墨脱县',95.3316,29.327,'Motuo',NULL,NULL),(542625,'波密县',542600,'波密',3,'0894','855200','中国,西藏自治区,林芝地区,波密县',95.771,29.8591,'Bomi',NULL,NULL),(542626,'察隅县',542600,'察隅',3,'0894','855100','中国,西藏自治区,林芝地区,察隅县',97.4668,28.6618,'Chayu',NULL,NULL),(542627,'朗县',542600,'朗县',3,'0894','856500','中国,西藏自治区,林芝地区,朗县',93.0754,29.0455,'Langxian',NULL,NULL),(610000,'陕西省',100000,'陕西',1,'','','中国,陕西省',108.948,34.2632,'Shaanxi',NULL,NULL),(610100,'西安市',610000,'西安',2,'029','710003','中国,陕西省,西安市',108.948,34.2632,'Xi\'an',NULL,NULL),(610102,'新城区',610100,'新城',3,'029','710004','中国,陕西省,西安市,新城区',108.961,34.2664,'Xincheng',NULL,NULL),(610103,'碑林区',610100,'碑林',3,'029','710001','中国,陕西省,西安市,碑林区',108.934,34.2304,'Beilin',NULL,NULL),(610104,'莲湖区',610100,'莲湖',3,'029','710003','中国,陕西省,西安市,莲湖区',108.94,34.2671,'Lianhu',NULL,NULL),(610111,'灞桥区',610100,'灞桥',3,'029','710038','中国,陕西省,西安市,灞桥区',109.065,34.2726,'Baqiao',NULL,NULL),(610112,'未央区',610100,'未央',3,'029','710014','中国,陕西省,西安市,未央区',108.947,34.293,'Weiyang',NULL,NULL),(610113,'雁塔区',610100,'雁塔',3,'029','710061','中国,陕西省,西安市,雁塔区',108.949,34.2225,'Yanta',NULL,NULL),(610114,'阎良区',610100,'阎良',3,'029','710087','中国,陕西省,西安市,阎良区',109.226,34.6622,'Yanliang',NULL,NULL),(610115,'临潼区',610100,'临潼',3,'029','710600','中国,陕西省,西安市,临潼区',109.214,34.3666,'Lintong',NULL,NULL),(610116,'长安区',610100,'长安',3,'029','710100','中国,陕西省,西安市,长安区',108.946,34.1556,'Chang\'an',NULL,NULL),(610122,'蓝田县',610100,'蓝田',3,'029','710500','中国,陕西省,西安市,蓝田县',109.323,34.1513,'Lantian',NULL,NULL),(610124,'周至县',610100,'周至',3,'029','710400','中国,陕西省,西安市,周至县',108.222,34.1634,'Zhouzhi',NULL,NULL),(610125,'户县',610100,'户县',3,'029','710300','中国,陕西省,西安市,户县',108.605,34.1086,'Huxian',NULL,NULL),(610126,'高陵区',610100,'高陵',3,'029','710200','中国,陕西省,西安市,高陵区',109.088,34.5348,'Gaoling',NULL,NULL),(610200,'铜川市',610000,'铜川',2,'0919','727100','中国,陕西省,铜川市',108.963,34.9089,'Tongchuan',NULL,NULL),(610202,'王益区',610200,'王益',3,'0919','727000','中国,陕西省,铜川市,王益区',109.076,35.069,'Wangyi',NULL,NULL),(610203,'印台区',610200,'印台',3,'0919','727007','中国,陕西省,铜川市,印台区',109.102,35.1169,'Yintai',NULL,NULL),(610204,'耀州区',610200,'耀州',3,'0919','727100','中国,陕西省,铜川市,耀州区',108.986,34.9131,'Yaozhou',NULL,NULL),(610222,'宜君县',610200,'宜君',3,'0919','727200','中国,陕西省,铜川市,宜君县',109.118,35.4011,'Yijun',NULL,NULL),(610300,'宝鸡市',610000,'宝鸡',2,'0917','721000','中国,陕西省,宝鸡市',107.145,34.3693,'Baoji',NULL,NULL),(610302,'渭滨区',610300,'渭滨',3,'0917','721000','中国,陕西省,宝鸡市,渭滨区',107.15,34.3712,'Weibin',NULL,NULL),(610303,'金台区',610300,'金台',3,'0917','721000','中国,陕西省,宝鸡市,金台区',107.147,34.3761,'Jintai',NULL,NULL),(610304,'陈仓区',610300,'陈仓',3,'0917','721300','中国,陕西省,宝鸡市,陈仓区',107.387,34.3545,'Chencang',NULL,NULL),(610322,'凤翔县',610300,'凤翔',3,'0917','721400','中国,陕西省,宝鸡市,凤翔县',107.396,34.5232,'Fengxiang',NULL,NULL),(610323,'岐山县',610300,'岐山',3,'0917','722400','中国,陕西省,宝鸡市,岐山县',107.622,34.4438,'Qishan',NULL,NULL),(610324,'扶风县',610300,'扶风',3,'0917','722200','中国,陕西省,宝鸡市,扶风县',107.9,34.3752,'Fufeng',NULL,NULL),(610326,'眉县',610300,'眉县',3,'0917','722300','中国,陕西省,宝鸡市,眉县',107.751,34.2757,'Meixian',NULL,NULL),(610327,'陇县',610300,'陇县',3,'0917','721200','中国,陕西省,宝鸡市,陇县',106.859,34.894,'Longxian',NULL,NULL),(610328,'千阳县',610300,'千阳',3,'0917','721100','中国,陕西省,宝鸡市,千阳县',107.13,34.6422,'Qianyang',NULL,NULL),(610329,'麟游县',610300,'麟游',3,'0917','721500','中国,陕西省,宝鸡市,麟游县',107.796,34.6784,'Linyou',NULL,NULL),(610330,'凤县',610300,'凤县',3,'0917','721700','中国,陕西省,宝鸡市,凤县',106.524,33.9117,'Fengxian',NULL,NULL),(610331,'太白县',610300,'太白',3,'0917','721600','中国,陕西省,宝鸡市,太白县',107.316,34.0621,'Taibai',NULL,NULL),(610400,'咸阳市',610000,'咸阳',2,'029','712000','中国,陕西省,咸阳市',108.705,34.3334,'Xianyang',NULL,NULL),(610402,'秦都区',610400,'秦都',3,'029','712000','中国,陕西省,咸阳市,秦都区',108.715,34.338,'Qindu',NULL,NULL),(610403,'杨陵区',610400,'杨陵',3,'029','712100','中国,陕西省,咸阳市,杨陵区',108.083,34.2704,'Yangling',NULL,NULL),(610404,'渭城区',610400,'渭城',3,'029','712000','中国,陕西省,咸阳市,渭城区',108.722,34.332,'Weicheng',NULL,NULL),(610422,'三原县',610400,'三原',3,'029','713800','中国,陕西省,咸阳市,三原县',108.932,34.6156,'Sanyuan',NULL,NULL),(610423,'泾阳县',610400,'泾阳',3,'029','713700','中国,陕西省,咸阳市,泾阳县',108.843,34.5271,'Jingyang',NULL,NULL),(610424,'乾县',610400,'乾县',3,'029','713300','中国,陕西省,咸阳市,乾县',108.242,34.5295,'Qianxian',NULL,NULL),(610425,'礼泉县',610400,'礼泉',3,'029','713200','中国,陕西省,咸阳市,礼泉县',108.426,34.4846,'Liquan',NULL,NULL),(610426,'永寿县',610400,'永寿',3,'029','713400','中国,陕西省,咸阳市,永寿县',108.145,34.6908,'Yongshou',NULL,NULL),(610427,'彬县',610400,'彬县',3,'029','713500','中国,陕西省,咸阳市,彬县',108.085,35.0342,'Binxian',NULL,NULL),(610428,'长武县',610400,'长武',3,'029','713600','中国,陕西省,咸阳市,长武县',107.795,35.2067,'Changwu',NULL,NULL),(610429,'旬邑县',610400,'旬邑',3,'029','711300','中国,陕西省,咸阳市,旬邑县',108.334,35.1134,'Xunyi',NULL,NULL),(610430,'淳化县',610400,'淳化',3,'029','711200','中国,陕西省,咸阳市,淳化县',108.58,34.7989,'Chunhua',NULL,NULL),(610431,'武功县',610400,'武功',3,'029','712200','中国,陕西省,咸阳市,武功县',108.204,34.26,'Wugong',NULL,NULL),(610481,'兴平市',610400,'兴平',3,'029','713100','中国,陕西省,咸阳市,兴平市',108.491,34.2979,'Xingping',NULL,NULL),(610500,'渭南市',610000,'渭南',2,'0913','714000','中国,陕西省,渭南市',109.503,34.4994,'Weinan',NULL,NULL),(610502,'临渭区',610500,'临渭',3,'0913','714000','中国,陕西省,渭南市,临渭区',109.493,34.4982,'Linwei',NULL,NULL),(610521,'华县',610500,'华县',3,'0913','714100','中国,陕西省,渭南市,华县',109.772,34.5126,'Huaxian',NULL,NULL),(610522,'潼关县',610500,'潼关',3,'0913','714300','中国,陕西省,渭南市,潼关县',110.244,34.5428,'Tongguan',NULL,NULL),(610523,'大荔县',610500,'大荔',3,'0913','715100','中国,陕西省,渭南市,大荔县',109.942,34.7957,'Dali',NULL,NULL),(610524,'合阳县',610500,'合阳',3,'0913','715300','中国,陕西省,渭南市,合阳县',110.149,35.238,'Heyang',NULL,NULL),(610525,'澄城县',610500,'澄城',3,'0913','715200','中国,陕西省,渭南市,澄城县',109.934,35.184,'Chengcheng',NULL,NULL),(610526,'蒲城县',610500,'蒲城',3,'0913','715500','中国,陕西省,渭南市,蒲城县',109.59,34.9568,'Pucheng',NULL,NULL),(610527,'白水县',610500,'白水',3,'0913','715600','中国,陕西省,渭南市,白水县',109.593,35.1786,'Baishui',NULL,NULL),(610528,'富平县',610500,'富平',3,'0913','711700','中国,陕西省,渭南市,富平县',109.18,34.7511,'Fuping',NULL,NULL),(610581,'韩城市',610500,'韩城',3,'0913','715400','中国,陕西省,渭南市,韩城市',110.442,35.4793,'Hancheng',NULL,NULL),(610582,'华阴市',610500,'华阴',3,'0913','714200','中国,陕西省,渭南市,华阴市',110.088,34.5661,'Huayin',NULL,NULL),(610600,'延安市',610000,'延安',2,'0911','716000','中国,陕西省,延安市',109.491,36.5965,'Yan\'an',NULL,NULL),(610602,'宝塔区',610600,'宝塔',3,'0911','716000','中国,陕西省,延安市,宝塔区',109.493,36.5915,'Baota',NULL,NULL),(610621,'延长县',610600,'延长',3,'0911','717100','中国,陕西省,延安市,延长县',110.011,36.579,'Yanchang',NULL,NULL),(610622,'延川县',610600,'延川',3,'0911','717200','中国,陕西省,延安市,延川县',110.194,36.8782,'Yanchuan',NULL,NULL),(610623,'子长县',610600,'子长',3,'0911','717300','中国,陕西省,延安市,子长县',109.675,37.1425,'Zichang',NULL,NULL),(610624,'安塞县',610600,'安塞',3,'0911','717400','中国,陕西省,延安市,安塞县',109.327,36.8651,'Ansai',NULL,NULL),(610625,'志丹县',610600,'志丹',3,'0911','717500','中国,陕西省,延安市,志丹县',108.768,36.8218,'Zhidan',NULL,NULL),(610626,'吴起县',610600,'吴起',3,'0911','717600','中国,陕西省,延安市,吴起县',108.176,36.9278,'Wuqi',NULL,NULL),(610627,'甘泉县',610600,'甘泉',3,'0911','716100','中国,陕西省,延安市,甘泉县',109.35,36.2775,'Ganquan',NULL,NULL),(610628,'富县',610600,'富县',3,'0911','727500','中国,陕西省,延安市,富县',109.379,35.988,'Fuxian',NULL,NULL),(610629,'洛川县',610600,'洛川',3,'0911','727400','中国,陕西省,延安市,洛川县',109.433,35.7608,'Luochuan',NULL,NULL),(610630,'宜川县',610600,'宜川',3,'0911','716200','中国,陕西省,延安市,宜川县',110.172,36.0473,'Yichuan',NULL,NULL),(610631,'黄龙县',610600,'黄龙',3,'0911','715700','中国,陕西省,延安市,黄龙县',109.843,35.5835,'Huanglong',NULL,NULL),(610632,'黄陵县',610600,'黄陵',3,'0911','727300','中国,陕西省,延安市,黄陵县',109.263,35.5836,'Huangling',NULL,NULL),(610700,'汉中市',610000,'汉中',2,'0916','723000','中国,陕西省,汉中市',107.029,33.0777,'Hanzhong',NULL,NULL),(610702,'汉台区',610700,'汉台',3,'0916','723000','中国,陕西省,汉中市,汉台区',107.032,33.0677,'Hantai',NULL,NULL),(610721,'南郑县',610700,'南郑',3,'0916','723100','中国,陕西省,汉中市,南郑县',106.94,33.003,'Nanzheng',NULL,NULL),(610722,'城固县',610700,'城固',3,'0916','723200','中国,陕西省,汉中市,城固县',107.334,33.1566,'Chenggu',NULL,NULL),(610723,'洋县',610700,'洋县',3,'0916','723300','中国,陕西省,汉中市,洋县',107.547,33.221,'Yangxian',NULL,NULL),(610724,'西乡县',610700,'西乡',3,'0916','723500','中国,陕西省,汉中市,西乡县',107.769,32.9841,'Xixiang',NULL,NULL),(610725,'勉县',610700,'勉县',3,'0916','724200','中国,陕西省,汉中市,勉县',106.676,33.1527,'Mianxian',NULL,NULL),(610726,'宁强县',610700,'宁强',3,'0916','724400','中国,陕西省,汉中市,宁强县',106.26,32.8288,'Ningqiang',NULL,NULL),(610727,'略阳县',610700,'略阳',3,'0916','724300','中国,陕西省,汉中市,略阳县',106.154,33.3301,'Lueyang',NULL,NULL),(610728,'镇巴县',610700,'镇巴',3,'0916','723600','中国,陕西省,汉中市,镇巴县',107.896,32.5349,'Zhenba',NULL,NULL),(610729,'留坝县',610700,'留坝',3,'0916','724100','中国,陕西省,汉中市,留坝县',106.922,33.6161,'Liuba',NULL,NULL),(610730,'佛坪县',610700,'佛坪',3,'0916','723400','中国,陕西省,汉中市,佛坪县',107.99,33.525,'Foping',NULL,NULL),(610800,'榆林市',610000,'榆林',2,'0912','719000','中国,陕西省,榆林市',109.741,38.2902,'Yulin',NULL,NULL),(610802,'榆阳区',610800,'榆阳',3,'0912','719000','中国,陕西省,榆林市,榆阳区',109.735,38.2784,'Yuyang',NULL,NULL),(610821,'神木县',610800,'神木',3,'0912','719300','中国,陕西省,榆林市,神木县',110.499,38.8423,'Shenmu',NULL,NULL),(610822,'府谷县',610800,'府谷',3,'0912','719400','中国,陕西省,榆林市,府谷县',111.067,39.028,'Fugu',NULL,NULL),(610823,'横山县',610800,'横山',3,'0912','719100','中国,陕西省,榆林市,横山县',109.296,37.958,'Hengshan',NULL,NULL),(610824,'靖边县',610800,'靖边',3,'0912','718500','中国,陕西省,榆林市,靖边县',108.794,37.5994,'Jingbian',NULL,NULL),(610825,'定边县',610800,'定边',3,'0912','718600','中国,陕西省,榆林市,定边县',107.598,37.5904,'Dingbian',NULL,NULL),(610826,'绥德县',610800,'绥德',3,'0912','718000','中国,陕西省,榆林市,绥德县',110.261,37.4978,'Suide',NULL,NULL),(610827,'米脂县',610800,'米脂',3,'0912','718100','中国,陕西省,榆林市,米脂县',110.184,37.7553,'Mizhi',NULL,NULL),(610828,'佳县',610800,'佳县',3,'0912','719200','中国,陕西省,榆林市,佳县',110.494,38.0225,'Jiaxian',NULL,NULL),(610829,'吴堡县',610800,'吴堡',3,'0912','718200','中国,陕西省,榆林市,吴堡县',110.745,37.4571,'Wubu',NULL,NULL),(610830,'清涧县',610800,'清涧',3,'0912','718300','中国,陕西省,榆林市,清涧县',110.122,37.0885,'Qingjian',NULL,NULL),(610831,'子洲县',610800,'子洲',3,'0912','718400','中国,陕西省,榆林市,子洲县',110.035,37.6124,'Zizhou',NULL,NULL),(610900,'安康市',610000,'安康',2,'0915','725000','中国,陕西省,安康市',109.029,32.6903,'Ankang',NULL,NULL),(610902,'汉滨区',610900,'汉滨',3,'0915','725000','中国,陕西省,安康市,汉滨区',109.027,32.6952,'Hanbin',NULL,NULL),(610921,'汉阴县',610900,'汉阴',3,'0915','725100','中国,陕西省,安康市,汉阴县',108.511,32.8913,'Hanyin',NULL,NULL),(610922,'石泉县',610900,'石泉',3,'0915','725200','中国,陕西省,安康市,石泉县',108.248,33.0397,'Shiquan',NULL,NULL),(610923,'宁陕县',610900,'宁陕',3,'0915','711600','中国,陕西省,安康市,宁陕县',108.315,33.3173,'Ningshan',NULL,NULL),(610924,'紫阳县',610900,'紫阳',3,'0915','725300','中国,陕西省,安康市,紫阳县',108.537,32.5211,'Ziyang',NULL,NULL),(610925,'岚皋县',610900,'岚皋',3,'0915','725400','中国,陕西省,安康市,岚皋县',108.903,32.3079,'Langao',NULL,NULL),(610926,'平利县',610900,'平利',3,'0915','725500','中国,陕西省,安康市,平利县',109.358,32.3911,'Pingli',NULL,NULL),(610927,'镇坪县',610900,'镇坪',3,'0915','725600','中国,陕西省,安康市,镇坪县',109.525,31.8833,'Zhenping',NULL,NULL),(610928,'旬阳县',610900,'旬阳',3,'0915','725700','中国,陕西省,安康市,旬阳县',109.362,32.8321,'Xunyang',NULL,NULL),(610929,'白河县',610900,'白河',3,'0915','725800','中国,陕西省,安康市,白河县',110.113,32.8096,'Baihe',NULL,NULL),(611000,'商洛市',610000,'商洛',2,'0914','726000','中国,陕西省,商洛市',109.94,33.8683,'Shangluo',NULL,NULL),(611002,'商州区',611000,'商州',3,'0914','726000','中国,陕西省,商洛市,商州区',109.941,33.8627,'Shangzhou',NULL,NULL),(611021,'洛南县',611000,'洛南',3,'0914','726100','中国,陕西省,商洛市,洛南县',110.146,34.0899,'Luonan',NULL,NULL),(611022,'丹凤县',611000,'丹凤',3,'0914','726200','中国,陕西省,商洛市,丹凤县',110.335,33.6947,'Danfeng',NULL,NULL),(611023,'商南县',611000,'商南',3,'0914','726300','中国,陕西省,商洛市,商南县',110.884,33.5258,'Shangnan',NULL,NULL),(611024,'山阳县',611000,'山阳',3,'0914','726400','中国,陕西省,商洛市,山阳县',109.888,33.5293,'Shanyang',NULL,NULL),(611025,'镇安县',611000,'镇安',3,'0914','711500','中国,陕西省,商洛市,镇安县',109.154,33.4237,'Zhen\'an',NULL,NULL),(611026,'柞水县',611000,'柞水',3,'0914','711400','中国,陕西省,商洛市,柞水县',109.111,33.6831,'Zhashui',NULL,NULL),(611100,'西咸新区',610000,'西咸',2,'029','712000','中国,陕西省,西咸新区',108.811,34.3071,'Xixian',NULL,NULL),(611101,'空港新城',611100,'空港',3,'0374','461000','中国,陕西省,西咸新区,空港新城',108.761,34.4409,'Konggang',NULL,NULL),(611102,'沣东新城',611100,'沣东',3,'029','710000','中国,陕西省,西咸新区,沣东新城',108.83,34.2674,'Fengdong',NULL,NULL),(611103,'秦汉新城',611100,'秦汉',3,'029','712000','中国,陕西省,西咸新区,秦汉新城',108.838,34.3865,'Qinhan',NULL,NULL),(611104,'沣西新城',611100,'沣西',3,'029','710000','中国,陕西省,西咸新区,沣西新城',108.712,34.1905,'Fengxi',NULL,NULL),(611105,'泾河新城',611100,'泾河',3,'029','713700','中国,陕西省,西咸新区,泾河新城',109.05,34.4606,'Jinghe',NULL,NULL),(620000,'甘肃省',100000,'甘肃',1,'','','中国,甘肃省',103.824,36.058,'Gansu',NULL,NULL),(620100,'兰州市',620000,'兰州',2,'0931','730030','中国,甘肃省,兰州市',103.824,36.058,'Lanzhou',NULL,NULL),(620102,'城关区',620100,'城关',3,'0931','730030','中国,甘肃省,兰州市,城关区',103.825,36.0573,'Chengguan',NULL,NULL),(620103,'七里河区',620100,'七里河',3,'0931','730050','中国,甘肃省,兰州市,七里河区',103.786,36.0658,'Qilihe',NULL,NULL),(620104,'西固区',620100,'西固',3,'0931','730060','中国,甘肃省,兰州市,西固区',103.628,36.0886,'Xigu',NULL,NULL),(620105,'安宁区',620100,'安宁',3,'0931','730070','中国,甘肃省,兰州市,安宁区',103.719,36.1038,'Anning',NULL,NULL),(620111,'红古区',620100,'红古',3,'0931','730084','中国,甘肃省,兰州市,红古区',102.86,36.3454,'Honggu',NULL,NULL),(620121,'永登县',620100,'永登',3,'0931','730300','中国,甘肃省,兰州市,永登县',103.261,36.7352,'Yongdeng',NULL,NULL),(620122,'皋兰县',620100,'皋兰',3,'0931','730200','中国,甘肃省,兰州市,皋兰县',103.945,36.3321,'Gaolan',NULL,NULL),(620123,'榆中县',620100,'榆中',3,'0931','730100','中国,甘肃省,兰州市,榆中县',104.115,35.8442,'Yuzhong',NULL,NULL),(620200,'嘉峪关市',620000,'嘉峪关',2,'0937','735100','中国,甘肃省,嘉峪关市',98.2773,39.7865,'Jiayuguan',NULL,NULL),(620201,'雄关区',620200,'雄关',3,'0937','735100','中国,甘肃省,嘉峪关市,雄关区',98.2774,39.7793,'Xiongguan',NULL,NULL),(620202,'长城区',620200,'长城',3,'0937','735106','中国,甘肃省,嘉峪关市,长城区',98.2735,39.7874,'Changcheng',NULL,NULL),(620203,'镜铁区',620200,'镜铁',3,'0937','735100','中国,甘肃省,嘉峪关市,镜铁区',98.2773,39.7865,'Jingtie',NULL,NULL),(620300,'金昌市',620000,'金昌',2,'0935','737100','中国,甘肃省,金昌市',102.188,38.5142,'Jinchang',NULL,NULL),(620302,'金川区',620300,'金川',3,'0935','737100','中国,甘肃省,金昌市,金川区',102.194,38.521,'Jinchuan',NULL,NULL),(620321,'永昌县',620300,'永昌',3,'0935','737200','中国,甘肃省,金昌市,永昌县',101.972,38.2471,'Yongchang',NULL,NULL),(620400,'白银市',620000,'白银',2,'0943','730900','中国,甘肃省,白银市',104.174,36.5457,'Baiyin',NULL,NULL),(620402,'白银区',620400,'白银',3,'0943','730900','中国,甘肃省,白银市,白银区',104.174,36.5441,'Baiyin',NULL,NULL),(620403,'平川区',620400,'平川',3,'0943','730913','中国,甘肃省,白银市,平川区',104.825,36.7277,'Pingchuan',NULL,NULL),(620421,'靖远县',620400,'靖远',3,'0943','730600','中国,甘肃省,白银市,靖远县',104.683,36.566,'Jingyuan',NULL,NULL),(620422,'会宁县',620400,'会宁',3,'0943','730700','中国,甘肃省,白银市,会宁县',105.053,35.6963,'Huining',NULL,NULL),(620423,'景泰县',620400,'景泰',3,'0943','730400','中国,甘肃省,白银市,景泰县',104.063,37.1836,'Jingtai',NULL,NULL),(620500,'天水市',620000,'天水',2,'0938','741000','中国,甘肃省,天水市',105.725,34.5785,'Tianshui',NULL,NULL),(620502,'秦州区',620500,'秦州',3,'0938','741000','中国,甘肃省,天水市,秦州区',105.724,34.5809,'Qinzhou',NULL,NULL),(620503,'麦积区',620500,'麦积',3,'0938','741020','中国,甘肃省,天水市,麦积区',105.89,34.5707,'Maiji',NULL,NULL),(620521,'清水县',620500,'清水',3,'0938','741400','中国,甘肃省,天水市,清水县',106.137,34.7503,'Qingshui',NULL,NULL),(620522,'秦安县',620500,'秦安',3,'0938','741600','中国,甘肃省,天水市,秦安县',105.67,34.8589,'Qin\'an',NULL,NULL),(620523,'甘谷县',620500,'甘谷',3,'0938','741200','中国,甘肃省,天水市,甘谷县',105.333,34.7366,'Gangu',NULL,NULL),(620524,'武山县',620500,'武山',3,'0938','741300','中国,甘肃省,天水市,武山县',104.884,34.7212,'Wushan',NULL,NULL),(620525,'张家川回族自治县',620500,'张家川',3,'0938','741500','中国,甘肃省,天水市,张家川回族自治县',106.216,34.9958,'Zhangjiachuan',NULL,NULL),(620600,'武威市',620000,'武威',2,'0935','733000','中国,甘肃省,武威市',102.635,37.93,'Wuwei',NULL,NULL),(620602,'凉州区',620600,'凉州',3,'0935','733000','中国,甘肃省,武威市,凉州区',102.642,37.9283,'Liangzhou',NULL,NULL),(620621,'民勤县',620600,'民勤',3,'0935','733300','中国,甘肃省,武威市,民勤县',103.09,38.6249,'Minqin',NULL,NULL),(620622,'古浪县',620600,'古浪',3,'0935','733100','中国,甘肃省,武威市,古浪县',102.892,37.4651,'Gulang',NULL,NULL),(620623,'天祝藏族自治县',620600,'天祝',3,'0935','733200','中国,甘肃省,武威市,天祝藏族自治县',103.136,36.9771,'Tianzhu',NULL,NULL),(620700,'张掖市',620000,'张掖',2,'0936','734000','中国,甘肃省,张掖市',100.455,38.9329,'Zhangye',NULL,NULL),(620702,'甘州区',620700,'甘州',3,'0936','734000','中国,甘肃省,张掖市,甘州区',100.453,38.9295,'Ganzhou',NULL,NULL),(620721,'肃南裕固族自治县',620700,'肃南',3,'0936','734400','中国,甘肃省,张掖市,肃南裕固族自治县',99.6141,38.8378,'Sunan',NULL,NULL),(620722,'民乐县',620700,'民乐',3,'0936','734500','中国,甘肃省,张掖市,民乐县',100.811,38.4348,'Minle',NULL,NULL),(620723,'临泽县',620700,'临泽',3,'0936','734200','中国,甘肃省,张掖市,临泽县',100.164,39.1525,'Linze',NULL,NULL),(620724,'高台县',620700,'高台',3,'0936','734300','中国,甘肃省,张掖市,高台县',99.8192,39.3783,'Gaotai',NULL,NULL),(620725,'山丹县',620700,'山丹',3,'0936','734100','中国,甘肃省,张掖市,山丹县',101.094,38.7847,'Shandan',NULL,NULL),(620800,'平凉市',620000,'平凉',2,'0933','744000','中国,甘肃省,平凉市',106.685,35.5428,'Pingliang',NULL,NULL),(620802,'崆峒区',620800,'崆峒',3,'0933','744000','中国,甘肃省,平凉市,崆峒区',106.675,35.5426,'Kongtong',NULL,NULL),(620821,'泾川县',620800,'泾川',3,'0933','744300','中国,甘肃省,平凉市,泾川县',107.366,35.3322,'Jingchuan',NULL,NULL),(620822,'灵台县',620800,'灵台',3,'0933','744400','中国,甘肃省,平凉市,灵台县',107.617,35.0677,'Lingtai',NULL,NULL),(620823,'崇信县',620800,'崇信',3,'0933','744200','中国,甘肃省,平凉市,崇信县',107.037,35.3034,'Chongxin',NULL,NULL),(620824,'华亭县',620800,'华亭',3,'0933','744100','中国,甘肃省,平凉市,华亭县',106.655,35.2183,'Huating',NULL,NULL),(620825,'庄浪县',620800,'庄浪',3,'0933','744600','中国,甘肃省,平凉市,庄浪县',106.037,35.2024,'Zhuanglang',NULL,NULL),(620826,'静宁县',620800,'静宁',3,'0933','743400','中国,甘肃省,平凉市,静宁县',105.727,35.5199,'Jingning',NULL,NULL),(620900,'酒泉市',620000,'酒泉',2,'0937','735000','中国,甘肃省,酒泉市',98.5108,39.744,'Jiuquan',NULL,NULL),(620902,'肃州区',620900,'肃州',3,'0937','735000','中国,甘肃省,酒泉市,肃州区',98.5078,39.7451,'Suzhou',NULL,NULL),(620921,'金塔县',620900,'金塔',3,'0937','735300','中国,甘肃省,酒泉市,金塔县',98.9,39.9773,'Jinta',NULL,NULL),(620922,'瓜州县',620900,'瓜州',3,'0937','736100','中国,甘肃省,酒泉市,瓜州县',95.7827,40.5155,'Guazhou',NULL,NULL),(620923,'肃北蒙古族自治县',620900,'肃北',3,'0937','736300','中国,甘肃省,酒泉市,肃北蒙古族自治县',94.8765,39.5121,'Subei',NULL,NULL),(620924,'阿克塞哈萨克族自治县',620900,'阿克塞',3,'0937','736400','中国,甘肃省,酒泉市,阿克塞哈萨克族自治县',94.341,39.6343,'Akesai',NULL,NULL),(620981,'玉门市',620900,'玉门',3,'0937','735200','中国,甘肃省,酒泉市,玉门市',97.0454,40.2917,'Yumen',NULL,NULL),(620982,'敦煌市',620900,'敦煌',3,'0937','736200','中国,甘肃省,酒泉市,敦煌市',94.6616,40.1421,'Dunhuang',NULL,NULL),(621000,'庆阳市',620000,'庆阳',2,'0934','745000','中国,甘肃省,庆阳市',107.638,35.7342,'Qingyang',NULL,NULL),(621002,'西峰区',621000,'西峰',3,'0934','745000','中国,甘肃省,庆阳市,西峰区',107.651,35.7307,'Xifeng',NULL,NULL),(621021,'庆城县',621000,'庆城',3,'0934','745100','中国,甘肃省,庆阳市,庆城县',107.883,36.0151,'Qingcheng',NULL,NULL),(621022,'环县',621000,'环县',3,'0934','745700','中国,甘肃省,庆阳市,环县',107.308,36.5685,'Huanxian',NULL,NULL),(621023,'华池县',621000,'华池',3,'0934','745600','中国,甘肃省,庆阳市,华池县',107.989,36.4611,'Huachi',NULL,NULL),(621024,'合水县',621000,'合水',3,'0934','745400','中国,甘肃省,庆阳市,合水县',108.02,35.8191,'Heshui',NULL,NULL),(621025,'正宁县',621000,'正宁',3,'0934','745300','中国,甘肃省,庆阳市,正宁县',108.36,35.4917,'Zhengning',NULL,NULL),(621026,'宁县',621000,'宁县',3,'0934','745200','中国,甘肃省,庆阳市,宁县',107.925,35.5016,'Ningxian',NULL,NULL),(621027,'镇原县',621000,'镇原',3,'0934','744500','中国,甘肃省,庆阳市,镇原县',107.199,35.6771,'Zhenyuan',NULL,NULL),(621100,'定西市',620000,'定西',2,'0932','743000','中国,甘肃省,定西市',104.626,35.5796,'Dingxi',NULL,NULL),(621102,'安定区',621100,'安定',3,'0932','743000','中国,甘肃省,定西市,安定区',104.611,35.5807,'Anding',NULL,NULL),(621121,'通渭县',621100,'通渭',3,'0932','743300','中国,甘肃省,定西市,通渭县',105.242,35.211,'Tongwei',NULL,NULL),(621122,'陇西县',621100,'陇西',3,'0932','748100','中国,甘肃省,定西市,陇西县',104.634,35.0024,'Longxi',NULL,NULL),(621123,'渭源县',621100,'渭源',3,'0932','748200','中国,甘肃省,定西市,渭源县',104.214,35.1365,'Weiyuan',NULL,NULL),(621124,'临洮县',621100,'临洮',3,'0932','730500','中国,甘肃省,定西市,临洮县',103.862,35.3751,'Lintao',NULL,NULL),(621125,'漳县',621100,'漳县',3,'0932','748300','中国,甘肃省,定西市,漳县',104.467,34.8498,'Zhangxian',NULL,NULL),(621126,'岷县',621100,'岷县',3,'0932','748400','中国,甘肃省,定西市,岷县',104.038,34.4344,'Minxian',NULL,NULL),(621200,'陇南市',620000,'陇南',2,'0939','746000','中国,甘肃省,陇南市',104.929,33.3886,'Longnan',NULL,NULL),(621202,'武都区',621200,'武都',3,'0939','746000','中国,甘肃省,陇南市,武都区',104.927,33.3924,'Wudu',NULL,NULL),(621221,'成县',621200,'成县',3,'0939','742500','中国,甘肃省,陇南市,成县',105.726,33.7393,'Chengxian',NULL,NULL),(621222,'文县',621200,'文县',3,'0939','746400','中国,甘肃省,陇南市,文县',104.684,32.9434,'Wenxian',NULL,NULL),(621223,'宕昌县',621200,'宕昌',3,'0939','748500','中国,甘肃省,陇南市,宕昌县',104.393,34.0473,'Dangchang',NULL,NULL),(621224,'康县',621200,'康县',3,'0939','746500','中国,甘肃省,陇南市,康县',105.607,33.3291,'Kangxian',NULL,NULL),(621225,'西和县',621200,'西和',3,'0939','742100','中国,甘肃省,陇南市,西和县',105.301,34.0143,'Xihe',NULL,NULL),(621226,'礼县',621200,'礼县',3,'0939','742200','中国,甘肃省,陇南市,礼县',105.178,34.1894,'Lixian',NULL,NULL),(621227,'徽县',621200,'徽县',3,'0939','742300','中国,甘肃省,陇南市,徽县',106.085,33.769,'Huixian',NULL,NULL),(621228,'两当县',621200,'两当',3,'0939','742400','中国,甘肃省,陇南市,两当县',106.305,33.9096,'Liangdang',NULL,NULL),(622900,'临夏回族自治州',620000,'临夏',2,'0930','731100','中国,甘肃省,临夏回族自治州',103.212,35.5994,'Linxia',NULL,NULL),(622901,'临夏市',622900,'临夏',3,'0930','731100','中国,甘肃省,临夏回族自治州,临夏市',103.21,35.5992,'Linxia',NULL,NULL),(622921,'临夏县',622900,'临夏',3,'0930','731800','中国,甘肃省,临夏回族自治州,临夏县',102.994,35.4952,'Linxia',NULL,NULL),(622922,'康乐县',622900,'康乐',3,'0930','731500','中国,甘肃省,临夏回族自治州,康乐县',103.711,35.3722,'Kangle',NULL,NULL),(622923,'永靖县',622900,'永靖',3,'0930','731600','中国,甘肃省,临夏回族自治州,永靖县',103.32,35.9384,'Yongjing',NULL,NULL),(622924,'广河县',622900,'广河',3,'0930','731300','中国,甘肃省,临夏回族自治州,广河县',103.569,35.481,'Guanghe',NULL,NULL),(622925,'和政县',622900,'和政',3,'0930','731200','中国,甘肃省,临夏回族自治州,和政县',103.349,35.4259,'Hezheng',NULL,NULL),(622926,'东乡族自治县',622900,'东乡族',3,'0930','731400','中国,甘肃省,临夏回族自治州,东乡族自治县',103.395,35.6647,'Dongxiangzu',NULL,NULL),(622927,'积石山保安族东乡族撒拉族自治县',622900,'积石山',3,'0930','731700','中国,甘肃省,临夏回族自治州,积石山保安族东乡族撒拉族自治县',102.874,35.7182,'Jishishan',NULL,NULL),(623000,'甘南藏族自治州',620000,'甘南',2,'0941','747000','中国,甘肃省,甘南藏族自治州',102.911,34.9864,'Gannan',NULL,NULL),(623001,'合作市',623000,'合作',3,'0941','747000','中国,甘肃省,甘南藏族自治州,合作市',102.911,35.0002,'Hezuo',NULL,NULL),(623021,'临潭县',623000,'临潭',3,'0941','747500','中国,甘肃省,甘南藏族自治州,临潭县',103.353,34.6951,'Lintan',NULL,NULL),(623022,'卓尼县',623000,'卓尼',3,'0941','747600','中国,甘肃省,甘南藏族自治州,卓尼县',103.508,34.5892,'Zhuoni',NULL,NULL),(623023,'舟曲县',623000,'舟曲',3,'0941','746300','中国,甘肃省,甘南藏族自治州,舟曲县',104.372,33.7847,'Zhouqu',NULL,NULL),(623024,'迭部县',623000,'迭部',3,'0941','747400','中国,甘肃省,甘南藏族自治州,迭部县',103.223,34.0562,'Diebu',NULL,NULL),(623025,'玛曲县',623000,'玛曲',3,'0941','747300','中国,甘肃省,甘南藏族自治州,玛曲县',102.075,33.997,'Maqu',NULL,NULL),(623026,'碌曲县',623000,'碌曲',3,'0941','747200','中国,甘肃省,甘南藏族自治州,碌曲县',102.492,34.5887,'Luqu',NULL,NULL),(623027,'夏河县',623000,'夏河',3,'0941','747100','中国,甘肃省,甘南藏族自治州,夏河县',102.522,35.2049,'Xiahe',NULL,NULL),(630000,'青海省',100000,'青海',1,'','','中国,青海省',101.779,36.6232,'Qinghai',NULL,NULL),(630100,'西宁市',630000,'西宁',2,'0971','810000','中国,青海省,西宁市',101.779,36.6232,'Xining',NULL,NULL),(630102,'城东区',630100,'城东',3,'0971','810007','中国,青海省,西宁市,城东区',101.804,36.5997,'Chengdong',NULL,NULL),(630103,'城中区',630100,'城中',3,'0971','810000','中国,青海省,西宁市,城中区',101.784,36.6228,'Chengzhong',NULL,NULL),(630104,'城西区',630100,'城西',3,'0971','810001','中国,青海省,西宁市,城西区',101.766,36.6283,'Chengxi',NULL,NULL),(630105,'城北区',630100,'城北',3,'0971','810003','中国,青海省,西宁市,城北区',101.766,36.6501,'Chengbei',NULL,NULL),(630121,'大通回族土族自治县',630100,'大通',3,'0971','810100','中国,青海省,西宁市,大通回族土族自治县',101.702,36.9349,'Datong',NULL,NULL),(630122,'湟中县',630100,'湟中',3,'0971','811600','中国,青海省,西宁市,湟中县',101.572,36.5008,'Huangzhong',NULL,NULL),(630123,'湟源县',630100,'湟源',3,'0971','812100','中国,青海省,西宁市,湟源县',101.256,36.6824,'Huangyuan',NULL,NULL),(630200,'海东市',630000,'海东',2,'0972','810700','中国,青海省,海东市',102.103,36.5029,'Haidong',NULL,NULL),(630202,'乐都区',630200,'乐都',3,'0972','810700','中国,青海省,海东市,乐都区',102.402,36.4803,'Ledu',NULL,NULL),(630221,'平安县',630200,'平安',3,'0972','810600','中国,青海省,海东市,平安县',102.104,36.5027,'Ping\'an',NULL,NULL),(630222,'民和回族土族自治县',630200,'民和',3,'0972','810800','中国,青海省,海东市,民和回族土族自治县',102.804,36.3295,'Minhe',NULL,NULL),(630223,'互助土族自治县',630200,'互助',3,'0972','810500','中国,青海省,海东市,互助土族自治县',101.957,36.8399,'Huzhu',NULL,NULL),(630224,'化隆回族自治县',630200,'化隆',3,'0972','810900','中国,青海省,海东市,化隆回族自治县',102.262,36.0983,'Hualong',NULL,NULL),(630225,'循化撒拉族自治县',630200,'循化',3,'0972','811100','中国,青海省,海东市,循化撒拉族自治县',102.487,35.8472,'Xunhua',NULL,NULL),(632200,'海北藏族自治州',630000,'海北',2,'0970','812200','中国,青海省,海北藏族自治州',100.901,36.9594,'Haibei',NULL,NULL),(632221,'门源回族自治县',632200,'门源',3,'0970','810300','中国,青海省,海北藏族自治州,门源回族自治县',101.622,37.3761,'Menyuan',NULL,NULL),(632222,'祁连县',632200,'祁连',3,'0970','810400','中国,青海省,海北藏族自治州,祁连县',100.246,38.179,'Qilian',NULL,NULL),(632223,'海晏县',632200,'海晏',3,'0970','812200','中国,青海省,海北藏族自治州,海晏县',100.993,36.899,'Haiyan',NULL,NULL),(632224,'刚察县',632200,'刚察',3,'0970','812300','中国,青海省,海北藏族自治州,刚察县',100.147,37.3216,'Gangcha',NULL,NULL),(632300,'黄南藏族自治州',630000,'黄南',2,'0973','811300','中国,青海省,黄南藏族自治州',102.02,35.5177,'Huangnan',NULL,NULL),(632321,'同仁县',632300,'同仁',3,'0973','811300','中国,青海省,黄南藏族自治州,同仁县',102.018,35.516,'Tongren',NULL,NULL),(632322,'尖扎县',632300,'尖扎',3,'0973','811200','中国,青海省,黄南藏族自治州,尖扎县',102.034,35.9395,'Jianzha',NULL,NULL),(632323,'泽库县',632300,'泽库',3,'0973','811400','中国,青海省,黄南藏族自治州,泽库县',101.464,35.0352,'Zeku',NULL,NULL),(632324,'河南蒙古族自治县',632300,'河南',3,'0973','811500','中国,青海省,黄南藏族自治州,河南蒙古族自治县',101.609,34.7348,'Henan',NULL,NULL),(632500,'海南藏族自治州',630000,'海南',2,'0974','813000','中国,青海省,海南藏族自治州',100.62,36.2804,'Hainan',NULL,NULL),(632521,'共和县',632500,'共和',3,'0974','813000','中国,青海省,海南藏族自治州,共和县',100.62,36.2841,'Gonghe',NULL,NULL),(632522,'同德县',632500,'同德',3,'0974','813200','中国,青海省,海南藏族自治州,同德县',100.572,35.2549,'Tongde',NULL,NULL),(632523,'贵德县',632500,'贵德',3,'0974','811700','中国,青海省,海南藏族自治州,贵德县',101.432,36.044,'Guide',NULL,NULL),(632524,'兴海县',632500,'兴海',3,'0974','813300','中国,青海省,海南藏族自治州,兴海县',99.9885,35.5903,'Xinghai',NULL,NULL),(632525,'贵南县',632500,'贵南',3,'0974','813100','中国,青海省,海南藏族自治州,贵南县',100.747,35.5867,'Guinan',NULL,NULL),(632600,'果洛藏族自治州',630000,'果洛',2,'0975','814000','中国,青海省,果洛藏族自治州',100.242,34.4736,'Golog',NULL,NULL),(632621,'玛沁县',632600,'玛沁',3,'0975','814000','中国,青海省,果洛藏族自治州,玛沁县',100.239,34.4775,'Maqin',NULL,NULL),(632622,'班玛县',632600,'班玛',3,'0975','814300','中国,青海省,果洛藏族自治州,班玛县',100.737,32.9325,'Banma',NULL,NULL),(632623,'甘德县',632600,'甘德',3,'0975','814100','中国,青海省,果洛藏族自治州,甘德县',99.9025,33.9684,'Gande',NULL,NULL),(632624,'达日县',632600,'达日',3,'0975','814200','中国,青海省,果洛藏族自治州,达日县',99.6518,33.7519,'Dari',NULL,NULL),(632625,'久治县',632600,'久治',3,'0975','624700','中国,青海省,果洛藏族自治州,久治县',101.483,33.4299,'Jiuzhi',NULL,NULL),(632626,'玛多县',632600,'玛多',3,'0975','813500','中国,青海省,果洛藏族自治州,玛多县',98.21,34.9157,'Maduo',NULL,NULL),(632700,'玉树藏族自治州',630000,'玉树',2,'0976','815000','中国,青海省,玉树藏族自治州',97.0085,33.004,'Yushu',NULL,NULL),(632701,'玉树市',632700,'玉树',3,'0976','815000','中国,青海省,玉树藏族自治州,玉树市',97.0088,33.0039,'Yushu',NULL,NULL),(632722,'杂多县',632700,'杂多',3,'0976','815300','中国,青海省,玉树藏族自治州,杂多县',95.2986,32.8932,'Zaduo',NULL,NULL),(632723,'称多县',632700,'称多',3,'0976','815100','中国,青海省,玉树藏族自治州,称多县',97.1079,33.369,'Chenduo',NULL,NULL),(632724,'治多县',632700,'治多',3,'0976','815400','中国,青海省,玉树藏族自治州,治多县',95.6157,33.8528,'Zhiduo',NULL,NULL),(632725,'囊谦县',632700,'囊谦',3,'0976','815200','中国,青海省,玉树藏族自治州,囊谦县',96.4775,32.2036,'Nangqian',NULL,NULL),(632726,'曲麻莱县',632700,'曲麻莱',3,'0976','815500','中国,青海省,玉树藏族自治州,曲麻莱县',95.7976,34.1261,'Qumalai',NULL,NULL),(632800,'海西蒙古族藏族自治州',630000,'海西',2,'0977','817000','中国,青海省,海西蒙古族藏族自治州',97.3708,37.3747,'Haixi',NULL,NULL),(632801,'格尔木市',632800,'格尔木',3,'0977','816000','中国,青海省,海西蒙古族藏族自治州,格尔木市',94.9033,36.4024,'Geermu',NULL,NULL),(632802,'德令哈市',632800,'德令哈',3,'0977','817000','中国,青海省,海西蒙古族藏族自治州,德令哈市',97.3608,37.3695,'Delingha',NULL,NULL),(632821,'乌兰县',632800,'乌兰',3,'0977','817100','中国,青海省,海西蒙古族藏族自治州,乌兰县',98.482,36.9347,'Wulan',NULL,NULL),(632822,'都兰县',632800,'都兰',3,'0977','816100','中国,青海省,海西蒙古族藏族自治州,都兰县',98.0923,36.3013,'Dulan',NULL,NULL),(632823,'天峻县',632800,'天峻',3,'0977','817200','中国,青海省,海西蒙古族藏族自治州,天峻县',99.0245,37.3033,'Tianjun',NULL,NULL),(640000,'宁夏回族自治区',100000,'宁夏',1,'','','中国,宁夏回族自治区',106.278,38.4664,'Ningxia',NULL,NULL),(640100,'银川市',640000,'银川',2,'0951','750004','中国,宁夏回族自治区,银川市',106.278,38.4664,'Yinchuan',NULL,NULL),(640104,'兴庆区',640100,'兴庆',3,'0951','750001','中国,宁夏回族自治区,银川市,兴庆区',106.289,38.4739,'Xingqing',NULL,NULL),(640105,'西夏区',640100,'西夏',3,'0951','750021','中国,宁夏回族自治区,银川市,西夏区',106.15,38.4914,'Xixia',NULL,NULL),(640106,'金凤区',640100,'金凤',3,'0951','750011','中国,宁夏回族自治区,银川市,金凤区',106.243,38.4729,'Jinfeng',NULL,NULL),(640121,'永宁县',640100,'永宁',3,'0951','750100','中国,宁夏回族自治区,银川市,永宁县',106.252,38.2756,'Yongning',NULL,NULL),(640122,'贺兰县',640100,'贺兰',3,'0951','750200','中国,宁夏回族自治区,银川市,贺兰县',106.35,38.5544,'Helan',NULL,NULL),(640181,'灵武市',640100,'灵武',3,'0951','750004','中国,宁夏回族自治区,银川市,灵武市',106.34,38.1027,'Lingwu',NULL,NULL),(640200,'石嘴山市',640000,'石嘴山',2,'0952','753000','中国,宁夏回族自治区,石嘴山市',106.376,39.0133,'Shizuishan',NULL,NULL),(640202,'大武口区',640200,'大武口',3,'0952','753000','中国,宁夏回族自治区,石嘴山市,大武口区',106.377,39.0123,'Dawukou',NULL,NULL),(640205,'惠农区',640200,'惠农',3,'0952','753600','中国,宁夏回族自治区,石嘴山市,惠农区',106.711,39.1319,'Huinong',NULL,NULL),(640221,'平罗县',640200,'平罗',3,'0952','753400','中国,宁夏回族自治区,石嘴山市,平罗县',106.545,38.9043,'Pingluo',NULL,NULL),(640300,'吴忠市',640000,'吴忠',2,'0953','751100','中国,宁夏回族自治区,吴忠市',106.199,37.9862,'Wuzhong',NULL,NULL),(640302,'利通区',640300,'利通',3,'0953','751100','中国,宁夏回族自治区,吴忠市,利通区',106.203,37.9851,'Litong',NULL,NULL),(640303,'红寺堡区',640300,'红寺堡',3,'0953','751900','中国,宁夏回族自治区,吴忠市,红寺堡区',106.198,37.9975,'Hongsibao',NULL,NULL),(640323,'盐池县',640300,'盐池',3,'0953','751500','中国,宁夏回族自治区,吴忠市,盐池县',107.407,37.7833,'Yanchi',NULL,NULL),(640324,'同心县',640300,'同心',3,'0953','751300','中国,宁夏回族自治区,吴忠市,同心县',105.914,36.9812,'Tongxin',NULL,NULL),(640381,'青铜峡市',640300,'青铜峡',3,'0953','751600','中国,宁夏回族自治区,吴忠市,青铜峡市',106.075,38.02,'Qingtongxia',NULL,NULL),(640400,'固原市',640000,'固原',2,'0954','756000','中国,宁夏回族自治区,固原市',106.285,36.0046,'Guyuan',NULL,NULL),(640402,'原州区',640400,'原州',3,'0954','756000','中国,宁夏回族自治区,固原市,原州区',106.288,36.0037,'Yuanzhou',NULL,NULL),(640422,'西吉县',640400,'西吉',3,'0954','756200','中国,宁夏回族自治区,固原市,西吉县',105.731,35.9662,'Xiji',NULL,NULL),(640423,'隆德县',640400,'隆德',3,'0954','756300','中国,宁夏回族自治区,固原市,隆德县',106.124,35.6172,'Longde',NULL,NULL),(640424,'泾源县',640400,'泾源',3,'0954','756400','中国,宁夏回族自治区,固原市,泾源县',106.339,35.4907,'Jingyuan',NULL,NULL),(640425,'彭阳县',640400,'彭阳',3,'0954','756500','中国,宁夏回族自治区,固原市,彭阳县',106.645,35.8508,'Pengyang',NULL,NULL),(640500,'中卫市',640000,'中卫',2,'0955','751700','中国,宁夏回族自治区,中卫市',105.19,37.5149,'Zhongwei',NULL,NULL),(640502,'沙坡头区',640500,'沙坡头',3,'0955','755000','中国,宁夏回族自治区,中卫市,沙坡头区',105.19,37.5104,'Shapotou',NULL,NULL),(640521,'中宁县',640500,'中宁',3,'0955','751200','中国,宁夏回族自治区,中卫市,中宁县',105.685,37.4915,'Zhongning',NULL,NULL),(640522,'海原县',640500,'海原',3,'0955','751800','中国,宁夏回族自治区,中卫市,海原县',105.647,36.565,'Haiyuan',NULL,NULL),(650000,'新疆维吾尔自治区',100000,'新疆',1,'','','中国,新疆维吾尔自治区',87.6177,43.7928,'Xinjiang',NULL,NULL),(650100,'乌鲁木齐市',650000,'乌鲁木齐',2,'0991','830002','中国,新疆维吾尔自治区,乌鲁木齐市',87.6177,43.7928,'Urumqi',NULL,NULL),(650102,'天山区',650100,'天山',3,'0991','830002','中国,新疆维吾尔自治区,乌鲁木齐市,天山区',87.6317,43.7944,'Tianshan',NULL,NULL),(650103,'沙依巴克区',650100,'沙依巴克',3,'0991','830000','中国,新疆维吾尔自治区,乌鲁木齐市,沙依巴克区',87.5979,43.8012,'Shayibake',NULL,NULL),(650104,'新市区',650100,'新市',3,'0991','830011','中国,新疆维吾尔自治区,乌鲁木齐市,新市区',87.5741,43.8435,'Xinshi',NULL,NULL),(650105,'水磨沟区',650100,'水磨沟',3,'0991','830017','中国,新疆维吾尔自治区,乌鲁木齐市,水磨沟区',87.6425,43.8325,'Shuimogou',NULL,NULL),(650106,'头屯河区',650100,'头屯河',3,'0991','830022','中国,新疆维吾尔自治区,乌鲁木齐市,头屯河区',87.2914,43.8549,'Toutunhe',NULL,NULL),(650107,'达坂城区',650100,'达坂城',3,'0991','830039','中国,新疆维吾尔自治区,乌鲁木齐市,达坂城区',88.307,43.358,'Dabancheng',NULL,NULL),(650109,'米东区',650100,'米东',3,'0991','830019','中国,新疆维吾尔自治区,乌鲁木齐市,米东区',87.6858,43.9474,'Midong',NULL,NULL),(650121,'乌鲁木齐县',650100,'乌鲁木齐',3,'0991','830063','中国,新疆维吾尔自治区,乌鲁木齐市,乌鲁木齐县',87.4094,43.4712,'Wulumuqi',NULL,NULL),(650200,'克拉玛依市',650000,'克拉玛依',2,'0990','834000','中国,新疆维吾尔自治区,克拉玛依市',84.8739,45.5959,'Karamay',NULL,NULL),(650202,'独山子区',650200,'独山子',3,'0992','834021','中国,新疆维吾尔自治区,克拉玛依市,独山子区',84.8867,44.3287,'Dushanzi',NULL,NULL),(650203,'克拉玛依区',650200,'克拉玛依',3,'0990','834000','中国,新疆维吾尔自治区,克拉玛依市,克拉玛依区',84.8623,45.5909,'Kelamayi',NULL,NULL),(650204,'白碱滩区',650200,'白碱滩',3,'0990','834008','中国,新疆维吾尔自治区,克拉玛依市,白碱滩区',85.1324,45.6877,'Baijiantan',NULL,NULL),(650205,'乌尔禾区',650200,'乌尔禾',3,'0990','834012','中国,新疆维吾尔自治区,克拉玛依市,乌尔禾区',85.6914,46.0901,'Wuerhe',NULL,NULL),(652100,'吐鲁番地区',650000,'吐鲁番',2,'0995','838000','中国,新疆维吾尔自治区,吐鲁番地区',89.1841,42.9476,'Turpan',NULL,NULL),(652101,'吐鲁番市',652100,'吐鲁番',3,'0995','838000','中国,新疆维吾尔自治区,吐鲁番地区,吐鲁番市',89.1858,42.9351,'Tulufan',NULL,NULL),(652122,'鄯善县',652100,'鄯善',3,'0995','838200','中国,新疆维吾尔自治区,吐鲁番地区,鄯善县',90.214,42.8635,'Shanshan',NULL,NULL),(652123,'托克逊县',652100,'托克逊',3,'0995','838100','中国,新疆维吾尔自治区,吐鲁番地区,托克逊县',88.6582,42.7923,'Tuokexun',NULL,NULL),(652200,'哈密地区',650000,'哈密',2,'0902','839000','中国,新疆维吾尔自治区,哈密地区',93.5132,42.8332,'Hami',NULL,NULL),(652201,'哈密市',652200,'哈密',3,'0902','839000','中国,新疆维吾尔自治区,哈密地区,哈密市',93.5145,42.827,'Hami',NULL,NULL),(652222,'巴里坤哈萨克自治县',652200,'巴里坤',3,'0902','839200','中国,新疆维吾尔自治区,哈密地区,巴里坤哈萨克自治县',93.0124,43.5999,'Balikun',NULL,NULL),(652223,'伊吾县',652200,'伊吾',3,'0902','839300','中国,新疆维吾尔自治区,哈密地区,伊吾县',94.694,43.2537,'Yiwu',NULL,NULL),(652300,'昌吉回族自治州',650000,'昌吉',2,'0994','831100','中国,新疆维吾尔自治区,昌吉回族自治州',87.304,44.0146,'Changji',NULL,NULL),(652301,'昌吉市',652300,'昌吉',3,'0994','831100','中国,新疆维吾尔自治区,昌吉回族自治州,昌吉市',87.3025,44.0127,'Changji',NULL,NULL),(652302,'阜康市',652300,'阜康',3,'0994','831500','中国,新疆维吾尔自治区,昌吉回族自治州,阜康市',87.9853,44.1584,'Fukang',NULL,NULL),(652323,'呼图壁县',652300,'呼图壁',3,'0994','831200','中国,新疆维吾尔自治区,昌吉回族自治州,呼图壁县',86.8989,44.1898,'Hutubi',NULL,NULL),(652324,'玛纳斯县',652300,'玛纳斯',3,'0994','832200','中国,新疆维吾尔自治区,昌吉回族自治州,玛纳斯县',86.2145,44.3044,'Manasi',NULL,NULL),(652325,'奇台县',652300,'奇台',3,'0994','831800','中国,新疆维吾尔自治区,昌吉回族自治州,奇台县',89.5932,44.0222,'Qitai',NULL,NULL),(652327,'吉木萨尔县',652300,'吉木萨尔',3,'0994','831700','中国,新疆维吾尔自治区,昌吉回族自治州,吉木萨尔县',89.1808,44.0005,'Jimusaer',NULL,NULL),(652328,'木垒哈萨克自治县',652300,'木垒',3,'0994','831900','中国,新疆维吾尔自治区,昌吉回族自治州,木垒哈萨克自治县',90.289,43.8351,'Mulei',NULL,NULL),(652700,'博尔塔拉蒙古自治州',650000,'博尔塔拉',2,'0909','833400','中国,新疆维吾尔自治区,博尔塔拉蒙古自治州',82.0748,44.9033,'Bortala',NULL,NULL),(652701,'博乐市',652700,'博乐',3,'0909','833400','中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,博乐市',82.0713,44.9005,'Bole',NULL,NULL),(652702,'阿拉山口市',652700,'阿拉山口',3,'0909','833400','中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,阿拉山口市',82.5677,45.1706,'Alashankou',NULL,NULL),(652722,'精河县',652700,'精河',3,'0909','833300','中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,精河县',82.8942,44.6077,'Jinghe',NULL,NULL),(652723,'温泉县',652700,'温泉',3,'0909','833500','中国,新疆维吾尔自治区,博尔塔拉蒙古自治州,温泉县',81.0313,44.9737,'Wenquan',NULL,NULL),(652800,'巴音郭楞蒙古自治州',650000,'巴音郭楞',2,'0996','841000','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州',86.151,41.7686,'Bayingol',NULL,NULL),(652801,'库尔勒市',652800,'库尔勒',3,'0996','841000','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,库尔勒市',86.1553,41.766,'Kuerle',NULL,NULL),(652822,'轮台县',652800,'轮台',3,'0996','841600','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,轮台县',84.261,41.7764,'Luntai',NULL,NULL),(652823,'尉犁县',652800,'尉犁',3,'0996','841500','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,尉犁县',86.259,41.3363,'Yuli',NULL,NULL),(652824,'若羌县',652800,'若羌',3,'0996','841800','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,若羌县',88.1681,39.0179,'Ruoqiang',NULL,NULL),(652825,'且末县',652800,'且末',3,'0996','841900','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,且末县',85.5297,38.1453,'Qiemo',NULL,NULL),(652826,'焉耆回族自治县',652800,'焉耆',3,'0996','841100','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,焉耆回族自治县',86.5744,42.059,'Yanqi',NULL,NULL),(652827,'和静县',652800,'和静',3,'0996','841300','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,和静县',86.3961,42.3184,'Hejing',NULL,NULL),(652828,'和硕县',652800,'和硕',3,'0996','841200','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,和硕县',86.8639,42.2681,'Heshuo',NULL,NULL),(652829,'博湖县',652800,'博湖',3,'0996','841400','中国,新疆维吾尔自治区,巴音郭楞蒙古自治州,博湖县',86.6333,41.9801,'Bohu',NULL,NULL),(652900,'阿克苏地区',650000,'阿克苏',2,'0997','843000','中国,新疆维吾尔自治区,阿克苏地区',80.2651,41.1707,'Aksu',NULL,NULL),(652901,'阿克苏市',652900,'阿克苏',3,'0997','843000','中国,新疆维吾尔自治区,阿克苏地区,阿克苏市',80.2634,41.1675,'Akesu',NULL,NULL),(652922,'温宿县',652900,'温宿',3,'0997','843100','中国,新疆维吾尔自治区,阿克苏地区,温宿县',80.2417,41.2768,'Wensu',NULL,NULL),(652923,'库车县',652900,'库车',3,'0997','842000','中国,新疆维吾尔自治区,阿克苏地区,库车县',82.9621,41.7179,'Kuche',NULL,NULL),(652924,'沙雅县',652900,'沙雅',3,'0997','842200','中国,新疆维吾尔自治区,阿克苏地区,沙雅县',82.7813,41.225,'Shaya',NULL,NULL),(652925,'新和县',652900,'新和',3,'0997','842100','中国,新疆维吾尔自治区,阿克苏地区,新和县',82.6105,41.5496,'Xinhe',NULL,NULL),(652926,'拜城县',652900,'拜城',3,'0997','842300','中国,新疆维吾尔自治区,阿克苏地区,拜城县',81.8756,41.798,'Baicheng',NULL,NULL),(652927,'乌什县',652900,'乌什',3,'0997','843400','中国,新疆维吾尔自治区,阿克苏地区,乌什县',79.2294,41.2157,'Wushi',NULL,NULL),(652928,'阿瓦提县',652900,'阿瓦提',3,'0997','843200','中国,新疆维吾尔自治区,阿克苏地区,阿瓦提县',80.3834,40.6393,'Awati',NULL,NULL),(652929,'柯坪县',652900,'柯坪',3,'0997','843600','中国,新疆维吾尔自治区,阿克苏地区,柯坪县',79.0475,40.5059,'Keping',NULL,NULL),(653000,'克孜勒苏柯尔克孜自治州',650000,'克孜勒苏',2,'0908','845350','中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州',76.1728,39.7134,'Kizilsu',NULL,NULL),(653001,'阿图什市',653000,'阿图什',3,'0908','845350','中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿图什市',76.1683,39.7161,'Atushi',NULL,NULL),(653022,'阿克陶县',653000,'阿克陶',3,'0908','845550','中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿克陶县',75.9469,39.1489,'Aketao',NULL,NULL),(653023,'阿合奇县',653000,'阿合奇',3,'0997','843500','中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,阿合奇县',78.4485,40.9395,'Aheqi',NULL,NULL),(653024,'乌恰县',653000,'乌恰',3,'0908','845450','中国,新疆维吾尔自治区,克孜勒苏柯尔克孜自治州,乌恰县',75.2584,39.7198,'Wuqia',NULL,NULL),(653100,'喀什地区',650000,'喀什',2,'0998','844000','中国,新疆维吾尔自治区,喀什地区',75.9891,39.4677,'Kashgar',NULL,NULL),(653101,'喀什市',653100,'喀什',3,'0998','844000','中国,新疆维吾尔自治区,喀什地区,喀什市',75.9938,39.4677,'Kashi',NULL,NULL),(653121,'疏附县',653100,'疏附',3,'0998','844100','中国,新疆维吾尔自治区,喀什地区,疏附县',75.8603,39.3753,'Shufu',NULL,NULL),(653122,'疏勒县',653100,'疏勒',3,'0998','844200','中国,新疆维吾尔自治区,喀什地区,疏勒县',76.054,39.4062,'Shule',NULL,NULL),(653123,'英吉沙县',653100,'英吉沙',3,'0998','844500','中国,新疆维吾尔自治区,喀什地区,英吉沙县',76.1757,38.9297,'Yingjisha',NULL,NULL),(653124,'泽普县',653100,'泽普',3,'0998','844800','中国,新疆维吾尔自治区,喀什地区,泽普县',77.2714,38.1894,'Zepu',NULL,NULL),(653125,'莎车县',653100,'莎车',3,'0998','844700','中国,新疆维吾尔自治区,喀什地区,莎车县',77.2432,38.416,'Shache',NULL,NULL),(653126,'叶城县',653100,'叶城',3,'0998','844900','中国,新疆维吾尔自治区,喀什地区,叶城县',77.4166,37.8832,'Yecheng',NULL,NULL),(653127,'麦盖提县',653100,'麦盖提',3,'0998','844600','中国,新疆维吾尔自治区,喀什地区,麦盖提县',77.6422,38.8966,'Maigaiti',NULL,NULL),(653128,'岳普湖县',653100,'岳普湖',3,'0998','844400','中国,新疆维吾尔自治区,喀什地区,岳普湖县',76.7723,39.2356,'Yuepuhu',NULL,NULL),(653129,'伽师县',653100,'伽师',3,'0998','844300','中国,新疆维吾尔自治区,喀什地区,伽师县',76.7237,39.488,'Jiashi',NULL,NULL),(653130,'巴楚县',653100,'巴楚',3,'0998','843800','中国,新疆维吾尔自治区,喀什地区,巴楚县',78.5489,39.7855,'Bachu',NULL,NULL),(653131,'塔什库尔干塔吉克自治县',653100,'塔什库尔干塔吉克',3,'0998','845250','中国,新疆维吾尔自治区,喀什地区,塔什库尔干塔吉克自治县',75.232,37.7789,'Tashikuergantajike',NULL,NULL),(653200,'和田地区',650000,'和田',2,'0903','848000','中国,新疆维吾尔自治区,和田地区',79.9253,37.1107,'Hotan',NULL,NULL),(653201,'和田市',653200,'和田市',3,'0903','848000','中国,新疆维吾尔自治区,和田地区,和田市',79.9135,37.1121,'Hetianshi',NULL,NULL),(653221,'和田县',653200,'和田县',3,'0903','848000','中国,新疆维吾尔自治区,和田地区,和田县',79.8287,37.0892,'Hetianxian',NULL,NULL),(653222,'墨玉县',653200,'墨玉',3,'0903','848100','中国,新疆维吾尔自治区,和田地区,墨玉县',79.7403,37.2725,'Moyu',NULL,NULL),(653223,'皮山县',653200,'皮山',3,'0903','845150','中国,新疆维吾尔自治区,和田地区,皮山县',78.2812,37.6201,'Pishan',NULL,NULL),(653224,'洛浦县',653200,'洛浦',3,'0903','848200','中国,新疆维吾尔自治区,和田地区,洛浦县',80.1854,37.0736,'Luopu',NULL,NULL),(653225,'策勒县',653200,'策勒',3,'0903','848300','中国,新疆维吾尔自治区,和田地区,策勒县',80.81,36.9984,'Cele',NULL,NULL),(653226,'于田县',653200,'于田',3,'0903','848400','中国,新疆维吾尔自治区,和田地区,于田县',81.6672,36.854,'Yutian',NULL,NULL),(653227,'民丰县',653200,'民丰',3,'0903','848500','中国,新疆维吾尔自治区,和田地区,民丰县',82.6844,37.0658,'Minfeng',NULL,NULL),(654000,'伊犁哈萨克自治州',650000,'伊犁',2,'0999','835100','中国,新疆维吾尔自治区,伊犁哈萨克自治州',81.3179,43.9219,'Ili',NULL,NULL),(654002,'伊宁市',654000,'伊宁',3,'0999','835000','中国,新疆维吾尔自治区,伊犁哈萨克自治州,伊宁市',81.3293,43.9129,'Yining',NULL,NULL),(654003,'奎屯市',654000,'奎屯',3,'0992','833200','中国,新疆维吾尔自治区,伊犁哈萨克自治州,奎屯市',84.9023,44.425,'Kuitun',NULL,NULL),(654004,'霍尔果斯市',654000,'霍尔果斯',3,'0999','835221','中国,新疆维吾尔自治区,伊犁哈萨克自治州,霍尔果斯市',80.4182,44.2058,'Huoerguosi',NULL,NULL),(654021,'伊宁县',654000,'伊宁',3,'0999','835100','中国,新疆维吾尔自治区,伊犁哈萨克自治州,伊宁县',81.5276,43.9786,'Yining',NULL,NULL),(654022,'察布查尔锡伯自治县',654000,'察布查尔锡伯',3,'0999','835300','中国,新疆维吾尔自治区,伊犁哈萨克自治州,察布查尔锡伯自治县',81.1496,43.8402,'Chabuchaerxibo',NULL,NULL),(654023,'霍城县',654000,'霍城',3,'0999','835200','中国,新疆维吾尔自治区,伊犁哈萨克自治州,霍城县',80.8783,44.0533,'Huocheng',NULL,NULL),(654024,'巩留县',654000,'巩留',3,'0999','835400','中国,新疆维吾尔自治区,伊犁哈萨克自治州,巩留县',82.2285,43.4843,'Gongliu',NULL,NULL),(654025,'新源县',654000,'新源',3,'0999','835800','中国,新疆维吾尔自治区,伊犁哈萨克自治州,新源县',83.2609,43.4284,'Xinyuan',NULL,NULL),(654026,'昭苏县',654000,'昭苏',3,'0999','835600','中国,新疆维吾尔自治区,伊犁哈萨克自治州,昭苏县',81.1307,43.1583,'Zhaosu',NULL,NULL),(654027,'特克斯县',654000,'特克斯',3,'0999','835500','中国,新疆维吾尔自治区,伊犁哈萨克自治州,特克斯县',81.84,43.2194,'Tekesi',NULL,NULL),(654028,'尼勒克县',654000,'尼勒克',3,'0999','835700','中国,新疆维吾尔自治区,伊犁哈萨克自治州,尼勒克县',82.5118,43.799,'Nileke',NULL,NULL),(654200,'塔城地区',650000,'塔城',2,'0901','834700','中国,新疆维吾尔自治区,塔城地区',82.9857,46.7463,'Qoqek',NULL,NULL),(654201,'塔城市',654200,'塔城',3,'0901','834700','中国,新疆维吾尔自治区,塔城地区,塔城市',82.9789,46.7485,'Tacheng',NULL,NULL),(654202,'乌苏市',654200,'乌苏',3,'0992','833000','中国,新疆维吾尔自治区,塔城地区,乌苏市',84.6826,44.4373,'Wusu',NULL,NULL),(654221,'额敏县',654200,'额敏',3,'0901','834600','中国,新疆维吾尔自治区,塔城地区,额敏县',83.6287,46.5284,'Emin',NULL,NULL),(654223,'沙湾县',654200,'沙湾',3,'0993','832100','中国,新疆维吾尔自治区,塔城地区,沙湾县',85.6193,44.3314,'Shawan',NULL,NULL),(654224,'托里县',654200,'托里',3,'0901','834500','中国,新疆维吾尔自治区,塔城地区,托里县',83.6059,45.9362,'Tuoli',NULL,NULL),(654225,'裕民县',654200,'裕民',3,'0901','834800','中国,新疆维吾尔自治区,塔城地区,裕民县',82.99,46.2038,'Yumin',NULL,NULL),(654226,'和布克赛尔蒙古自治县',654200,'和布克赛尔',3,'0990','834400','中国,新疆维吾尔自治区,塔城地区,和布克赛尔蒙古自治县',85.7266,46.7936,'Hebukesaier',NULL,NULL),(654300,'阿勒泰地区',650000,'阿勒泰',2,'0906','836500','中国,新疆维吾尔自治区,阿勒泰地区',88.1396,47.8484,'Altay',NULL,NULL),(654301,'阿勒泰市',654300,'阿勒泰',3,'0906','836500','中国,新疆维吾尔自治区,阿勒泰地区,阿勒泰市',88.1391,47.8317,'Aletai',NULL,NULL),(654321,'布尔津县',654300,'布尔津',3,'0906','836600','中国,新疆维吾尔自治区,阿勒泰地区,布尔津县',86.8575,47.7006,'Buerjin',NULL,NULL),(654322,'富蕴县',654300,'富蕴',3,'0906','836100','中国,新疆维吾尔自治区,阿勒泰地区,富蕴县',89.5268,46.9944,'Fuyun',NULL,NULL),(654323,'福海县',654300,'福海',3,'0906','836400','中国,新疆维吾尔自治区,阿勒泰地区,福海县',87.4951,47.1106,'Fuhai',NULL,NULL),(654324,'哈巴河县',654300,'哈巴河',3,'0906','836700','中国,新疆维吾尔自治区,阿勒泰地区,哈巴河县',86.4209,48.0605,'Habahe',NULL,NULL),(654325,'青河县',654300,'青河',3,'0906','836200','中国,新疆维吾尔自治区,阿勒泰地区,青河县',90.383,46.6738,'Qinghe',NULL,NULL),(654326,'吉木乃县',654300,'吉木乃',3,'0906','836800','中国,新疆维吾尔自治区,阿勒泰地区,吉木乃县',85.8781,47.4336,'Jimunai',NULL,NULL),(659000,'直辖县级',650000,' ',2,'','','中国,新疆维吾尔自治区,直辖县级',91.1322,29.6604,'',NULL,NULL),(659001,'石河子市',659000,'石河子',3,'0993','832000','中国,新疆维吾尔自治区,直辖县级,石河子市',86.0411,44.3059,'Shihezi',NULL,NULL),(659002,'阿拉尔市',659000,'阿拉尔',3,'0997','843300','中国,新疆维吾尔自治区,直辖县级,阿拉尔市',81.2859,40.5419,'Aral',NULL,NULL),(659003,'图木舒克市',659000,'图木舒克',3,'0998','843806','中国,新疆维吾尔自治区,直辖县级,图木舒克市',79.078,39.8673,'Tumxuk',NULL,NULL),(659004,'五家渠市',659000,'五家渠',3,'0994','831300','中国,新疆维吾尔自治区,直辖县级,五家渠市',87.5269,44.1674,'Wujiaqu',NULL,NULL),(659005,'北屯市',659000,'北屯',3,'0906','836000','中国,新疆维吾尔自治区,直辖县级,北屯市',87.8085,47.3623,'Beitun',NULL,NULL),(659006,'铁门关市',659000,'铁门关',3,'0906','836000','中国,新疆维吾尔自治区,直辖县级,铁门关市',86.1947,41.811,'Tiemenguan',NULL,NULL),(659007,'双河市',659000,'双河',3,'0909','833408','中国,新疆维吾尔自治区,直辖县级,双河市',91.1322,29.6604,'Shuanghe',NULL,NULL),(710000,'台湾',100000,'台湾',1,'','','中国,台湾',121.509,25.0443,'Taiwan',NULL,NULL),(710100,'台北市',710000,'台北',2,'02','1','中国,台湾,台北市',121.565,25.0378,'Taipei',NULL,NULL),(710101,'松山区',710100,'松山',3,'02','105','中国,台湾,台北市,松山区',121.577,25.0497,'Songshan',NULL,NULL),(710102,'信义区',710100,'信义',3,'02','110','中国,台湾,台北市,信义区',121.751,25.1294,'Xinyi',NULL,NULL),(710103,'大安区',710100,'大安',3,'02','106','中国,台湾,台北市,大安区',121.535,25.0264,'Da\'an',NULL,NULL),(710104,'中山区',710100,'中山',3,'02','104','中国,台湾,台北市,中山区',121.533,25.0644,'Zhongshan',NULL,NULL),(710105,'中正区',710100,'中正',3,'02','100','中国,台湾,台北市,中正区',121.518,25.0324,'Zhongzheng',NULL,NULL),(710106,'大同区',710100,'大同',3,'02','103','中国,台湾,台北市,大同区',121.516,25.066,'Datong',NULL,NULL),(710107,'万华区',710100,'万华',3,'02','108','中国,台湾,台北市,万华区',121.499,25.0319,'Wanhua',NULL,NULL),(710108,'文山区',710100,'文山',3,'02','116','中国,台湾,台北市,文山区',121.57,24.9898,'Wenshan',NULL,NULL),(710109,'南港区',710100,'南港',3,'02','115','中国,台湾,台北市,南港区',121.607,25.0547,'Nangang',NULL,NULL),(710110,'内湖区',710100,'内湖',3,'02','114','中国,台湾,台北市,内湖区',121.589,25.0697,'Nahu',NULL,NULL),(710111,'士林区',710100,'士林',3,'02','111','中国,台湾,台北市,士林区',121.52,25.0928,'Shilin',NULL,NULL),(710112,'北投区',710100,'北投',3,'02','112','中国,台湾,台北市,北投区',121.501,25.1324,'Beitou',NULL,NULL),(710200,'高雄市',710000,'高雄',2,'07','8','中国,台湾,高雄市',120.312,22.6209,'Kaohsiung',NULL,NULL),(710201,'盐埕区',710200,'盐埕',3,'07','803','中国,台湾,高雄市,盐埕区',120.287,22.6247,'Yancheng',NULL,NULL),(710202,'鼓山区',710200,'鼓山',3,'07','804','中国,台湾,高雄市,鼓山区',120.281,22.6368,'Gushan',NULL,NULL),(710203,'左营区',710200,'左营',3,'07','813','中国,台湾,高雄市,左营区',120.295,22.6901,'Zuoying',NULL,NULL),(710204,'楠梓区',710200,'楠梓',3,'07','811','中国,台湾,高雄市,楠梓区',120.326,22.7284,'Nanzi',NULL,NULL),(710205,'三民区',710200,'三民',3,'07','807','中国,台湾,高雄市,三民区',120.3,22.6477,'Sanmin',NULL,NULL),(710206,'新兴区',710200,'新兴',3,'07','800','中国,台湾,高雄市,新兴区',120.31,22.6311,'Xinxing',NULL,NULL),(710207,'前金区',710200,'前金',3,'07','801','中国,台湾,高雄市,前金区',120.294,22.6274,'Qianjin',NULL,NULL),(710208,'苓雅区',710200,'苓雅',3,'07','802','中国,台湾,高雄市,苓雅区',120.312,22.6218,'Lingya',NULL,NULL),(710209,'前镇区',710200,'前镇',3,'07','806','中国,台湾,高雄市,前镇区',120.319,22.5864,'Qianzhen',NULL,NULL),(710210,'旗津区',710200,'旗津',3,'07','805','中国,台湾,高雄市,旗津区',120.284,22.5906,'Qijin',NULL,NULL),(710211,'小港区',710200,'小港',3,'07','812','中国,台湾,高雄市,小港区',120.338,22.5654,'Xiaogang',NULL,NULL),(710212,'凤山区',710200,'凤山',3,'07','830','中国,台湾,高雄市,凤山区',120.357,22.6269,'Fengshan',NULL,NULL),(710213,'林园区',710200,'林园',3,'07','832','中国,台湾,高雄市,林园区',120.396,22.5015,'Linyuan',NULL,NULL),(710214,'大寮区',710200,'大寮',3,'07','831','中国,台湾,高雄市,大寮区',120.395,22.6054,'Daliao',NULL,NULL),(710215,'大树区',710200,'大树',3,'07','840','中国,台湾,高雄市,大树区',120.433,22.6934,'Dashu',NULL,NULL),(710216,'大社区',710200,'大社',3,'07','815','中国,台湾,高雄市,大社区',120.347,22.73,'Dashe',NULL,NULL),(710217,'仁武区',710200,'仁武',3,'07','814','中国,台湾,高雄市,仁武区',120.348,22.7019,'Renwu',NULL,NULL),(710218,'鸟松区',710200,'鸟松',3,'07','833','中国,台湾,高雄市,鸟松区',120.364,22.6593,'Niaosong',NULL,NULL),(710219,'冈山区',710200,'冈山',3,'07','820','中国,台湾,高雄市,冈山区',120.296,22.7968,'Gangshan',NULL,NULL),(710220,'桥头区',710200,'桥头',3,'07','825','中国,台湾,高雄市,桥头区',120.306,22.7575,'Qiaotou',NULL,NULL),(710221,'燕巢区',710200,'燕巢',3,'07','824','中国,台湾,高雄市,燕巢区',120.362,22.7934,'Yanchao',NULL,NULL),(710222,'田寮区',710200,'田寮',3,'07','823','中国,台湾,高雄市,田寮区',120.36,22.8693,'Tianliao',NULL,NULL),(710223,'阿莲区',710200,'阿莲',3,'07','822','中国,台湾,高雄市,阿莲区',120.327,22.8837,'Alian',NULL,NULL),(710224,'路竹区',710200,'路竹',3,'07','821','中国,台湾,高雄市,路竹区',120.262,22.8569,'Luzhu',NULL,NULL),(710225,'湖内区',710200,'湖内',3,'07','829','中国,台湾,高雄市,湖内区',120.212,22.9082,'Huna',NULL,NULL),(710226,'茄萣区',710200,'茄萣',3,'07','852','中国,台湾,高雄市,茄萣区',120.183,22.9066,'Qieding',NULL,NULL),(710227,'永安区',710200,'永安',3,'07','828','中国,台湾,高雄市,永安区',120.225,22.8186,'Yong\'an',NULL,NULL),(710228,'弥陀区',710200,'弥陀',3,'07','827','中国,台湾,高雄市,弥陀区',120.247,22.7829,'Mituo',NULL,NULL),(710229,'梓官区',710200,'梓官',3,'07','826','中国,台湾,高雄市,梓官区',120.267,22.7605,'Ziguan',NULL,NULL),(710230,'旗山区',710200,'旗山',3,'07','842','中国,台湾,高雄市,旗山区',120.484,22.8885,'Qishan',NULL,NULL),(710231,'美浓区',710200,'美浓',3,'07','843','中国,台湾,高雄市,美浓区',120.542,22.8979,'Meinong',NULL,NULL),(710232,'六龟区',710200,'六龟',3,'07','844','中国,台湾,高雄市,六龟区',120.633,22.9979,'Liugui',NULL,NULL),(710233,'甲仙区',710200,'甲仙',3,'07','847','中国,台湾,高雄市,甲仙区',120.591,23.0847,'Jiaxian',NULL,NULL),(710234,'杉林区',710200,'杉林',3,'07','846','中国,台湾,高雄市,杉林区',120.539,22.9707,'Shanlin',NULL,NULL),(710235,'内门区',710200,'内门',3,'07','845','中国,台湾,高雄市,内门区',120.462,22.9434,'Namen',NULL,NULL),(710236,'茂林区',710200,'茂林',3,'07','851','中国,台湾,高雄市,茂林区',120.663,22.8862,'Maolin',NULL,NULL),(710237,'桃源区',710200,'桃源',3,'07','848','中国,台湾,高雄市,桃源区',120.76,23.1591,'Taoyuan',NULL,NULL),(710238,'那玛夏区',710200,'那玛夏',3,'07','849','中国,台湾,高雄市,那玛夏区',120.693,23.217,'Namaxia',NULL,NULL),(710300,'基隆市',710000,'基隆',2,'02','2','中国,台湾,基隆市',121.746,25.1307,'Keelung',NULL,NULL),(710301,'中正区',710300,'中正',3,'02','202','中国,台湾,基隆市,中正区',121.518,25.0324,'Zhongzheng',NULL,NULL),(710302,'七堵区',710300,'七堵',3,'02','206','中国,台湾,基隆市,七堵区',121.713,25.0957,'Qidu',NULL,NULL),(710303,'暖暖区',710300,'暖暖',3,'02','205','中国,台湾,基隆市,暖暖区',121.736,25.0998,'Nuannuan',NULL,NULL),(710304,'仁爱区',710300,'仁爱',3,'02','200','中国,台湾,基隆市,仁爱区',121.741,25.1275,'Renai',NULL,NULL),(710305,'中山区',710300,'中山',3,'02','203','中国,台湾,基隆市,中山区',121.739,25.134,'Zhongshan',NULL,NULL),(710306,'安乐区',710300,'安乐',3,'02','204','中国,台湾,基隆市,安乐区',121.723,25.1209,'Anle',NULL,NULL),(710307,'信义区',710300,'信义',3,'02','201','中国,台湾,基隆市,信义区',121.751,25.1294,'Xinyi',NULL,NULL),(710400,'台中市',710000,'台中',2,'04','4','中国,台湾,台中市',120.679,24.1386,'Taichung',NULL,NULL),(710401,'中区',710400,'中区',3,'04','400','中国,台湾,台中市,中区',120.68,24.1438,'Zhongqu',NULL,NULL),(710402,'东区',710400,'东区',3,'04','401','中国,台湾,台中市,东区',120.704,24.1366,'Dongqu',NULL,NULL),(710403,'南区',710400,'南区',3,'04','402','中国,台湾,台中市,南区',120.189,22.9609,'Nanqu',NULL,NULL),(710404,'西区',710400,'西区',3,'04','403','中国,台湾,台中市,西区',120.671,24.1414,'Xiqu',NULL,NULL),(710405,'北区',710400,'北区',3,'04','404','中国,台湾,台中市,北区',120.682,24.166,'Beiqu',NULL,NULL),(710406,'西屯区',710400,'西屯',3,'04','407','中国,台湾,台中市,西屯区',120.64,24.1813,'Xitun',NULL,NULL),(710407,'南屯区',710400,'南屯',3,'04','408','中国,台湾,台中市,南屯区',120.643,24.1383,'Nantun',NULL,NULL),(710408,'北屯区',710400,'北屯',3,'04','406','中国,台湾,台中市,北屯区',120.686,24.1822,'Beitun',NULL,NULL),(710409,'丰原区',710400,'丰原',3,'04','420','中国,台湾,台中市,丰原区',120.718,24.2422,'Fengyuan',NULL,NULL),(710410,'东势区',710400,'东势',3,'04','423','中国,台湾,台中市,东势区',120.828,24.2586,'Dongshi',NULL,NULL),(710411,'大甲区',710400,'大甲',3,'04','437','中国,台湾,台中市,大甲区',120.622,24.3489,'Dajia',NULL,NULL),(710412,'清水区',710400,'清水',3,'04','436','中国,台湾,台中市,清水区',120.56,24.2687,'Qingshui',NULL,NULL),(710413,'沙鹿区',710400,'沙鹿',3,'04','433','中国,台湾,台中市,沙鹿区',120.566,24.2335,'Shalu',NULL,NULL),(710414,'梧栖区',710400,'梧栖',3,'04','435','中国,台湾,台中市,梧栖区',120.532,24.255,'Wuqi',NULL,NULL),(710415,'后里区',710400,'后里',3,'04','421','中国,台湾,台中市,后里区',120.711,24.3049,'Houli',NULL,NULL),(710416,'神冈区',710400,'神冈',3,'04','429','中国,台湾,台中市,神冈区',120.662,24.2578,'Shengang',NULL,NULL),(710417,'潭子区',710400,'潭子',3,'04','427','中国,台湾,台中市,潭子区',120.705,24.2095,'Tanzi',NULL,NULL),(710418,'大雅区',710400,'大雅',3,'04','428','中国,台湾,台中市,大雅区',120.648,24.2292,'Daya',NULL,NULL),(710419,'新社区',710400,'新社',3,'04','426','中国,台湾,台中市,新社区',120.81,24.2341,'Xinshe',NULL,NULL),(710420,'石冈区',710400,'石冈',3,'04','422','中国,台湾,台中市,石冈区',120.78,24.275,'Shigang',NULL,NULL),(710421,'外埔区',710400,'外埔',3,'04','438','中国,台湾,台中市,外埔区',120.654,24.332,'Waipu',NULL,NULL),(710422,'大安区',710400,'大安',3,'04','439','中国,台湾,台中市,大安区',120.587,24.3461,'Da\'an',NULL,NULL),(710423,'乌日区',710400,'乌日',3,'04','414','中国,台湾,台中市,乌日区',120.624,24.1045,'Wuri',NULL,NULL),(710424,'大肚区',710400,'大肚',3,'04','432','中国,台湾,台中市,大肚区',120.541,24.1537,'Dadu',NULL,NULL),(710425,'龙井区',710400,'龙井',3,'04','434','中国,台湾,台中市,龙井区',120.546,24.1927,'Longjing',NULL,NULL),(710426,'雾峰区',710400,'雾峰',3,'04','413','中国,台湾,台中市,雾峰区',120.7,24.0615,'Wufeng',NULL,NULL),(710427,'太平区',710400,'太平',3,'04','411','中国,台湾,台中市,太平区',120.719,24.1265,'Taiping',NULL,NULL),(710428,'大里区',710400,'大里',3,'04','412','中国,台湾,台中市,大里区',120.678,24.0994,'Dali',NULL,NULL),(710429,'和平区',710400,'和平',3,'04','424','中国,台湾,台中市,和平区',120.883,24.1748,'Heping',NULL,NULL),(710500,'台南市',710000,'台南',2,'06','7','中国,台湾,台南市',120.279,23.1725,'Tainan',NULL,NULL),(710501,'东区',710500,'东区',3,'06','701','中国,台湾,台南市,东区',120.224,22.9801,'Dongqu',NULL,NULL),(710502,'南区',710500,'南区',3,'06','702','中国,台湾,台南市,南区',120.189,22.9609,'Nanqu',NULL,NULL),(710504,'北区',710500,'北区',3,'06','704','中国,台湾,台南市,北区',120.682,24.166,'Beiqu',NULL,NULL),(710506,'安南区',710500,'安南',3,'06','709','中国,台湾,台南市,安南区',120.185,23.0472,'Annan',NULL,NULL),(710507,'安平区',710500,'安平',3,'06','708','中国,台湾,台南市,安平区',120.167,23.0008,'Anping',NULL,NULL),(710508,'中西区',710500,'中西',3,'06','700','中国,台湾,台南市,中西区',120.206,22.9922,'Zhongxi',NULL,NULL),(710509,'新营区',710500,'新营',3,'06','730','中国,台湾,台南市,新营区',120.317,23.3103,'Xinying',NULL,NULL),(710510,'盐水区',710500,'盐水',3,'06','737','中国,台湾,台南市,盐水区',120.266,23.3198,'Yanshui',NULL,NULL),(710511,'白河区',710500,'白河',3,'06','732','中国,台湾,台南市,白河区',120.416,23.3512,'Baihe',NULL,NULL),(710512,'柳营区',710500,'柳营',3,'06','736','中国,台湾,台南市,柳营区',120.311,23.2781,'Liuying',NULL,NULL),(710513,'后壁区',710500,'后壁',3,'06','731','中国,台湾,台南市,后壁区',120.363,23.3667,'Houbi',NULL,NULL),(710514,'东山区',710500,'东山',3,'06','733','中国,台湾,台南市,东山区',120.404,23.3261,'Dongshan',NULL,NULL),(710515,'麻豆区',710500,'麻豆',3,'06','721','中国,台湾,台南市,麻豆区',120.248,23.1817,'Madou',NULL,NULL),(710516,'下营区',710500,'下营',3,'06','735','中国,台湾,台南市,下营区',120.264,23.2354,'Xiaying',NULL,NULL),(710517,'六甲区',710500,'六甲',3,'06','734','中国,台湾,台南市,六甲区',120.348,23.2319,'Liujia',NULL,NULL),(710518,'官田区',710500,'官田',3,'06','720','中国,台湾,台南市,官田区',120.314,23.1947,'Guantian',NULL,NULL),(710519,'大内区',710500,'大内',3,'06','742','中国,台湾,台南市,大内区',120.349,23.1195,'Dana',NULL,NULL),(710520,'佳里区',710500,'佳里',3,'06','722','中国,台湾,台南市,佳里区',120.177,23.1651,'Jiali',NULL,NULL),(710521,'学甲区',710500,'学甲',3,'06','726','中国,台湾,台南市,学甲区',120.18,23.2323,'Xuejia',NULL,NULL),(710522,'西港区',710500,'西港',3,'06','723','中国,台湾,台南市,西港区',120.204,23.1231,'Xigang',NULL,NULL),(710523,'七股区',710500,'七股',3,'06','724','中国,台湾,台南市,七股区',120.14,23.1405,'Qigu',NULL,NULL),(710524,'将军区',710500,'将军',3,'06','725','中国,台湾,台南市,将军区',120.157,23.1995,'Jiangjun',NULL,NULL),(710525,'北门区',710500,'北门',3,'06','727','中国,台湾,台南市,北门区',120.126,23.2671,'Beimen',NULL,NULL),(710526,'新化区',710500,'新化',3,'06','712','中国,台湾,台南市,新化区',120.311,23.0385,'Xinhua',NULL,NULL),(710527,'善化区',710500,'善化',3,'06','741','中国,台湾,台南市,善化区',120.297,23.1323,'Shanhua',NULL,NULL),(710528,'新市区',710500,'新市',3,'06','744','中国,台湾,台南市,新市区',120.295,23.079,'Xinshi',NULL,NULL),(710529,'安定区',710500,'安定',3,'06','745','中国,台湾,台南市,安定区',120.237,23.1215,'Anding',NULL,NULL),(710530,'山上区',710500,'山上',3,'06','743','中国,台湾,台南市,山上区',120.353,23.1032,'Shanshang',NULL,NULL),(710531,'玉井区',710500,'玉井',3,'06','714','中国,台湾,台南市,玉井区',120.46,23.1239,'Yujing',NULL,NULL),(710532,'楠西区',710500,'楠西',3,'06','715','中国,台湾,台南市,楠西区',120.485,23.1735,'Nanxi',NULL,NULL),(710533,'南化区',710500,'南化',3,'06','716','中国,台湾,台南市,南化区',120.477,23.0426,'Nanhua',NULL,NULL),(710534,'左镇区',710500,'左镇',3,'06','713','中国,台湾,台南市,左镇区',120.407,23.058,'Zuozhen',NULL,NULL),(710535,'仁德区',710500,'仁德',3,'06','717','中国,台湾,台南市,仁德区',120.252,22.9722,'Rende',NULL,NULL),(710536,'归仁区',710500,'归仁',3,'06','711','中国,台湾,台南市,归仁区',120.294,22.9671,'Guiren',NULL,NULL),(710537,'关庙区',710500,'关庙',3,'06','718','中国,台湾,台南市,关庙区',120.328,22.9629,'Guanmiao',NULL,NULL),(710538,'龙崎区',710500,'龙崎',3,'06','719','中国,台湾,台南市,龙崎区',120.361,22.9657,'Longqi',NULL,NULL),(710539,'永康区',710500,'永康',3,'06','710','中国,台湾,台南市,永康区',120.257,23.0261,'Yongkang',NULL,NULL),(710600,'新竹市',710000,'新竹',2,'03','3','中国,台湾,新竹市',120.969,24.8067,'Hsinchu',NULL,NULL),(710601,'东区',710600,'东区',3,'03','300','中国,台湾,新竹市,东区',120.97,24.8013,'Dongqu',NULL,NULL),(710602,'北区',710600,'北区',3,'03','','中国,台湾,新竹市,北区',120.682,24.166,'Beiqu',NULL,NULL),(710603,'香山区',710600,'香山',3,'03','','中国,台湾,新竹市,香山区',120.957,24.7689,'Xiangshan',NULL,NULL),(710700,'嘉义市',710000,'嘉义',2,'05','6','中国,台湾,嘉义市',120.453,23.4816,'Chiayi',NULL,NULL),(710701,'东区',710700,'东区',3,'05','600','中国,台湾,嘉义市,东区',120.458,23.4862,'Dongqu',NULL,NULL),(710702,'西区',710700,'西区',3,'05','600','中国,台湾,嘉义市,西区',120.437,23.473,'Xiqu',NULL,NULL),(710800,'新北市',710000,'新北',2,'02','2','中国,台湾,新北市',121.466,25.0124,'New Taipei',NULL,NULL),(710801,'板桥区',710800,'板桥',3,'02','220','中国,台湾,新北市,板桥区',121.459,25.0096,'Banqiao',NULL,NULL),(710802,'三重区',710800,'三重',3,'02','241','中国,台湾,新北市,三重区',121.488,25.0615,'Sanzhong',NULL,NULL),(710803,'中和区',710800,'中和',3,'02','235','中国,台湾,新北市,中和区',121.499,24.9994,'Zhonghe',NULL,NULL),(710804,'永和区',710800,'永和',3,'02','234','中国,台湾,新北市,永和区',121.514,25.0078,'Yonghe',NULL,NULL),(710805,'新庄区',710800,'新庄',3,'02','242','中国,台湾,新北市,新庄区',121.45,25.0359,'Xinzhuang',NULL,NULL),(710806,'新店区',710800,'新店',3,'02','231','中国,台湾,新北市,新店区',121.542,24.9676,'Xindian',NULL,NULL),(710807,'树林区',710800,'树林',3,'02','238','中国,台湾,新北市,树林区',121.421,24.9907,'Shulin',NULL,NULL),(710808,'莺歌区',710800,'莺歌',3,'02','239','中国,台湾,新北市,莺歌区',121.355,24.9554,'Yingge',NULL,NULL),(710809,'三峡区',710800,'三峡',3,'02','237','中国,台湾,新北市,三峡区',121.369,24.9343,'Sanxia',NULL,NULL),(710810,'淡水区',710800,'淡水',3,'02','251','中国,台湾,新北市,淡水区',121.441,25.1695,'Danshui',NULL,NULL),(710811,'汐止区',710800,'汐止',3,'02','221','中国,台湾,新北市,汐止区',121.629,25.063,'Xizhi',NULL,NULL),(710812,'瑞芳区',710800,'瑞芳',3,'02','224','中国,台湾,新北市,瑞芳区',121.81,25.1089,'Ruifang',NULL,NULL),(710813,'土城区',710800,'土城',3,'02','236','中国,台湾,新北市,土城区',121.443,24.9722,'Tucheng',NULL,NULL),(710814,'芦洲区',710800,'芦洲',3,'02','247','中国,台湾,新北市,芦洲区',121.474,25.0849,'Luzhou',NULL,NULL),(710815,'五股区',710800,'五股',3,'02','248','中国,台湾,新北市,五股区',121.438,25.0827,'Wugu',NULL,NULL),(710816,'泰山区',710800,'泰山',3,'02','243','中国,台湾,新北市,泰山区',121.431,25.0589,'Taishan',NULL,NULL),(710817,'林口区',710800,'林口',3,'02','244','中国,台湾,新北市,林口区',121.392,25.0775,'Linkou',NULL,NULL),(710818,'深坑区',710800,'深坑',3,'02','222','中国,台湾,新北市,深坑区',121.616,25.0023,'Shenkeng',NULL,NULL),(710819,'石碇区',710800,'石碇',3,'02','223','中国,台湾,新北市,石碇区',121.659,24.9917,'Shiding',NULL,NULL),(710820,'坪林区',710800,'坪林',3,'02','232','中国,台湾,新北市,坪林区',121.711,24.9374,'Pinglin',NULL,NULL),(710821,'三芝区',710800,'三芝',3,'02','252','中国,台湾,新北市,三芝区',121.501,25.258,'Sanzhi',NULL,NULL),(710822,'石门区',710800,'石门',3,'02','253','中国,台湾,新北市,石门区',121.568,25.2904,'Shimen',NULL,NULL),(710823,'八里区',710800,'八里',3,'02','249','中国,台湾,新北市,八里区',121.398,25.1467,'Bali',NULL,NULL),(710824,'平溪区',710800,'平溪',3,'02','226','中国,台湾,新北市,平溪区',121.738,25.0257,'Pingxi',NULL,NULL),(710825,'双溪区',710800,'双溪',3,'02','227','中国,台湾,新北市,双溪区',121.866,25.0334,'Shuangxi',NULL,NULL),(710826,'贡寮区',710800,'贡寮',3,'02','228','中国,台湾,新北市,贡寮区',121.908,25.0224,'Gongliao',NULL,NULL),(710827,'金山区',710800,'金山',3,'02','208','中国,台湾,新北市,金山区',121.636,25.2219,'Jinshan',NULL,NULL),(710828,'万里区',710800,'万里',3,'02','207','中国,台湾,新北市,万里区',121.689,25.1812,'Wanli',NULL,NULL),(710829,'乌来区',710800,'乌来',3,'02','233','中国,台湾,新北市,乌来区',121.551,24.8653,'Wulai',NULL,NULL),(712200,'宜兰县',710000,'宜兰',2,'03','2','中国,台湾,宜兰县',121.5,24.6,'Yilan',NULL,NULL),(712201,'宜兰市',712200,'宜兰',3,'03','260','中国,台湾,宜兰县,宜兰市',121.753,24.7517,'Yilan',NULL,NULL),(712221,'罗东镇',712200,'罗东',3,'03','265','中国,台湾,宜兰县,罗东镇',121.767,24.677,'Luodong',NULL,NULL),(712222,'苏澳镇',712200,'苏澳',3,'03','270','中国,台湾,宜兰县,苏澳镇',121.843,24.5946,'Suao',NULL,NULL),(712223,'头城镇',712200,'头城',3,'03','261','中国,台湾,宜兰县,头城镇',121.823,24.8592,'Toucheng',NULL,NULL),(712224,'礁溪乡',712200,'礁溪',3,'03','262','中国,台湾,宜兰县,礁溪乡',121.767,24.8223,'Jiaoxi',NULL,NULL),(712225,'壮围乡',712200,'壮围',3,'03','263','中国,台湾,宜兰县,壮围乡',121.782,24.7449,'Zhuangwei',NULL,NULL),(712226,'员山乡',712200,'员山',3,'03','264','中国,台湾,宜兰县,员山乡',121.722,24.7418,'Yuanshan',NULL,NULL),(712227,'冬山乡',712200,'冬山',3,'03','269','中国,台湾,宜兰县,冬山乡',121.792,24.6345,'Dongshan',NULL,NULL),(712228,'五结乡',712200,'五结',3,'03','268','中国,台湾,宜兰县,五结乡',121.798,24.6846,'Wujie',NULL,NULL),(712229,'三星乡',712200,'三星',3,'03','266','中国,台湾,宜兰县,三星乡',121.003,23.7753,'Sanxing',NULL,NULL),(712230,'大同乡',712200,'大同',3,'03','267','中国,台湾,宜兰县,大同乡',121.606,24.676,'Datong',NULL,NULL),(712231,'南澳乡',712200,'南澳',3,'03','272','中国,台湾,宜兰县,南澳乡',121.8,24.4654,'Nanao',NULL,NULL),(712300,'桃园县',710000,'桃园',2,'03','3','中国,台湾,桃园县',121.083,25,'Taoyuan',NULL,NULL),(712301,'桃园市',712300,'桃园',3,'03','330','中国,台湾,桃园县,桃园市',121.301,24.9938,'Taoyuan',NULL,NULL),(712302,'中坜市',712300,'中坜',3,'03','320','中国,台湾,桃园县,中坜市',121.225,24.9654,'Zhongli',NULL,NULL),(712303,'平镇市',712300,'平镇',3,'03','324','中国,台湾,桃园县,平镇市',121.218,24.9458,'Pingzhen',NULL,NULL),(712304,'八德市',712300,'八德',3,'03','334','中国,台湾,桃园县,八德市',121.285,24.9287,'Bade',NULL,NULL),(712305,'杨梅市',712300,'杨梅',3,'03','326','中国,台湾,桃园县,杨梅市',121.146,24.9076,'Yangmei',NULL,NULL),(712306,'芦竹市',712300,'芦竹',3,'03','338','中国,台湾,桃园县,芦竹市',121.292,25.0454,'Luzhu',NULL,NULL),(712321,'大溪镇',712300,'大溪',3,'03','335','中国,台湾,桃园县,大溪镇',121.287,24.8806,'Daxi',NULL,NULL),(712324,'大园乡',712300,'大园',3,'03','337','中国,台湾,桃园县,大园乡',121.196,25.0645,'Dayuan',NULL,NULL),(712325,'龟山乡',712300,'龟山',3,'03','333','中国,台湾,桃园县,龟山乡',121.338,24.9925,'Guishan',NULL,NULL),(712327,'龙潭乡',712300,'龙潭',3,'03','325','中国,台湾,桃园县,龙潭乡',121.216,24.8639,'Longtan',NULL,NULL),(712329,'新屋乡',712300,'新屋',3,'03','327','中国,台湾,桃园县,新屋乡',121.106,24.9722,'Xinwu',NULL,NULL),(712330,'观音乡',712300,'观音',3,'03','328','中国,台湾,桃园县,观音乡',121.078,25.0333,'Guanyin',NULL,NULL),(712331,'复兴乡',712300,'复兴',3,'03','336','中国,台湾,桃园县,复兴乡',121.353,24.8209,'Fuxing',NULL,NULL),(712400,'新竹县',710000,'新竹',2,'03','3','中国,台湾,新竹县',121.16,24.6,'Hsinchu',NULL,NULL),(712401,'竹北市',712400,'竹北',3,'03','302','中国,台湾,新竹县,竹北市',121.004,24.8397,'Zhubei',NULL,NULL),(712421,'竹东镇',712400,'竹东',3,'03','310','中国,台湾,新竹县,竹东镇',121.086,24.7336,'Zhudong',NULL,NULL),(712422,'新埔镇',712400,'新埔',3,'03','305','中国,台湾,新竹县,新埔镇',121.073,24.8248,'Xinpu',NULL,NULL),(712423,'关西镇',712400,'关西',3,'03','306','中国,台湾,新竹县,关西镇',121.177,24.7888,'Guanxi',NULL,NULL),(712424,'湖口乡',712400,'湖口',3,'03','303','中国,台湾,新竹县,湖口乡',121.044,24.9039,'Hukou',NULL,NULL),(712425,'新丰乡',712400,'新丰',3,'03','304','中国,台湾,新竹县,新丰乡',120.983,24.8996,'Xinfeng',NULL,NULL),(712426,'芎林乡',712400,'芎林',3,'03','307','中国,台湾,新竹县,芎林乡',121.077,24.7744,'Xionglin',NULL,NULL),(712427,'横山乡',712400,'横山',3,'03','312','中国,台湾,新竹县,横山乡',121.116,24.7208,'Hengshan',NULL,NULL),(712428,'北埔乡',712400,'北埔',3,'03','314','中国,台湾,新竹县,北埔乡',121.053,24.6971,'Beipu',NULL,NULL),(712429,'宝山乡',712400,'宝山',3,'03','308','中国,台湾,新竹县,宝山乡',120.986,24.761,'Baoshan',NULL,NULL),(712430,'峨眉乡',712400,'峨眉',3,'03','315','中国,台湾,新竹县,峨眉乡',121.015,24.6861,'Emei',NULL,NULL),(712431,'尖石乡',712400,'尖石',3,'03','313','中国,台湾,新竹县,尖石乡',121.198,24.7044,'Jianshi',NULL,NULL),(712432,'五峰乡',712400,'五峰',3,'03','311','中国,台湾,新竹县,五峰乡',121.003,23.7753,'Wufeng',NULL,NULL),(712500,'苗栗县',710000,'苗栗',2,'037','3','中国,台湾,苗栗县',120.75,24.5,'Miaoli',NULL,NULL),(712501,'苗栗市',712500,'苗栗',3,'037','360','中国,台湾,苗栗县,苗栗市',120.819,24.5615,'Miaoli',NULL,NULL),(712521,'苑里镇',712500,'苑里',3,'037','358','中国,台湾,苗栗县,苑里镇',120.649,24.4417,'Yuanli',NULL,NULL),(712522,'通霄镇',712500,'通霄',3,'037','357','中国,台湾,苗栗县,通霄镇',120.677,24.4891,'Tongxiao',NULL,NULL),(712523,'竹南镇',712500,'竹南',3,'037','350','中国,台湾,苗栗县,竹南镇',120.873,24.6855,'Zhunan',NULL,NULL),(712524,'头份镇',712500,'头份',3,'037','351','中国,台湾,苗栗县,头份镇',120.895,24.688,'Toufen',NULL,NULL),(712525,'后龙镇',712500,'后龙',3,'037','356','中国,台湾,苗栗县,后龙镇',120.786,24.6126,'Houlong',NULL,NULL),(712526,'卓兰镇',712500,'卓兰',3,'037','369','中国,台湾,苗栗县,卓兰镇',120.823,24.3095,'Zhuolan',NULL,NULL),(712527,'大湖乡',712500,'大湖',3,'037','364','中国,台湾,苗栗县,大湖乡',120.864,24.4225,'Dahu',NULL,NULL),(712528,'公馆乡',712500,'公馆',3,'037','363','中国,台湾,苗栗县,公馆乡',120.823,24.4991,'Gongguan',NULL,NULL),(712529,'铜锣乡',712500,'铜锣',3,'037','366','中国,台湾,苗栗县,铜锣乡',121.003,23.7753,'Tongluo',NULL,NULL),(712530,'南庄乡',712500,'南庄',3,'037','353','中国,台湾,苗栗县,南庄乡',120.995,24.5968,'Nanzhuang',NULL,NULL),(712531,'头屋乡',712500,'头屋',3,'037','362','中国,台湾,苗栗县,头屋乡',120.847,24.5742,'Touwu',NULL,NULL),(712532,'三义乡',712500,'三义',3,'037','367','中国,台湾,苗栗县,三义乡',120.742,24.3503,'Sanyi',NULL,NULL),(712533,'西湖乡',712500,'西湖',3,'037','368','中国,台湾,苗栗县,西湖乡',121.003,23.7753,'Xihu',NULL,NULL),(712534,'造桥乡',712500,'造桥',3,'037','361','中国,台湾,苗栗县,造桥乡',120.862,24.6375,'Zaoqiao',NULL,NULL),(712535,'三湾乡',712500,'三湾',3,'037','352','中国,台湾,苗栗县,三湾乡',120.951,24.6511,'Sanwan',NULL,NULL),(712536,'狮潭乡',712500,'狮潭',3,'037','354','中国,台湾,苗栗县,狮潭乡',120.918,24.54,'Shitan',NULL,NULL),(712537,'泰安乡',712500,'泰安',3,'037','365','中国,台湾,苗栗县,泰安乡',120.904,24.4426,'Tai\'an',NULL,NULL),(712700,'彰化县',710000,'彰化',2,'04','5','中国,台湾,彰化县',120.416,24,'Changhua',NULL,NULL),(712701,'彰化市',712700,'彰化市',3,'04','500','中国,台湾,彰化县,彰化市',120.542,24.0809,'Zhanghuashi',NULL,NULL),(712721,'鹿港镇',712700,'鹿港',3,'04','505','中国,台湾,彰化县,鹿港镇',120.435,24.0569,'Lugang',NULL,NULL),(712722,'和美镇',712700,'和美',3,'04','508','中国,台湾,彰化县,和美镇',120.5,24.1109,'Hemei',NULL,NULL),(712723,'线西乡',712700,'线西',3,'04','507','中国,台湾,彰化县,线西乡',120.466,24.1287,'Xianxi',NULL,NULL),(712724,'伸港乡',712700,'伸港',3,'04','509','中国,台湾,彰化县,伸港乡',120.484,24.1461,'Shengang',NULL,NULL),(712725,'福兴乡',712700,'福兴',3,'04','506','中国,台湾,彰化县,福兴乡',120.444,24.0479,'Fuxing',NULL,NULL),(712726,'秀水乡',712700,'秀水',3,'04','504','中国,台湾,彰化县,秀水乡',120.503,24.0353,'Xiushui',NULL,NULL),(712727,'花坛乡',712700,'花坛',3,'04','503','中国,台湾,彰化县,花坛乡',120.538,24.0294,'Huatan',NULL,NULL),(712728,'芬园乡',712700,'芬园',3,'04','502','中国,台湾,彰化县,芬园乡',120.629,24.0137,'Fenyuan',NULL,NULL),(712729,'员林镇',712700,'员林',3,'04','510','中国,台湾,彰化县,员林镇',120.575,23.959,'Yuanlin',NULL,NULL),(712730,'溪湖镇',712700,'溪湖',3,'04','514','中国,台湾,彰化县,溪湖镇',120.479,23.9623,'Xihu',NULL,NULL),(712731,'田中镇',712700,'田中',3,'04','520','中国,台湾,彰化县,田中镇',120.581,23.8617,'Tianzhong',NULL,NULL),(712732,'大村乡',712700,'大村',3,'04','515','中国,台湾,彰化县,大村乡',120.541,23.9937,'Dacun',NULL,NULL),(712733,'埔盐乡',712700,'埔盐',3,'04','516','中国,台湾,彰化县,埔盐乡',120.464,24.0003,'Puyan',NULL,NULL),(712734,'埔心乡',712700,'埔心',3,'04','513','中国,台湾,彰化县,埔心乡',120.544,23.953,'Puxin',NULL,NULL),(712735,'永靖乡',712700,'永靖',3,'04','512','中国,台湾,彰化县,永靖乡',120.548,23.9247,'Yongjing',NULL,NULL),(712736,'社头乡',712700,'社头',3,'04','511','中国,台湾,彰化县,社头乡',120.583,23.8967,'Shetou',NULL,NULL),(712737,'二水乡',712700,'二水',3,'04','530','中国,台湾,彰化县,二水乡',120.619,23.807,'Ershui',NULL,NULL),(712738,'北斗镇',712700,'北斗',3,'04','521','中国,台湾,彰化县,北斗镇',120.52,23.8709,'Beidou',NULL,NULL),(712739,'二林镇',712700,'二林',3,'04','526','中国,台湾,彰化县,二林镇',120.374,23.8998,'Erlin',NULL,NULL),(712740,'田尾乡',712700,'田尾',3,'04','522','中国,台湾,彰化县,田尾乡',120.525,23.8907,'Tianwei',NULL,NULL),(712741,'埤头乡',712700,'埤头',3,'04','523','中国,台湾,彰化县,埤头乡',120.463,23.8913,'Pitou',NULL,NULL),(712742,'芳苑乡',712700,'芳苑',3,'04','528','中国,台湾,彰化县,芳苑乡',120.32,23.9242,'Fangyuan',NULL,NULL),(712743,'大城乡',712700,'大城',3,'04','527','中国,台湾,彰化县,大城乡',120.321,23.8524,'Dacheng',NULL,NULL),(712744,'竹塘乡',712700,'竹塘',3,'04','525','中国,台湾,彰化县,竹塘乡',120.427,23.8601,'Zhutang',NULL,NULL),(712745,'溪州乡',712700,'溪州',3,'04','524','中国,台湾,彰化县,溪州乡',120.499,23.8512,'Xizhou',NULL,NULL),(712800,'南投县',710000,'南投',2,'049','5','中国,台湾,南投县',120.83,23.83,'Nantou',NULL,NULL),(712801,'南投市',712800,'南投市',3,'049','540','中国,台湾,南投县,南投市',120.684,23.91,'Nantoushi',NULL,NULL),(712821,'埔里镇',712800,'埔里',3,'049','545','中国,台湾,南投县,埔里镇',120.965,23.9648,'Puli',NULL,NULL),(712822,'草屯镇',712800,'草屯',3,'049','542','中国,台湾,南投县,草屯镇',120.68,23.9739,'Caotun',NULL,NULL),(712823,'竹山镇',712800,'竹山',3,'049','557','中国,台湾,南投县,竹山镇',120.672,23.7577,'Zhushan',NULL,NULL),(712824,'集集镇',712800,'集集',3,'049','552','中国,台湾,南投县,集集镇',120.784,23.829,'Jiji',NULL,NULL),(712825,'名间乡',712800,'名间',3,'049','551','中国,台湾,南投县,名间乡',120.703,23.8384,'Mingjian',NULL,NULL),(712826,'鹿谷乡',712800,'鹿谷',3,'049','558','中国,台湾,南投县,鹿谷乡',120.753,23.7445,'Lugu',NULL,NULL),(712827,'中寮乡',712800,'中寮',3,'049','541','中国,台湾,南投县,中寮乡',120.767,23.8789,'Zhongliao',NULL,NULL),(712828,'鱼池乡',712800,'鱼池',3,'049','555','中国,台湾,南投县,鱼池乡',120.936,23.8964,'Yuchi',NULL,NULL),(712829,'国姓乡',712800,'国姓',3,'049','544','中国,台湾,南投县,国姓乡',120.859,24.0423,'Guoxing',NULL,NULL),(712830,'水里乡',712800,'水里',3,'049','553','中国,台湾,南投县,水里乡',120.856,23.8121,'Shuili',NULL,NULL),(712831,'信义乡',712800,'信义',3,'049','556','中国,台湾,南投县,信义乡',120.855,23.6999,'Xinyi',NULL,NULL),(712832,'仁爱乡',712800,'仁爱',3,'049','546','中国,台湾,南投县,仁爱乡',121.134,24.0244,'Renai',NULL,NULL),(712900,'云林县',710000,'云林',2,'05','6','中国,台湾,云林县',120.25,23.75,'Yunlin',NULL,NULL),(712901,'斗六市',712900,'斗六',3,'05','640','中国,台湾,云林县,斗六市',120.527,23.6977,'Douliu',NULL,NULL),(712921,'斗南镇',712900,'斗南',3,'05','630','中国,台湾,云林县,斗南镇',120.479,23.6797,'Dounan',NULL,NULL),(712922,'虎尾镇',712900,'虎尾',3,'05','632','中国,台湾,云林县,虎尾镇',120.445,23.7082,'Huwei',NULL,NULL),(712923,'西螺镇',712900,'西螺',3,'05','648','中国,台湾,云林县,西螺镇',120.466,23.798,'Xiluo',NULL,NULL),(712924,'土库镇',712900,'土库',3,'05','633','中国,台湾,云林县,土库镇',120.393,23.6778,'Tuku',NULL,NULL),(712925,'北港镇',712900,'北港',3,'05','651','中国,台湾,云林县,北港镇',120.302,23.5755,'Beigang',NULL,NULL),(712926,'古坑乡',712900,'古坑',3,'05','646','中国,台湾,云林县,古坑乡',120.562,23.6426,'Gukeng',NULL,NULL),(712927,'大埤乡',712900,'大埤',3,'05','631','中国,台湾,云林县,大埤乡',120.431,23.6459,'Dapi',NULL,NULL),(712928,'莿桐乡',712900,'莿桐',3,'05','647','中国,台湾,云林县,莿桐乡',120.502,23.7608,'Citong',NULL,NULL),(712929,'林内乡',712900,'林内',3,'05','643','中国,台湾,云林县,林内乡',120.611,23.7587,'Linna',NULL,NULL),(712930,'二仑乡',712900,'二仑',3,'05','649','中国,台湾,云林县,二仑乡',120.415,23.7713,'Erlun',NULL,NULL),(712931,'仑背乡',712900,'仑背',3,'05','637','中国,台湾,云林县,仑背乡',120.354,23.7588,'Lunbei',NULL,NULL),(712932,'麦寮乡',712900,'麦寮',3,'05','638','中国,台湾,云林县,麦寮乡',120.252,23.7538,'Mailiao',NULL,NULL),(712933,'东势乡',712900,'东势',3,'05','635','中国,台湾,云林县,东势乡',120.253,23.6747,'Dongshi',NULL,NULL),(712934,'褒忠乡',712900,'褒忠',3,'05','634','中国,台湾,云林县,褒忠乡',120.31,23.6942,'Baozhong',NULL,NULL),(712935,'台西乡',712900,'台西',3,'05','636','中国,台湾,云林县,台西乡',120.196,23.7028,'Taixi',NULL,NULL),(712936,'元长乡',712900,'元长',3,'05','655','中国,台湾,云林县,元长乡',120.315,23.6495,'Yuanchang',NULL,NULL),(712937,'四湖乡',712900,'四湖',3,'05','654','中国,台湾,云林县,四湖乡',120.226,23.6377,'Sihu',NULL,NULL),(712938,'口湖乡',712900,'口湖',3,'05','653','中国,台湾,云林县,口湖乡',120.185,23.5824,'Kouhu',NULL,NULL),(712939,'水林乡',712900,'水林',3,'05','652','中国,台湾,云林县,水林乡',120.246,23.5726,'Shuilin',NULL,NULL),(713000,'嘉义县',710000,'嘉义',2,'05','6','中国,台湾,嘉义县',120.3,23.5,'Chiayi',NULL,NULL),(713001,'太保市',713000,'太保',3,'05','612','中国,台湾,嘉义县,太保市',120.333,23.4596,'Taibao',NULL,NULL),(713002,'朴子市',713000,'朴子',3,'05','613','中国,台湾,嘉义县,朴子市',120.247,23.465,'Puzi',NULL,NULL),(713023,'布袋镇',713000,'布袋',3,'05','625','中国,台湾,嘉义县,布袋镇',120.167,23.378,'Budai',NULL,NULL),(713024,'大林镇',713000,'大林',3,'05','622','中国,台湾,嘉义县,大林镇',120.471,23.6038,'Dalin',NULL,NULL),(713025,'民雄乡',713000,'民雄',3,'05','621','中国,台湾,嘉义县,民雄乡',120.429,23.5515,'Minxiong',NULL,NULL),(713026,'溪口乡',713000,'溪口',3,'05','623','中国,台湾,嘉义县,溪口乡',120.394,23.6022,'Xikou',NULL,NULL),(713027,'新港乡',713000,'新港',3,'05','616','中国,台湾,嘉义县,新港乡',120.348,23.5518,'Xingang',NULL,NULL),(713028,'六脚乡',713000,'六脚',3,'05','615','中国,台湾,嘉义县,六脚乡',120.291,23.4939,'Liujiao',NULL,NULL),(713029,'东石乡',713000,'东石',3,'05','614','中国,台湾,嘉义县,东石乡',120.154,23.4592,'Dongshi',NULL,NULL),(713030,'义竹乡',713000,'义竹',3,'05','624','中国,台湾,嘉义县,义竹乡',120.243,23.3363,'Yizhu',NULL,NULL),(713031,'鹿草乡',713000,'鹿草',3,'05','611','中国,台湾,嘉义县,鹿草乡',120.308,23.4108,'Lucao',NULL,NULL),(713032,'水上乡',713000,'水上',3,'05','608','中国,台湾,嘉义县,水上乡',120.398,23.4281,'Shuishang',NULL,NULL),(713033,'中埔乡',713000,'中埔',3,'05','606','中国,台湾,嘉义县,中埔乡',120.523,23.4251,'Zhongpu',NULL,NULL),(713034,'竹崎乡',713000,'竹崎',3,'05','604','中国,台湾,嘉义县,竹崎乡',120.551,23.5232,'Zhuqi',NULL,NULL),(713035,'梅山乡',713000,'梅山',3,'05','603','中国,台湾,嘉义县,梅山乡',120.557,23.5849,'Meishan',NULL,NULL),(713036,'番路乡',713000,'番路',3,'05','602','中国,台湾,嘉义县,番路乡',120.555,23.4652,'Fanlu',NULL,NULL),(713037,'大埔乡',713000,'大埔',3,'05','607','中国,台湾,嘉义县,大埔乡',120.594,23.2967,'Dapu',NULL,NULL),(713038,'阿里山乡',713000,'阿里山',3,'05','605','中国,台湾,嘉义县,阿里山乡',120.733,23.468,'Alishan',NULL,NULL),(713300,'屏东县',710000,'屏东',2,'08','9','中国,台湾,屏东县',120.488,22.6828,'Pingtung',NULL,NULL),(713301,'屏东市',713300,'屏东',3,'08','900','中国,台湾,屏东县,屏东市',120.488,22.6697,'Pingdong',NULL,NULL),(713321,'潮州镇',713300,'潮州',3,'08','920','中国,台湾,屏东县,潮州镇',120.543,22.5505,'Chaozhou',NULL,NULL),(713322,'东港镇',713300,'东港',3,'08','928','中国,台湾,屏东县,东港镇',120.454,22.4666,'Donggang',NULL,NULL),(713323,'恒春镇',713300,'恒春',3,'08','946','中国,台湾,屏东县,恒春镇',120.745,22.0024,'Hengchun',NULL,NULL),(713324,'万丹乡',713300,'万丹',3,'08','913','中国,台湾,屏东县,万丹乡',120.485,22.5898,'Wandan',NULL,NULL),(713325,'长治乡',713300,'长治',3,'08','908','中国,台湾,屏东县,长治乡',120.528,22.6771,'Changzhi',NULL,NULL),(713326,'麟洛乡',713300,'麟洛',3,'08','909','中国,台湾,屏东县,麟洛乡',120.527,22.6506,'Linluo',NULL,NULL),(713327,'九如乡',713300,'九如',3,'08','904','中国,台湾,屏东县,九如乡',120.49,22.7398,'Jiuru',NULL,NULL),(713328,'里港乡',713300,'里港',3,'08','905','中国,台湾,屏东县,里港乡',120.494,22.7792,'Ligang',NULL,NULL),(713329,'盐埔乡',713300,'盐埔',3,'08','907','中国,台湾,屏东县,盐埔乡',120.573,22.7548,'Yanpu',NULL,NULL),(713330,'高树乡',713300,'高树',3,'08','906','中国,台湾,屏东县,高树乡',120.6,22.8268,'Gaoshu',NULL,NULL),(713331,'万峦乡',713300,'万峦',3,'08','923','中国,台湾,屏东县,万峦乡',120.566,22.572,'Wanluan',NULL,NULL),(713332,'内埔乡',713300,'内埔',3,'08','912','中国,台湾,屏东县,内埔乡',120.567,22.612,'Napu',NULL,NULL),(713333,'竹田乡',713300,'竹田',3,'08','911','中国,台湾,屏东县,竹田乡',120.544,22.5847,'Zhutian',NULL,NULL),(713334,'新埤乡',713300,'新埤',3,'08','925','中国,台湾,屏东县,新埤乡',120.55,22.47,'Xinpi',NULL,NULL),(713335,'枋寮乡',713300,'枋寮',3,'08','940','中国,台湾,屏东县,枋寮乡',120.593,22.3656,'Fangliao',NULL,NULL),(713336,'新园乡',713300,'新园',3,'08','932','中国,台湾,屏东县,新园乡',120.462,22.544,'Xinyuan',NULL,NULL),(713337,'崁顶乡',713300,'崁顶',3,'08','924','中国,台湾,屏东县,崁顶乡',120.515,22.5148,'Kanding',NULL,NULL),(713338,'林边乡',713300,'林边',3,'08','927','中国,台湾,屏东县,林边乡',120.515,22.434,'Linbian',NULL,NULL),(713339,'南州乡',713300,'南州',3,'08','926','中国,台湾,屏东县,南州乡',120.51,22.4902,'Nanzhou',NULL,NULL),(713340,'佳冬乡',713300,'佳冬',3,'08','931','中国,台湾,屏东县,佳冬乡',120.552,22.4177,'Jiadong',NULL,NULL),(713341,'琉球乡',713300,'琉球',3,'08','929','中国,台湾,屏东县,琉球乡',120.369,22.3424,'Liuqiu',NULL,NULL),(713342,'车城乡',713300,'车城',3,'08','944','中国,台湾,屏东县,车城乡',120.711,22.0721,'Checheng',NULL,NULL),(713343,'满州乡',713300,'满州',3,'08','947','中国,台湾,屏东县,满州乡',120.839,22.0209,'Manzhou',NULL,NULL),(713344,'枋山乡',713300,'枋山',3,'08','941','中国,台湾,屏东县,枋山乡',120.656,22.2603,'Fangshan',NULL,NULL),(713345,'三地门乡',713300,'三地门',3,'08','901','中国,台湾,屏东县,三地门乡',120.654,22.7139,'Sandimen',NULL,NULL),(713346,'雾台乡',713300,'雾台',3,'08','902','中国,台湾,屏东县,雾台乡',120.732,22.7449,'Wutai',NULL,NULL),(713347,'玛家乡',713300,'玛家',3,'08','903','中国,台湾,屏东县,玛家乡',120.644,22.7067,'Majia',NULL,NULL),(713348,'泰武乡',713300,'泰武',3,'08','921','中国,台湾,屏东县,泰武乡',120.633,22.5918,'Taiwu',NULL,NULL),(713349,'来义乡',713300,'来义',3,'08','922','中国,台湾,屏东县,来义乡',120.634,22.5259,'Laiyi',NULL,NULL),(713350,'春日乡',713300,'春日',3,'08','942','中国,台湾,屏东县,春日乡',120.629,22.3707,'Chunri',NULL,NULL),(713351,'狮子乡',713300,'狮子',3,'08','943','中国,台湾,屏东县,狮子乡',120.705,22.2019,'Shizi',NULL,NULL),(713352,'牡丹乡',713300,'牡丹',3,'08','945','中国,台湾,屏东县,牡丹乡',120.77,22.1257,'Mudan',NULL,NULL),(713400,'台东县',710000,'台东',2,'089','9','中国,台湾,台东县',120.916,23,'Taitung',NULL,NULL),(713401,'台东市',713400,'台东',3,'089','950','中国,台湾,台东县,台东市',121.146,22.756,'Taidong',NULL,NULL),(713421,'成功镇',713400,'成功',3,'089','961','中国,台湾,台东县,成功镇',121.38,23.1002,'Chenggong',NULL,NULL),(713422,'关山镇',713400,'关山',3,'089','956','中国,台湾,台东县,关山镇',121.163,23.0474,'Guanshan',NULL,NULL),(713423,'卑南乡',713400,'卑南',3,'089','954','中国,台湾,台东县,卑南乡',121.084,22.786,'Beinan',NULL,NULL),(713424,'鹿野乡',713400,'鹿野',3,'089','955','中国,台湾,台东县,鹿野乡',121.136,22.914,'Luye',NULL,NULL),(713425,'池上乡',713400,'池上',3,'089','958','中国,台湾,台东县,池上乡',121.215,23.1224,'Chishang',NULL,NULL),(713426,'东河乡',713400,'东河',3,'089','959','中国,台湾,台东县,东河乡',121.3,22.9699,'Donghe',NULL,NULL),(713427,'长滨乡',713400,'长滨',3,'089','962','中国,台湾,台东县,长滨乡',121.452,23.315,'Changbin',NULL,NULL),(713428,'太麻里乡',713400,'太麻里',3,'089','963','中国,台湾,台东县,太麻里乡',121.007,22.6154,'Taimali',NULL,NULL),(713429,'大武乡',713400,'大武',3,'089','965','中国,台湾,台东县,大武乡',120.89,22.3399,'Dawu',NULL,NULL),(713430,'绿岛乡',713400,'绿岛',3,'089','951','中国,台湾,台东县,绿岛乡',121.493,22.6617,'Lvdao',NULL,NULL),(713431,'海端乡',713400,'海端',3,'089','957','中国,台湾,台东县,海端乡',121.172,23.1011,'Haiduan',NULL,NULL),(713432,'延平乡',713400,'延平',3,'089','953','中国,台湾,台东县,延平乡',121.084,22.9024,'Yanping',NULL,NULL),(713433,'金峰乡',713400,'金峰',3,'089','964','中国,台湾,台东县,金峰乡',120.971,22.5955,'Jinfeng',NULL,NULL),(713434,'达仁乡',713400,'达仁',3,'089','966','中国,台湾,台东县,达仁乡',120.884,22.2949,'Daren',NULL,NULL),(713435,'兰屿乡',713400,'兰屿',3,'089','952','中国,台湾,台东县,兰屿乡',121.532,22.0567,'Lanyu',NULL,NULL),(713500,'花莲县',710000,'花莲',2,'03','9','中国,台湾,花莲县',121.3,23.83,'Hualien',NULL,NULL),(713501,'花莲市',713500,'花莲',3,'03','970','中国,台湾,花莲县,花莲市',121.607,23.9821,'Hualian',NULL,NULL),(713521,'凤林镇',713500,'凤林',3,'03','975','中国,台湾,花莲县,凤林镇',121.452,23.7446,'Fenglin',NULL,NULL),(713522,'玉里镇',713500,'玉里',3,'03','981','中国,台湾,花莲县,玉里镇',121.316,23.3365,'Yuli',NULL,NULL),(713523,'新城乡',713500,'新城',3,'03','971','中国,台湾,花莲县,新城乡',121.641,24.1281,'Xincheng',NULL,NULL),(713524,'吉安乡',713500,'吉安',3,'03','973','中国,台湾,花莲县,吉安乡',121.568,23.9616,'Ji\'an',NULL,NULL),(713525,'寿丰乡',713500,'寿丰',3,'03','974','中国,台湾,花莲县,寿丰乡',121.509,23.8707,'Shoufeng',NULL,NULL),(713526,'光复乡',713500,'光复',3,'03','976','中国,台湾,花莲县,光复乡',121.423,23.6691,'Guangfu',NULL,NULL),(713527,'丰滨乡',713500,'丰滨',3,'03','977','中国,台湾,花莲县,丰滨乡',121.519,23.5971,'Fengbin',NULL,NULL),(713528,'瑞穗乡',713500,'瑞穗',3,'03','978','中国,台湾,花莲县,瑞穗乡',121.376,23.4968,'Ruisui',NULL,NULL),(713529,'富里乡',713500,'富里',3,'03','983','中国,台湾,花莲县,富里乡',121.25,23.18,'Fuli',NULL,NULL),(713530,'秀林乡',713500,'秀林',3,'03','972','中国,台湾,花莲县,秀林乡',121.62,24.1166,'Xiulin',NULL,NULL),(713531,'万荣乡',713500,'万荣',3,'03','979','中国,台湾,花莲县,万荣乡',121.407,23.7153,'Wanrong',NULL,NULL),(713532,'卓溪乡',713500,'卓溪',3,'03','982','中国,台湾,花莲县,卓溪乡',121.303,23.3464,'Zhuoxi',NULL,NULL),(713600,'澎湖县',710000,'澎湖',2,'06','8','中国,台湾,澎湖县',119.566,23.5697,'Penghu',NULL,NULL),(713601,'马公市',713600,'马公',3,'06','880','中国,台湾,澎湖县,马公市',119.566,23.5658,'Magong',NULL,NULL),(713621,'湖西乡',713600,'湖西',3,'06','885','中国,台湾,澎湖县,湖西乡',119.66,23.5834,'Huxi',NULL,NULL),(713622,'白沙乡',713600,'白沙',3,'06','884','中国,台湾,澎湖县,白沙乡',119.598,23.6661,'Baisha',NULL,NULL),(713623,'西屿乡',713600,'西屿',3,'06','881','中国,台湾,澎湖县,西屿乡',119.507,23.6008,'Xiyu',NULL,NULL),(713624,'望安乡',713600,'望安',3,'06','882','中国,台湾,澎湖县,望安乡',119.501,23.3575,'Wang\'an',NULL,NULL),(713625,'七美乡',713600,'七美',3,'06','883','中国,台湾,澎湖县,七美乡',119.424,23.206,'Qimei',NULL,NULL),(713700,'金门县',710000,'金门',2,'082','8','中国,台湾,金门县',118.317,24.4327,'Jinmen',NULL,NULL),(713701,'金城镇',713700,'金城',3,'082','893','中国,台湾,金门县,金城镇',118.317,24.4167,'Jincheng',NULL,NULL),(713702,'金湖镇',713700,'金湖',3,'082','891','中国,台湾,金门县,金湖镇',118.42,24.4386,'Jinhu',NULL,NULL),(713703,'金沙镇',713700,'金沙',3,'082','890','中国,台湾,金门县,金沙镇',118.428,24.4811,'Jinsha',NULL,NULL),(713704,'金宁乡',713700,'金宁',3,'082','892','中国,台湾,金门县,金宁乡',118.335,24.4567,'Jinning',NULL,NULL),(713705,'烈屿乡',713700,'烈屿',3,'082','894','中国,台湾,金门县,烈屿乡',118.247,24.4331,'Lieyu',NULL,NULL),(713706,'乌丘乡',713700,'乌丘',3,'082','896','中国,台湾,金门县,乌丘乡',118.32,24.435,'Wuqiu',NULL,NULL),(713800,'连江县',710000,'连江',2,'0836','2','中国,台湾,连江县',119.54,26.1974,'Lienchiang',NULL,NULL),(713801,'南竿乡',713800,'南竿',3,'0836','209','中国,台湾,连江县,南竿乡',119.944,26.144,'Nangan',NULL,NULL),(713802,'北竿乡',713800,'北竿',3,'0836','210','中国,台湾,连江县,北竿乡',120.001,26.222,'Beigan',NULL,NULL),(713803,'莒光乡',713800,'莒光',3,'0836','211','中国,台湾,连江县,莒光乡',119.94,25.9763,'Juguang',NULL,NULL),(713804,'东引乡',713800,'东引',3,'0836','212','中国,台湾,连江县,东引乡',120.494,26.3662,'Dongyin',NULL,NULL),(810000,'香港特别行政区',100000,'香港',1,'','','中国,香港特别行政区',114.173,22.32,'Hong Kong',NULL,NULL),(810100,'香港岛',810000,'香港岛',2,'00852','999077','中国,香港特别行政区,香港岛',114.177,22.2664,'Hong Kong Island',NULL,NULL),(810101,'中西区',810100,'中西区',3,'00852','999077','中国,香港特别行政区,香港岛,中西区',114.154,22.282,'Central and Western District',NULL,NULL),(810102,'湾仔区',810100,'湾仔区',3,'00852','999077','中国,香港特别行政区,香港岛,湾仔区',114.183,22.2764,'Wan Chai District',NULL,NULL),(810103,'东区',810100,'东区',3,'00852','999077','中国,香港特别行政区,香港岛,东区',114.256,22.2628,'Eastern District',NULL,NULL),(810104,'南区',810100,'南区',3,'00852','999077','中国,香港特别行政区,香港岛,南区',114.174,22.2468,'Southern District',NULL,NULL),(810200,'九龙',810000,'九龙',2,'00852','999077','中国,香港特别行政区,九龙',114.175,22.3271,'Kowloon',NULL,NULL),(810201,'油尖旺区',810200,'油尖旺',3,'00852','999077','中国,香港特别行政区,九龙,油尖旺区',114.173,22.3117,'Yau Tsim Mong',NULL,NULL),(810202,'深水埗区',810200,'深水埗',3,'00852','999077','中国,香港特别行政区,九龙,深水埗区',114.167,22.3282,'Sham Shui Po',NULL,NULL),(810203,'九龙城区',810200,'九龙城',3,'00852','999077','中国,香港特别行政区,九龙,九龙城区',114.195,22.3267,'Jiulongcheng',NULL,NULL),(810204,'黄大仙区',810200,'黄大仙',3,'00852','999077','中国,香港特别行政区,九龙,黄大仙区',114.199,22.3363,'Wong Tai Sin',NULL,NULL),(810205,'观塘区',810200,'观塘',3,'00852','999077','中国,香港特别行政区,九龙,观塘区',114.231,22.3094,'Kwun Tong',NULL,NULL),(810300,'新界',810000,'新界',2,'00852','999077','中国,香港特别行政区,新界',114.202,22.3418,'New Territories',NULL,NULL),(810301,'荃湾区',810300,'荃湾',3,'00852','999077','中国,香港特别行政区,新界,荃湾区',114.123,22.371,'Tsuen Wan',NULL,NULL),(810302,'屯门区',810300,'屯门',3,'00852','999077','中国,香港特别行政区,新界,屯门区',113.977,22.391,'Tuen Mun',NULL,NULL),(810303,'元朗区',810300,'元朗',3,'00852','999077','中国,香港特别行政区,新界,元朗区',114.04,22.4433,'Yuen Long',NULL,NULL),(810304,'北区',810300,'北区',3,'00852','999077','中国,香港特别行政区,新界,北区',114.149,22.4941,'North District',NULL,NULL),(810305,'大埔区',810300,'大埔',3,'00852','999077','中国,香港特别行政区,新界,大埔区',114.172,22.4457,'Tai Po',NULL,NULL),(810306,'西贡区',810300,'西贡',3,'00852','999077','中国,香港特别行政区,新界,西贡区',114.279,22.3794,'Sai Kung',NULL,NULL),(810307,'沙田区',810300,'沙田',3,'00852','999077','中国,香港特别行政区,新界,沙田区',114.192,22.3793,'Sha Tin',NULL,NULL),(810308,'葵青区',810300,'葵青',3,'00852','999077','中国,香港特别行政区,新界,葵青区',114.139,22.3639,'Kwai Tsing',NULL,NULL),(810309,'离岛区',810300,'离岛',3,'00852','999077','中国,香港特别行政区,新界,离岛区',113.946,22.2815,'Outlying Islands',NULL,NULL),(820000,'澳门特别行政区',100000,'澳门',1,'','','中国,澳门特别行政区',113.549,22.199,'Macau',NULL,NULL),(820100,'澳门半岛',820000,'澳门半岛',2,'00853','999078','中国,澳门特别行政区,澳门半岛',113.549,22.1988,'MacauPeninsula',NULL,NULL),(820101,'花地玛堂区',820100,'花地玛堂区',3,'00853','999078','中国,澳门特别行政区,澳门半岛,花地玛堂区',113.552,22.2081,'Nossa Senhora de Fatima',NULL,NULL),(820102,'圣安多尼堂区',820100,'圣安多尼堂区',3,'00853','999078','中国,澳门特别行政区,澳门半岛,圣安多尼堂区',113.564,22.1238,'Santo Antonio',NULL,NULL),(820103,'大堂区',820100,'大堂',3,'00853','999078','中国,澳门特别行政区,澳门半岛,大堂区',113.553,22.1884,'Sé',NULL,NULL),(820104,'望德堂区',820100,'望德堂区',3,'00853','999078','中国,澳门特别行政区,澳门半岛,望德堂区',113.551,22.1941,'Sao Lazaro',NULL,NULL),(820105,'风顺堂区',820100,'风顺堂区',3,'00853','999078','中国,澳门特别行政区,澳门半岛,风顺堂区',113.542,22.1874,'Sao Lourenco',NULL,NULL),(820200,'氹仔岛',820000,'氹仔岛',2,'00853','999078','中国,澳门特别行政区,氹仔岛',113.578,22.1568,'Taipa',NULL,NULL),(820201,'嘉模堂区',820200,'嘉模堂区',3,'00853','999078','中国,澳门特别行政区,氹仔岛,嘉模堂区',113.565,22.149,'Our Lady Of Carmel\'s Parish',NULL,NULL),(820300,'路环岛',820000,'路环岛',2,'00853','999078','中国,澳门特别行政区,路环岛',113.565,22.1162,'Coloane',NULL,NULL),(820301,'圣方济各堂区',820300,'圣方济各堂区',3,'00853','999078','中国,澳门特别行政区,路环岛,圣方济各堂区',113.56,22.1235,'St Francis Xavier\'s Parish',NULL,NULL),(900000,'钓鱼岛',100000,'钓鱼岛',1,'','','中国,钓鱼岛',123.478,25.7424,'DiaoyuDao',NULL,NULL); +/*!40000 ALTER TABLE `sys_region` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_resource` +-- + +DROP TABLE IF EXISTS `sys_resource`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_resource` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `app_id_` varchar(64) DEFAULT NULL COMMENT '应用ID', + `code_` varchar(64) DEFAULT NULL COMMENT '权限编码', + `name_` varchar(64) DEFAULT NULL COMMENT '名字', + `url_` varchar(120) DEFAULT NULL COMMENT '路由地址', + `enable_` tinyint DEFAULT NULL COMMENT '是否启用', + `opened_` tinyint DEFAULT NULL COMMENT '是否默认打开', + `is_api_` tinyint DEFAULT NULL COMMENT '是否API资源', + `icon_` varchar(50) DEFAULT NULL COMMENT '图标', + `type_` varchar(50) DEFAULT NULL COMMENT 'menu,button,API', + `sn_` int DEFAULT NULL COMMENT '排序', + `parent_id_` varchar(50) DEFAULT NULL COMMENT '父节点ID', + `path_` varchar(512) DEFAULT NULL COMMENT '路径', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='系统权限资源定义'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_resource` +-- + +LOCK TABLES `sys_resource` WRITE; +/*!40000 ALTER TABLE `sys_resource` DISABLE KEYS */; +INSERT INTO `sys_resource` VALUES ('1602918114232172583','1602918114232172552','office','办公','',1,0,NULL,'HomeFilled','menu',1,'0','0','2022-12-14 14:47:09','112','','2023-06-27 17:26:17','系统管理员','1602918114232172634'),('1602918114232172641','1602918114232172552','Officebpm','事项办理','',1,1,NULL,'Pointer','menu',2,'1602918114232172583','0.1602918114232172583','2022-12-14 14:47:09','11112222','','2023-06-28 10:00:01','系统管理员','1602918114232172634'),('1602918114232172654','1602918114232172552','BpmMyTaskTodoList','待办事项','/bpm/my/todoList',1,0,NULL,'','menu',1,'1602918114232172641','0.1602918114232172583.1602918114232172641','2022-12-14 14:47:09','11112222','','2023-03-27 14:33:30','管理员','1'),('1602918114232172667','1602918114232172552','BpmMyApproveList','办理历史','/bpm/my/approveList',1,0,NULL,'','menu',3,'1602918114232172641','0.1602918114232172583.1602918114232172641','2022-12-14 14:47:09','11112222','','2023-03-27 14:33:30','管理员','1'),('1602918114232172673','1602918114232172552','shishangshenqing','事项申请','',1,1,NULL,'Promotion','menu',3,'1602918114232172583','0.1602918114232172583','2022-12-14 14:47:09','11112222','','2023-06-28 10:06:19','系统管理员','1602918114232172634'),('1602918114232172682','1602918114232172552','startFlow','发起申请','/bpm/my/definitionList',1,0,NULL,'','menu',1,'1602918114232172673','0.1602918114232172583.1602918114232172673','2022-12-14 14:47:09','11112222','','2023-03-27 14:33:31','管理员','1'),('1602918114232172693','1602918114232172552','BpmMyApplyList','申请历史','/bpm/my/applyList',1,0,NULL,'','menu',2,'1602918114232172673','0.1602918114232172583.1602918114232172673','2022-12-14 14:47:09','11112222','','2023-03-27 14:33:31','管理员','1'),('1602918114232172696','1602918114232172552','BpmMyDraftList','我的草稿','/bpm/my/draftList',1,0,NULL,'','menu',3,'1602918114232172673','0.1602918114232172583.1602918114232172673','2022-12-14 14:47:09','11112222','','2023-03-27 14:33:31','管理员','1'),('1602918114232172753','1602918114232172552','bizFormDesign','表单设计','/biz/bizForm/bizFormDesignList',1,0,NULL,'DocumentAdd','menu',1,'1602918114232172829','0.1602918114232172712.1602918114232172743','2022-12-14 14:47:09','11112222','','2022-12-21 16:34:49','系统管理员','1602918114232172634'),('1602918114232172823','1602918114232172552','CustomDialogList','自定义数据对话框','/biz/custom/customDialogList',1,0,NULL,'MessageBox','menu',5,'1602918114232172829','0.1602918114232172712.1602918114232172809','2022-12-14 14:47:09','11112222','','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1602918114232172829','1602918114232172552','BPM','流程','',1,0,NULL,'HelpFilled','menu',5,'0','0','2022-12-14 14:47:09','11112222','','2022-12-21 16:34:49','系统管理员','1602918114232172634'),('1602918114232172834','1602918114232172552','BpmDefinitionList','流程设计','/bpm/bpm/definitionList',1,0,NULL,'Connection','menu',2,'1602918114232172829','0.1602918114232172829','2022-12-14 14:47:09','11112222','','2023-03-20 10:00:36','管理员','1'),('1602918114232172836','1602918114232172552','bpmDefinition_save','编辑','/bpm/definition/save',1,0,NULL,NULL,'button',1,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172845','1602918114232172552','bpmDefinition_detail','详情','/bpm/definition/get',1,0,NULL,NULL,'button',2,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172847','1602918114232172552','bpmDefinition_bpmDesigner','设计','bpm/definition/getDesign',1,0,NULL,NULL,'button',3,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172850','1602918114232172552','bpmDefinition_definitionVersionList','版本管理','/bpm/definition/listJson',1,0,NULL,NULL,'button',4,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172853','1602918114232172552','bpmDefinition_del','删除','/bpm/definition/remove',1,0,NULL,NULL,'API',5,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172857','1602918114232172552','bpmDefinition_Authorizations','授权','/sys/authorization/getAuthorizations',1,0,NULL,NULL,'button',6,'1602918114232172834','0.1602918114232172829.1602918114232172834','2022-12-14 14:47:09','11112222','','2023-03-14 21:04:23','管理员','1'),('1602918114232172860','1602918114232172552','BpmInstanceList','流程实例管理','/bpm/bpm/instanceList',1,0,NULL,'Menu','menu',3,'1602918114232172829','0.1602918114232172829','2022-12-14 14:47:09','11112222','','2023-06-28 09:13:12','系统管理员','1602918114232172634'),('1602918114232172871','1602918114232172552','BpmTaskList','任务管理','/bpm/bpm/taskList',1,0,NULL,'Menu','menu',4,'1602918114232172829','0.1602918114232172829','2022-12-14 14:47:09','11112222','','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1602918114232172987','1602918114232172552','CMS','CMS','',1,0,NULL,'Notification','menu',2,'0','0','2022-12-14 14:47:09','11112222','','2022-12-21 16:34:49','系统管理员','1602918114232172634'),('1602918114232173009','1602918114232172552','NewsList','新闻管理','/cms/news/newsList',1,0,NULL,'Discount','menu',3,'1602918114232172987','0.1602918114232172987','2022-12-14 14:47:09','11112222','','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1602918114232173033','1602918114232172552','NoticeList','公告管理','/cms/notice/noticeList',1,0,NULL,'ChatLineSquare','menu',4,'1602918114232172987','0.1602918114232172987','2022-12-14 14:47:09','11112222','','2023-02-14 18:08:27','管理员','1'),('1602918114232173036','1602918114232172552','System','系统','',1,0,NULL,'Setting','menu',6,'0','0','2022-12-14 14:47:09','11112222','','2023-06-28 09:14:26','系统管理员','1602918114232172634'),('1602918114232173040','1602918114232172552','PropertyList','系统属性','/sys/properties/propertyList',1,0,NULL,'Operation','menu',3,'1602918114232173036','0.1602918114232173036','2022-12-14 14:47:09','11112222','','2023-06-27 16:09:40','系统管理员','1602918114232172634'),('1602918123749048326','1602918114232172552','ScriptList','常用脚本','/sys/scripts/scriptList',1,0,NULL,'Menu','menu',3,'1602918114232173036',NULL,'2022-12-14 14:47:09','11112222','','2023-06-27 16:09:40','系统管理员','1602918114232172634'),('1602918123753242629','1602918114232172552','DatadicList','字典分类','/sys/dataDic/datadicList',1,0,NULL,'List','menu',1,'1602918114232173036',NULL,'2022-12-14 14:47:09','11112222','','2023-06-27 16:09:40','系统管理员','1602918114232172634'),('1602918123753242640','1602918114232172552','SysResource','系统资源','/sys/sysResourceList',1,0,NULL,'Platform','menu',2,'1602918114232173036','0.1602918114232173036','2022-12-14 14:47:09','11112222','','2023-06-27 16:09:40','系统管理员','1602918114232172634'),('1602918123753242642','1602918114232172552','ORG','组织','',1,0,NULL,'Avatar','menu',3,'0','0','2022-12-14 14:47:09','11112222','','2023-06-28 09:15:57','系统管理员','1602918114232172634'),('1602918123753242644','1602918114232172552','GroupList','组织管理','/org/group/groupList',1,0,NULL,'Menu','menu',2,'1602918123753242642','0.1602918123753242642','2022-12-14 14:47:09','11112222','','2022-12-26 17:28:46','系统管理员11','1'),('1602918123753242650','1602918114232172552','groupList_del','删除组织','/ab-org/group/remove',1,0,NULL,'Menu','API',1,'1602918123753242644','0.1602918123753242642.1602918123753242644','2022-12-14 14:47:09','11112222','','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1602918123753242652','1602918114232172552','roleMananger','角色管理','/org/role/list',1,0,NULL,'User','menu',3,'1602918123753242642','0.1602918123753242642','2022-12-14 14:47:09','11112222','','2022-12-21 16:34:49','系统管理员','1602918114232172634'),('1602918123753242653','1602918114232172552','roleManager_dataPrivilegeGiven','数据权限','',1,0,NULL,'Menu','button',9,'1602918123753242652','0.1602918123753242642.1602918123753242652','2022-12-14 14:47:09','11112222','','2023-04-17 15:24:23','超级管理员','1'),('1602918123753242655','1602918114232172552','roleManager_userList','分配用户','',1,0,NULL,'Menu','button',8,'1602918123753242652','0.1602918123753242642.1602918123753242652','2022-12-14 14:47:09','11112222','','2023-04-17 15:24:23','超级管理员','1'),('1602918123753242657','1602918114232172552','roleManager_resource','角色资源','',1,0,NULL,'Menu','button',7,'1602918123753242652','0.1602918123753242642.1602918123753242652','2022-12-14 14:47:09','11112222','','2023-04-17 15:24:23','超级管理员','1'),('1602918123753242660','1602918114232172552','roleManager_edit','编辑','/ab-org/role/get',1,0,NULL,'Menu','button',6,'1602918123753242652','0.1602918123753242642.1602918123753242652','2022-12-14 14:47:09','11112222','','2023-04-17 15:24:22','超级管理员','1'),('1602918123753242661','1602918114232172552','roleManager_delete','删除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',10,'1602918123753242652','0.1602918123753242642.1602918123753242652','2022-12-14 14:47:09','11112222','','2023-04-17 15:24:23','超级管理员','1'),('1602918123753242663','1602918114232172552','UserList','用户管理','/org/user/userList',1,0,NULL,'UserFilled','menu',1,'1602918123753242642','0.1602918123753242642','2022-12-14 14:47:09','11112222','','2023-04-14 16:54:37','超级管理员','1'),('1602918123753242666','1602918114232172552','userManager_add','添加','',1,0,NULL,'Menu','button',3,'1602918123753242663','0.1602918123753242642.1602918123753242663','2022-12-14 14:47:09','11112222','','2023-04-17 19:46:30','超级管理员','1'),('1602918123753242667','1602918114232172552','userManager_edit','编辑','/ab-org/user/getUserVO',1,0,NULL,'Menu','button',6,'1602918123753242663','0.1602918123753242642.1602918123753242663','2022-12-14 14:47:09','11112222','','2023-04-17 19:46:31','超级管理员','1'),('1602918123753242668','1602918114232172552','userManager_del','删除','/ab-org/user/remove',1,0,NULL,'Menu','button',9,'1602918123753242663','0.1602918123753242642.1602918123753242663','2022-12-14 14:47:09','11112222','','2023-04-17 19:46:31','超级管理员','1'),('1602918123753242670','1602918114232172552','userManager_search','查询','/ab-org/user/listJson',1,0,NULL,'Menu','button',2,'1602918123753242663','0.1602918123753242642.1602918123753242663','2022-12-14 14:47:09','11112222','','2023-04-17 19:46:30','超级管理员','1'),('1602918123753242672','1602918114232172552','userManager_batchDel','批量删除','/ab-org/user/remove',1,0,NULL,'Menu','button',4,'1602918123753242663','0.1602918123753242642.1602918123753242663','2022-12-14 14:47:09','11112222','','2023-04-17 19:46:31','超级管理员','1'),('1605477742950285312','1602918114232172552','LogErrorList','异常日志','/sys/auditLog/logErrorList',1,0,NULL,'Menu','menu',1,'1602918114232173036',NULL,'2022-12-21 16:18:17','1602918114232172634','','2023-06-27 16:09:40','系统管理员','1602918114232172634'),('1640240459969064960','1602918114232172552','index','工作台','/dashboard',1,0,NULL,'DataLine','menu',1,'1602918114232172583',NULL,'2023-03-27 14:32:55','1','1580378480353263616','2023-06-28 10:06:23','系统管理员','1602918114232172634'),('1640260043996524544','1602918114232172552','todoTaskListUrl','待办列表接口','/ab-bpm/bpm/my/todoTaskList',1,0,NULL,'Menu','API',1,'1602918114232172654',NULL,'2023-03-27 15:50:44','1','1580378480353263616','2023-03-27 15:50:44','管理员','1'),('1640260212456550400','1602918114232172552','todoTaskListTypeTreeUrl','代办列表左侧树接口','/ab-bpm/bpm/my/todoTaskListTypeTree',1,0,NULL,'Menu','API',2,'1602918114232172654',NULL,'2023-03-27 15:51:24','1','1580378480353263616','2023-03-27 15:51:24','管理员','1'),('1640263434172755968','1602918114232172552','getTaskDataUrl','获取任务数据接口','/ab-bpm/bpm/task/getTaskData',1,0,NULL,'Menu','API',3,'1602918114232172654',NULL,'2023-03-27 16:04:12','1','1580378480353263616','2023-03-27 16:04:12','管理员','1'),('1640290003196964864','1602918114232172552','approveListUrl','办理历史列表接口','/ab-bpm/bpm/my/approveList',1,0,NULL,'Menu','API',1,'1602918114232172667',NULL,'2023-03-27 17:49:47','1','410054569125740545','2023-03-27 17:53:49','管理员','1'),('1640291284246458368','1602918114232172552','approveListTypeTreeUrl','办理历史左侧树接口','/ab-bpm/bpm/my/approveListTypeTree',1,0,NULL,'Menu','API',2,'1602918114232172667',NULL,'2023-03-27 17:54:52','1','410054569125740545','2023-03-27 17:54:52','管理员','1'),('1640292882754433024','1602918114232172552','definitionListUrl','获取我可发起的流程列表','/ab-bpm/bpm/my/definitionList',1,0,NULL,'Menu','API',1,'1602918114232172682',NULL,'2023-03-27 18:01:13','1','410054569125740545','2023-03-27 18:01:13','管理员','1'),('1640293248741011456','1602918114232172552','getStartFlowDataUrl','获取流程启动所需数据','/ab-bpm/bpm/instance/getStartFlowData',1,0,NULL,'Menu','API',2,'1602918114232172682',NULL,'2023-03-27 18:02:40','1','410054569125740545','2023-03-27 18:02:40','管理员','1'),('1640293866843009024','1602918114232172552','applyTaskListUrl','申请历史列表接口','/ab-bpm/bpm/my/applyTaskList',1,0,NULL,'Menu','API',1,'1602918114232172693',NULL,'2023-03-27 18:05:08','1','410054569125740545','2023-03-27 18:05:08','管理员','1'),('1640294646572511232','1602918114232172552','applyTaskListTypeTreeUrl','申请历史左侧树接口','/ab-bpm/bpm/my/applyTaskListTypeTree',1,0,NULL,'Menu','API',2,'1602918114232172693',NULL,'2023-03-27 18:08:14','1','410054569125740545','2023-03-27 18:08:14','管理员','1'),('1640294901040934912','1602918114232172552','draftListUrl','我的草稿列表接口','/ab-bpm/bpm/my/draftList',1,0,NULL,'Menu','API',1,'1602918114232172696',NULL,'2023-03-27 18:09:14','1','410054569125740545','2023-03-27 18:09:14','管理员','1'),('1640295199453081600','1602918114232172552','getNodeListUrl','根据流程实例id获取节点列表数据','/ab-bpm/bpm/task/discuss/getNodeList',1,0,NULL,'Menu','API',3,'1602918114232172693',NULL,'2023-03-27 18:10:25','1','410054569125740545','2023-03-27 18:15:16','管理员','1'),('1640298849059598336','1602918114232172552','getDraftDataUrl','获取草稿详情接口','/ab-bpm/bpm/instance/getDraftData',1,0,NULL,'Menu','API',2,'1602918114232172696',NULL,'2023-03-27 18:24:56','1','410054569125740545','2023-03-27 18:24:56','管理员','1'),('1640299172503351296','1602918114232172552','myDraftRemoveUrl','删除我的草稿','/ab-bpm/bpm/instance/remove',1,0,NULL,'Menu','API',3,'1602918114232172696',NULL,'2023-03-27 18:26:13','1','410054569125740545','2023-03-27 18:26:13','管理员','1'),('1640305327522836480','1602918114232172552','handleNodeFreeSelectUserUrl','获取自由候选人接口','/ab-bpm/bpm/task/handleNodeFreeSelectUser',1,0,NULL,'Menu','API',3,'1602918114232172682',NULL,'2023-03-27 18:50:40','1','410054569125740545','2023-03-27 18:50:40','管理员','1'),('1640309612490911744','1602918114232172552','instanceDoActionUrl','流程发起接口','/ab-bpm/bpm/instance/doAction',1,0,NULL,'Menu','API',4,'1602918114232172682',NULL,'2023-03-27 19:07:42','1','410054569125740545','2023-03-27 19:07:42','管理员','1'),('1640319690128355328','1602918114232172552','cmsNewsListJson','新闻管理列表接口','/ab-bpm/cms/cmsNews/listJson',1,0,NULL,'Menu','API',1,'1602918114232173009',NULL,'2023-03-27 19:47:44','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640319928515817472','1602918114232172552','cmsNewsgetOne','根据id获取新闻接口','/ab-bpm/cms/cmsNews/getOne',1,0,NULL,'Menu','API',2,'1602918114232173009',NULL,'2023-03-27 19:48:41','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640320121546076160','1602918114232172552','cmsNewsSaveOrUpdate','新闻新增保存接口','/ab-bpm/cms/cmsNews/saveOrUpdate',1,0,NULL,'Menu','API',3,'1602918114232173009',NULL,'2023-03-27 19:49:27','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640320416367898624','1602918114232172552','cmsNewsReleaseNews','新闻发布接口','/ab-bpm/cms/cmsNews/releaseNews',1,0,NULL,'Menu','API',4,'1602918114232173009',NULL,'2023-03-27 19:50:38','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640320512862056448','1602918114232172552','cmsNewsremove','新闻删除接口','/ab-bpm/cms/cmsNews/remove',1,0,NULL,'Menu','API',5,'1602918114232173009',NULL,'2023-03-27 19:51:01','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640320650338758656','1602918114232172552','cmsNewswithdrawNews','新闻下架接口','/ab-bpm/cms/cmsNews/withdrawNews',1,0,NULL,'Menu','API',6,'1602918114232173009',NULL,'2023-03-27 19:51:33','1','410054569125740545','2023-07-04 09:37:55','系统管理员','1602918114232172634'),('1640320959286996992','1602918114232172552','cmsNotifyListJson','公告列表接口','/ab-bpm/cms/cmsNotify/listJson',1,0,NULL,'Menu','API',1,'1602918114232173033',NULL,'2023-03-27 19:52:47','1','410054569125740545','2023-03-27 19:52:47','管理员','1'),('1640321344206663680','1602918114232172552','cmsNotifyGetOne','获取详细公告接口','/ab-bpm/cms/cmsNotify/getOne',1,0,NULL,'Menu','API',2,'1602918114232173033',NULL,'2023-03-27 19:54:19','1','410054569125740545','2023-03-27 19:54:19','管理员','1'),('1640321603293016064','1602918114232172552','cmsNotifywithdrawNotify','下架公告接口','/ab-bpm/cms/cmsNotify/withdrawNotify',1,0,NULL,'Menu','API',3,'1602918114232173033',NULL,'2023-03-27 19:55:21','1','410054569125740545','2023-03-27 19:55:21','管理员','1'),('1640321780066152448','1602918114232172552','cmsNotifyReleaseNotify','上架公告接口','/ab-bpm/cms/cmsNotify/releaseNotify',1,0,NULL,'Menu','API',4,'1602918114232173033',NULL,'2023-03-27 19:56:03','1','410054569125740545','2023-03-27 19:56:03','管理员','1'),('1640321956977700864','1602918114232172552','cmsNotifyremove','删除公告接口','/ab-bpm/cms/cmsNotify/remove',1,0,NULL,'Menu','API',5,'1602918114232173033',NULL,'2023-03-27 19:56:45','1','410054569125740545','2023-03-27 19:56:45','管理员','1'),('1640324953984069632','1602918114232172552','userlistJson','获取用户列表接口','/ab-org/user/listJson',1,0,NULL,'Menu','API',6,'1602918123753242663',NULL,'2023-03-27 20:08:39','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640325060003491840','1602918114232172552','usergetUserVO','根据id获取用户信息','/ab-org/user/getUserVO',1,0,NULL,'Menu','API',7,'1602918123753242663',NULL,'2023-03-27 20:09:05','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640325225733025792','1602918114232172552','usersaveUserDto','保存用户信息接口','/ab-org/user/saveUserDto',1,0,NULL,'Menu','API',8,'1602918123753242663',NULL,'2023-03-27 20:09:44','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640325347799855104','1602918114232172552','userManager_updateStatus','启用/禁用','/ab-org/user/updateUserStatus',1,0,NULL,'Menu','button',7,'1602918123753242663',NULL,'2023-03-27 20:10:13','1','410054569125740545','2023-04-17 19:46:31','超级管理员','1'),('1640325568772567040','1602918114232172552','userManager_resetPassword','重置密码','/ab-org/user/resetUserPassword',1,0,NULL,'Menu','button',8,'1602918123753242663',NULL,'2023-03-27 20:11:06','1','410054569125740545','2023-04-17 19:46:31','超级管理员','1'),('1640326053567000576','1602918114232172552','userremove','用户删除接口','/ab-org/user/remove',1,0,NULL,'Menu','API',11,'1602918123753242663',NULL,'2023-03-27 20:13:02','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640326471093186560','1602918114232172552','getOrgTreeUrl','获取组织树','/ab-org/group/getOrgTree',1,0,NULL,'Menu','API',1,'1602918123753242644',NULL,'2023-03-27 20:14:41','1','410054569125740545','2023-04-17 19:49:40','超级管理员','1'),('1640326817005826048','1602918114232172552','getGroupVo','组织详情','/ab-org/group/getGroupVo',1,0,NULL,'Menu','API',2,'1602918123753242644',NULL,'2023-03-27 20:16:04','1','410054569125740545','2023-04-17 19:49:40','超级管理员','1'),('1640326986975801344','1602918114232172552','queryGroupUser','组织人员','/ab-org/orgRelation/queryGroupUser',1,0,NULL,'Menu','API',5,'1602918123753242644',NULL,'2023-03-27 20:16:44','1','410054569125740545','2023-04-17 19:49:41','超级管理员','1'),('1640327110233812992','1602918114232172552','groupsaveGroup','更新保存组织信息','/ab-org/group/saveGroup',1,0,NULL,'Menu','API',5,'1602918123753242644',NULL,'2023-03-27 20:17:14','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640327716885360640','1602918114232172552','orgRelation_updateStatus','启用/禁用','/ab-org/orgRelation/updateStatus',1,0,NULL,'Menu','button',4,'1640326986975801344',NULL,'2023-03-27 20:19:38','1','410054569125740545','2023-04-17 19:58:14','超级管理员','1'),('1640327857855918080','1602918114232172552','orgRelation_setMaster','设为主组织','/ab-org/orgRelation/setMaster',1,0,NULL,'Menu','button',5,'1640326986975801344',NULL,'2023-03-27 20:20:12','1','410054569125740545','2023-04-17 19:58:14','超级管理员','1'),('1640328005445087232','1602918114232172552','orgRelation_remove','删除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',6,'1640326986975801344',NULL,'2023-03-27 20:20:47','1','410054569125740545','2023-04-17 19:58:14','超级管理员','1'),('1640328185477197824','1602918114232172552','orgRelation_getGroupPost','组织岗位','/ab-org/orgRelation/getGroupPost',1,0,NULL,'Menu','API',1,'1647931158949433344',NULL,'2023-03-27 20:21:30','1','410054569125740545','2023-04-17 19:58:14','超级管理员','1'),('1640328344390987776','1602918114232172552','orgRelation_saveGroupUserRel','保存岗位用户','/ab-org/orgRelation/saveGroupUserRel',1,0,NULL,'Menu','API',2,'1647931158949433344',NULL,'2023-03-27 20:22:08','1','410054569125740545','2023-04-17 19:58:14','超级管理员','1'),('1640328924882661376','1602918114232172552','roleManager_search','查询','/ab-org/role/listJson',1,0,NULL,'Menu','button',2,'1602918123753242652',NULL,'2023-03-27 20:24:26','1','410054569125740545','2023-04-17 15:24:22','超级管理员','1'),('1640329079992217600','1602918114232172552','roleget','根据id获取角色接口','/ab-org/role/get',1,0,NULL,'Menu','API',7,'1602918123753242652',NULL,'2023-03-27 20:25:03','1','410054569125740545','2023-04-07 18:24:24','系统管理员','1602918114232172634'),('1640329210791587840','1602918114232172552','roleManager_save','保存','/ab-org/role/save',1,0,NULL,'Menu','button',4,'1602918123753242652',NULL,'2023-03-27 20:25:34','1','410054569125740545','2023-04-17 15:24:22','超级管理员','1'),('1640329524181594112','1602918114232172552','orgRelation_batchDel','批量删除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',4,'1602918123753242655',NULL,'2023-03-27 20:26:49','1','410054569125740545','2023-04-17 16:04:07','超级管理员','1'),('1640329719468388352','1602918114232172552','orgRelation_add','添加用户','',1,0,NULL,'Menu','button',3,'1602918123753242655',NULL,'2023-03-27 20:27:36','1','410054569125740545','2023-04-17 16:45:43','超级管理员','1'),('1640329949924421632','1602918114232172552','roleManager_batchDel','批量删除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',5,'1602918123753242652',NULL,'2023-03-27 20:28:31','1','410054569125740545','2023-04-17 15:24:22','超级管理员','1'),('1640330270046285824','1602918114232172552','resRo_getRoleResTreeData','角色资源树','/ab-bpm/sys/resRole/getRoleResTreeData',1,0,NULL,'Menu','API',2,'1602918123753242657',NULL,'2023-03-27 20:29:47','1','410054569125740545','2023-04-17 15:24:23','超级管理员','1'),('1640330707092762624','1602918114232172552','authorityList_list','资源定义列表','/ab-bpm/sys/dataPrivilege/authorityListJson',1,0,NULL,'Menu','API',1,'1602918123753242653',NULL,'2023-03-27 20:31:31','1','410054569125740545','2023-04-17 16:37:51','超级管理员','1'),('1640330980360056832','1602918114232172552','authorityList_setUp','设置','/ab-bpm/sys/dataPrivilege/getGivenDetail',1,0,NULL,'Menu','button',4,'1602918123753242653',NULL,'2023-03-27 20:32:36','1','410054569125740545','2023-04-17 16:35:02','超级管理员','1'),('1640331181405630464','1602918114232172552','authorityList_given','确定','/ab-bpm/sys/dataPrivilege/given',1,0,NULL,'Menu','API',1,'1640330980360056832',NULL,'2023-03-27 20:33:24','1','410054569125740545','2023-04-17 16:58:12','超级管理员','1'),('1640331436872298496','1602918114232172552','authorityList_resetGiven','重置','/ab-bpm/sys/dataPrivilege/resetGiven',1,0,NULL,'Menu','button',5,'1602918123753242653',NULL,'2023-03-27 20:34:25','1','410054569125740545','2023-04-17 16:35:02','超级管理员','1'),('1640543677970206720','1602918114232172552','propertiesgetByCode','获取系统属性接口','/ab-bpm/sys/properties/getByCode',1,0,NULL,'Menu','API',1,'1602918114232172753',NULL,'2023-03-28 10:37:47','1','410054569125740545','2023-03-28 10:37:47','管理员','1'),('1640543862406336512','1602918114232172552','bizFormDesignlistVOJson','获取表单设计列表接口','/ab-bpm/biz/bizFormDesign/listVOJson',1,0,NULL,'Menu','API',2,'1602918114232172753',NULL,'2023-03-28 10:38:31','1','410054569125740545','2023-03-28 10:38:31','管理员','1'),('1640544522858221568','1602918114232172552','bizFormDesigngetVO','根据id获取表单设计vo接口','/ab-bpm/biz/bizFormDesign/getVO',1,0,NULL,'Menu','API',3,'1602918114232172753',NULL,'2023-03-28 10:41:09','1','410054569125740545','2023-03-28 10:41:09','管理员','1'),('1640544681071562752','1602918114232172552','bizObjectgetBo','根据key获取bo对象接口','/ab-bpm/biz/bizObject/getBo',1,0,NULL,'Menu','API',4,'1602918114232172753',NULL,'2023-03-28 10:41:46','1','410054569125740545','2023-03-28 10:41:46','管理员','1'),('1640544886672150528','1602918114232172552','bizFormTemplatelistJson','获取表单模板接口','/ab-bpm/biz/bizFormTemplate/listJson',1,0,NULL,'Menu','API',5,'1602918114232172753',NULL,'2023-03-28 10:42:35','1','410054569125740545','2023-03-28 10:42:35','管理员','1'),('1640561438402367488','1602918114232172552','bizCustDialoglistJson','自定义对话框列表接口','/ab-bpm/biz/bizCustDialog/listJson',1,0,NULL,'Menu','API',1,'1602918114232172823',NULL,'2023-03-28 11:48:22','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640561752643817472','1602918114232172552','dataSourcelistUrl','获取数据源列表接口','/ab-bpm/sys/dataSource/listJson',1,0,NULL,'Menu','API',2,'1602918114232172823',NULL,'2023-03-28 11:49:37','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640596207907459072','1602918114232172552','bizTablesearchObjNameUrl','根据数据源和关键字搜索匹配的表信息','/ab-bpm/biz/bizTable/searchObjName',1,0,NULL,'Menu','API',3,'1602918114232172823',NULL,'2023-03-28 14:06:31','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640597585983787008','1602918114232172552','bizCustDialogget','自定义对话框-通过ID查询接口','/ab-bpm/biz/bizCustDialog/get',1,0,NULL,'Menu','API',4,'1602918114232172823',NULL,'2023-03-28 14:12:00','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640597847112765440','1602918114232172552','bizCustDialoggetByCode','自定义对话框通过编号查询接口','/ab-bpm/biz/bizCustDialog/getByCode',1,0,NULL,'Menu','API',5,'1602918114232172823',NULL,'2023-03-28 14:13:02','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640598069008224256','1602918114232172552','bizCustDialoglistData','自定义对话框预览接口','/ab-bpm/biz/bizCustDialog/listData',1,0,NULL,'Menu','API',6,'1602918114232172823',NULL,'2023-03-28 14:13:55','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640598226525310976','1602918114232172552','bizCustDialoglistSimpleData','自定义对话框-预览查询接口','/ab-bpm/biz/bizCustDialog/listSimpleData',1,0,NULL,'Menu','API',7,'1602918114232172823',NULL,'2023-03-28 14:14:33','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640598340903981056','1602918114232172552','bizCustDialogtreeData','自定义对话框-预览树形数据','/ab-bpm/biz/bizCustDialog/treeData',1,0,NULL,'Menu','API',8,'1602918114232172823',NULL,'2023-03-28 14:15:00','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640598484269486080','1602918114232172552','bizCustDialogremove','自定义对话框-删除','/ab-bpm/biz/bizCustDialog/remove',1,0,NULL,'Menu','API',9,'1602918114232172823',NULL,'2023-03-28 14:15:34','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640598607623966720','1602918114232172552','bizCustDialoggetTable','自定义对话框-模糊查询表','/ab-bpm/biz/bizCustDialog/getTable',1,0,NULL,'Menu','API',10,'1602918114232172823',NULL,'2023-03-28 14:16:04','1','410054569125740545','2023-06-28 09:13:35','系统管理员','1602918114232172634'),('1640618460778942464','1602918114232172552','definitionlistJsonUrl_1','查看流程列表接口','/ab-bpm/bpm/definition/listJson',1,0,NULL,'Menu','API',7,'1602918114232172834',NULL,'2023-03-28 15:34:57','1','410054569125740545','2023-03-28 15:34:57','管理员','1'),('1640618912056692736','1602918114232172552','definitionsaveUrl','保存流程设置基本信息接口','/ab-bpm/bpm/definition/save',1,0,NULL,'Menu','API',8,'1602918114232172834',NULL,'2023-03-28 15:36:45','1','410054569125740545','2023-03-28 15:36:45','管理员','1'),('1640619140465905664','1602918114232172552','definitiongetDesign','根据id获取流程设计','/ab-bpm/bpm/definition/getDesign',1,0,NULL,'Menu','API',9,'1602918114232172834',NULL,'2023-03-28 15:37:39','1','410054569125740545','2023-03-28 15:37:39','管理员','1'),('1640619324843315200','1602918114232172552','processDefgetDefaultButtons','获取默认流程按钮接口','/ab-bpm/bpm/processDef/getDefaultButtons',1,0,NULL,'Menu','API',10,'1602918114232172834',NULL,'2023-03-28 15:38:23','1','410054569125740545','2023-03-28 15:38:23','管理员','1'),('1640620101271900160','1602918114232172552','definitionsaveDesign','保存流程定义接口','/ab-bpm/bpm/definition/saveDesign',1,0,NULL,'Menu','API',11,'1602918114232172834',NULL,'2023-03-28 15:41:28','1','410054569125740545','2023-03-28 15:41:28','管理员','1'),('1640620291819130880','1602918114232172552','instancegetStartFlowDataUrl','获取启动流程数据接口','/ab-bpm/bpm/instance/getStartFlowData',1,0,NULL,'Menu','API',12,'1602918114232172834',NULL,'2023-03-28 15:42:13','1','410054569125740545','2023-03-28 15:42:13','管理员','1'),('1640620422547197952','1602918114232172552','authorizationgetAuthorizations_1','获取流程权限信息接口','/ab-bpm/sys/authorization/getAuthorizations',1,0,NULL,'Menu','API',13,'1602918114232172834',NULL,'2023-03-28 15:42:45','1','410054569125740545','2023-03-28 15:42:45','管理员','1'),('1640620523382460416','1602918114232172552','saveAuthorization','保存流程权限配置接口','/ab-bpm/sys/authorization/saveAuthorization',1,0,NULL,'Menu','API',14,'1602918114232172834',NULL,'2023-03-28 15:43:09','1','410054569125740545','2023-03-29 09:28:54','管理员','1'),('1640620667834290176','1602918114232172552','definitionlistJson_1','流程版本管理列表接口','/ab-bpm/bpm/definition/listJson',1,0,NULL,'Menu','API',15,'1602918114232172834',NULL,'2023-03-28 15:43:43','1','410054569125740545','2023-03-28 15:43:43','管理员','1'),('1640620797916434432','1602918114232172552','definitionremove_1','流程设计删除接口','/ab-bpm/bpm/definition/remove',1,0,NULL,'Menu','API',16,'1602918114232172834',NULL,'2023-03-28 15:44:14','1','410054569125740545','2023-03-28 15:44:14','管理员','1'),('1640623150195363840','1602918114232172552','instancelistJson','流程实例列表接口','/ab-bpm/bpm/instance/listJson',1,0,NULL,'Menu','API',1,'1602918114232172860',NULL,'2023-03-28 15:53:35','1','410054569125740545','2023-06-28 09:13:12','系统管理员','1602918114232172634'),('1640623243661234176','1602918114232172552','instanceListTypeTree','流程实例左侧树接口','/ab-bpm/bpm/my/instanceListTypeTree',1,0,NULL,'Menu','API',2,'1602918114232172860',NULL,'2023-03-28 15:53:57','1','410054569125740545','2023-06-28 09:13:12','系统管理员','1602918114232172634'),('1640623389971140608','1602918114232172552','instancegetInstData','获取流程实例数据接口','/ab-bpm/bpm/instance/getInstData',1,0,NULL,'Menu','API',3,'1602918114232172860',NULL,'2023-03-28 15:54:32','1','410054569125740545','2023-06-28 09:13:12','系统管理员','1602918114232172634'),('1640623728539553792','1602918114232172552','taskListTypeTree','任务列表左侧树接口','/ab-bpm/bpm/my/taskListTypeTree',1,0,NULL,'Menu','API',1,'1602918114232172871',NULL,'2023-03-28 15:55:53','1','410054569125740545','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1640623838191243264','1602918114232172552','tasklistJson','任务列表接口','/ab-bpm/bpm/task/listJson',1,0,NULL,'Menu','API',2,'1602918114232172871',NULL,'2023-03-28 15:56:19','1','410054569125740545','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1640624396943839232','1602918114232172552','assignTask','任务指派接口','/ab-bpm/bpm/task/assignTask',1,0,NULL,'Menu','API',3,'1602918114232172871',NULL,'2023-03-28 15:58:32','1','410054569125740545','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1640624547431272448','1602918114232172552','taskgetTaskData','获取任务数据接口','/ab-bpm/bpm/task/getTaskData',1,0,NULL,'Menu','API',4,'1602918114232172871',NULL,'2023-03-28 15:59:08','1','410054569125740545','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1640624623260094464','1602918114232172552','getFlowImageInfo','获取流程图接口','/ab-bpm/bpm/instance/getFlowImageInfo',1,0,NULL,'Menu','API',5,'1602918114232172871',NULL,'2023-03-28 15:59:26','1','410054569125740545','2023-06-28 09:13:22','系统管理员','1602918114232172634'),('1640631041182748672','1602918114232172552','resourcegetTreeData','获取系统树接口','/ab-bpm/sys/resource/getTreeData',1,0,NULL,'Menu','API',1,'1602918123753242640',NULL,'2023-03-28 16:24:56','1','410054569125740545','2023-03-28 16:28:03','管理员','1'),('1640631357630402560','1602918114232172552','resourcesave','保存系统树接口','/ab-bpm/sys/resource/save',1,0,NULL,'Menu','API',2,'1602918123753242640',NULL,'2023-03-28 16:26:12','1','410054569125740545','2023-03-28 16:28:03','管理员','1'),('1640631644055228416','1602918114232172552','resourcegetJson','根据id获取资源接口','/ab-bpm/sys/resource/getJson',1,0,NULL,'Menu','API',1,'1602918114232173036',NULL,'2023-03-28 16:27:20','1','410054569125740545','2023-06-28 09:14:26','系统管理员','1602918114232172634'),('1640632141860392960','1602918114232172552','resourcesaveTree','保存系统树接口','/ab-bpm/sys/resource/saveTree',1,0,NULL,'Menu','API',3,'1602918123753242640',NULL,'2023-03-28 16:29:19','1','410054569125740545','2023-03-28 16:29:19','管理员','1'),('1640633218970243072','1602918114232172552','propertieslistJson','系统属性列表接口','/ab-bpm/sys/properties/listJson',1,0,NULL,'Menu','API',1,'1602918114232173040',NULL,'2023-03-28 16:33:36','1','410054569125740545','2023-03-28 16:33:36','管理员','1'),('1640634326987919360','1602918114232172552','propertiessave','系统属性保存接口','/ab-bpm/sys/properties/save',1,0,NULL,'Menu','API',2,'1602918114232173040',NULL,'2023-03-28 16:38:00','1','410054569125740545','2023-03-28 16:38:00','管理员','1'),('1640634472769343488','1602918114232172552','propertiesget','系统属性获取接口','/ab-bpm/sys/properties/get',1,0,NULL,'Menu','API',3,'1602918114232173040',NULL,'2023-03-28 16:38:34','1','410054569125740545','2023-03-28 16:38:34','管理员','1'),('1640634683554091008','1602918114232172552','propertiesremove','删除系统属性接口','ab-bpm/sys/properties/remove',1,0,NULL,'Menu','API',4,'1602918114232173040',NULL,'2023-03-28 16:39:25','1','410054569125740545','2023-03-28 16:39:25','管理员','1'),('1640634915738177536','1602918114232172552','dataDictgetDictData','获取字典项','/ab-bpm/sys/dataDict/getDictData',1,0,NULL,'Menu','API',1,'1602918123753242629',NULL,'2023-03-28 16:40:20','1','410054569125740545','2023-03-28 16:40:20','管理员','1'),('1640635035263258624','1602918114232172552','dataDictgetDictList','获取字典列表','/ab-bpm/sys/dataDict/getDictList',1,0,NULL,'Menu','API',2,'1602918123753242629',NULL,'2023-03-28 16:40:49','1','410054569125740545','2023-03-28 16:40:49','管理员','1'),('1640635308186619904','1602918114232172552','dataDictsave','保存数据字典','/ab-bpm/sys/dataDict/save',1,0,NULL,'Menu','API',3,'1602918123753242629',NULL,'2023-03-28 16:41:54','1','410054569125740545','2023-03-28 16:41:54','管理员','1'),('1640635380471255040','1602918114232172552','fyhqzdlb','分页获取字典列表','/ab-bpm/sys/dataDict/listJson',1,0,NULL,'Menu','API',4,'1602918123753242629',NULL,'2023-03-28 16:42:11','1','410054569125740545','2023-03-28 16:42:11','管理员','1'),('1640635441817145344','1602918114232172552','sczd','删除字典','/ab-bpm/sys/dataDict/remove',1,0,NULL,'Menu','API',5,'1602918123753242629',NULL,'2023-03-28 16:42:26','1','410054569125740545','2023-03-28 16:42:26','管理员','1'),('1640635509836173312','1602918114232172552','hqzds','获取字典树','/ab-bpm/sys/dataDict/getDictTree',1,0,NULL,'Menu','API',6,'1602918123753242629',NULL,'2023-03-28 16:42:42','1','410054569125740545','2023-03-28 16:42:42','管理员','1'),('1640635574646558720','1602918114232172552','hqzdfljflxdzd','获取字典分类及分类下的字典','/ab-bpm/sys/dataDict/getDictTypeTree',1,0,NULL,'Menu','API',7,'1602918123753242629',NULL,'2023-03-28 16:42:57','1','410054569125740545','2023-03-28 16:42:57','管理员','1'),('1640635671363014656','1602918114232172552','plbcsjzd','批量保存数据字典','/ab-bpm/sys/dataDict/saveBatch',1,0,NULL,'Menu','API',8,'1602918123753242629',NULL,'2023-03-28 16:43:20','1','410054569125740545','2023-03-28 16:43:20','管理员','1'),('1640637001951432704','1602918114232172552','xtjbhq','系统脚本获取','/ab-bpm/sys/script/get',1,0,NULL,'Menu','API',1,'1602918123749048326',NULL,'2023-03-28 16:48:37','1','410054569125740545','2023-03-28 16:48:37','管理员','1'),('1640637056653545472','1602918114232172552','xtjblb','系统脚本列表','/ab-bpm/sys/script/listJson',1,0,NULL,'Menu','API',2,'1602918123749048326',NULL,'2023-03-28 16:48:51','1','410054569125740545','2023-03-28 16:48:51','管理员','1'),('1640637122646724608','1602918114232172552','xtjbsc','系统脚本删除','/ab-bpm/sys/script/remove',1,0,NULL,'Menu','API',3,'1602918123749048326',NULL,'2023-03-28 16:49:06','1','410054569125740545','2023-03-28 16:49:06','管理员','1'),('1640637187587133440','1602918114232172552','xtjbbc','系统脚本保存','/ab-bpm/sys/script/save',1,0,NULL,'Menu','API',4,'1602918123749048326',NULL,'2023-03-28 16:49:22','1','410054569125740545','2023-03-28 16:49:22','管理员','1'),('1640642847787798528','1602918114232172552','ycrzlb','异常日志列表','/ab-bpm/sys/logErr/listJson',1,0,NULL,'Menu','API',1,'1605477742950285312',NULL,'2023-03-28 17:11:51','1','410054569125740545','2023-03-28 17:11:51','管理员','1'),('1640642953261961216','1602918114232172552','hqycrzmx','获取异常日志明细','/ab-bpm/sys/logErr/get',1,0,NULL,'Menu','API',2,'1605477742950285312',NULL,'2023-03-28 17:12:16','1','410054569125740545','2023-03-28 17:12:16','管理员','1'),('1640643130718769152','1602918114232172552','ycrzscjk','异常日志删除接口','/ab-bpm/sys/logErr/remove',1,0,NULL,'Menu','API',3,'1605477742950285312',NULL,'2023-03-28 17:12:59','1','410054569125740545','2023-03-28 17:12:59','管理员','1'),('1640903961616445440','1602918114232172552','lccljk','流程处理接口','/ab-bpm/bpm/instance/doAction',1,0,NULL,'Menu','API',1,'1602918114232172583',NULL,'2023-03-29 10:29:26','1','410054569125740545','2023-06-27 17:26:17','系统管理员','1602918114232172634'),('1640973106823364608','1602918114232172552','rwcljk','任务处理接口','/ab-bpm/bpm/task/doAction',1,0,NULL,'Menu','API',2,'1602918114232172583',NULL,'2023-03-29 15:04:11','1','410054569125740545','2023-06-27 17:26:17','系统管理员','1602918114232172634'),('1640981731805560832','1602918114232172552','instancegetFlowImageInfo','获取流程图接口','/ab-bpm/bpm/instance/getFlowImageInfo',1,0,NULL,'Menu','API',3,'1602918114232172583',NULL,'2023-03-29 15:38:27','1','410054569125740545','2023-06-27 17:26:17','系统管理员','1602918114232172634'),('1645710802312806400','1602918114232172552','hqjdlbjk','获取节点列表接口','http://test.a5.tongzhouyun.com',1,0,NULL,'Menu','API',4,'1602918114232172860',NULL,'2023-04-11 16:50:06','1602918114232172634','1602918114232172544','2023-06-28 09:13:12','系统管理员','1602918114232172634'),('1646799989788815360','1602918114232172552','groupManager_add','新增','',1,0,NULL,'Menu','button',1,'1640326471093186560',NULL,'2023-04-14 16:58:08','1','1574312461024690176','2023-04-17 19:49:40','超级管理员','1'),('1646800150527127552','1602918114232172552','groupManager_edit','编辑','/ab-org/group/getGroupVo',1,0,NULL,'Menu','button',3,'1602918123753242644',NULL,'2023-04-14 16:58:47','1','1574312461024690176','2023-04-17 19:49:41','超级管理员','1'),('1646800661313662976','1602918114232172552','groupManager_del','删除','/ab-org/group/remove',1,0,NULL,'Menu','button',2,'1640326471093186560',NULL,'2023-04-14 17:00:48','1','1574312461024690176','2023-04-17 19:49:40','超级管理员','1'),('1646801040151588864','1602918114232172552','saveGroup','保存','/ab-org/group/saveGroup',1,0,NULL,'Menu','API',4,'1602918123753242644',NULL,'2023-04-14 17:02:19','1','1574312461024690176','2023-04-17 19:49:41','超级管理员','1'),('1646805147742547968','1602918114232172552','groupManager_refreshTree','刷新','/ab-org/group/getOrgTree',1,0,NULL,'Menu','button',3,'1640326471093186560',NULL,'2023-04-14 17:18:38','1','1574312461024690176','2023-04-18 09:22:35','超级管理员','1'),('1646807731765161984','1602918114232172552','userManager_save','保存','/ab-org/user/saveUserDto',1,0,NULL,'Menu','API',5,'1602918123753242663',NULL,'2023-04-14 17:28:54','1','1574312461024690176','2023-04-17 19:46:31','超级管理员','1'),('1647795683106394112','1602918114232172552','roleManager_add','添加','',1,0,NULL,'Menu','button',3,'1602918123753242652',NULL,'2023-04-17 10:54:40','1','1574312461024690176','2023-04-17 15:24:22','超级管理员','1'),('1647798080461508608','1602918114232172552','orgRelation_search','查询','/ab-org/orgRelation/roleJson',1,0,NULL,'Menu','button',2,'1602918123753242655',NULL,'2023-04-17 11:04:12','1','1574312461024690176','2023-04-17 16:13:23','超级管理员','1'),('1647858089165754368','1602918114232172552','application_list','应用查询','/ab-bpm/sys/application/listJson',1,0,NULL,'Menu','API',1,'1602918123753242657',NULL,'2023-04-17 15:02:39','1','1574312461024690176','2023-04-17 15:24:22','超级管理员','1'),('1647858955268558848','1602918114232172552','resRole_grantRoleResource','保存','/ab-bpm/sys/resRole/grantRoleResource',1,0,NULL,'Menu','button',3,'1602918123753242657',NULL,'2023-04-17 15:06:05','1','1574312461024690176','2023-04-17 15:24:23','超级管理员','1'),('1647863296809406464','1602918114232172552','user_listJson','用户列表','/ab-org/user/listJson',1,0,NULL,'Menu','API',1,'1602918123753242663',NULL,'2023-04-17 15:23:20','1','1574312461024690176','2023-04-17 19:46:30','超级管理员','1'),('1647863503857029120','1602918114232172552','role_listJson','角色列表','/ab-org/role/listJson',1,0,NULL,'Menu','API',1,'1602918123753242652',NULL,'2023-04-17 15:24:10','1','1574312461024690176','2023-04-17 15:24:22','超级管理员','1'),('1647863823999864832','1602918114232172552','orgRelation_roleJson','用户列表','/ab-org/orgRelation/roleJson',1,0,NULL,'Menu','API',1,'1602918123753242655',NULL,'2023-04-17 15:25:26','1','1574312461024690176','2023-04-17 15:28:23','超级管理员','1'),('1647864526046662656','1602918114232172552','orgRelation_del','移除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',5,'1602918123753242655',NULL,'2023-04-17 15:28:14','1','1574312461024690176','2023-04-17 16:04:41','超级管理员','1'),('1647877218769735680','1602918114232172552','authorityList_search','查询','/ab-bpm/sys/dataPrivilege/authorityListJson',1,0,NULL,'Menu','button',2,'1602918123753242653',NULL,'2023-04-17 16:18:40','1','1574312461024690176','2023-04-17 16:35:02','超级管理员','1'),('1647880328523190272','1602918114232172552','dataPrivilege_global','确定','/ab-bpm/sys/dataPrivilege/given',1,0,NULL,'Menu','API',1,'1647881130339897344',NULL,'2023-04-17 16:31:01','1','1574312461024690176','2023-04-17 16:57:57','超级管理员','1'),('1647881130339897344','1602918114232172552','dataPrivilege_getGivenDetail','全局设置','/ab-bpm/sys/dataPrivilege/getGivenDetail',1,0,NULL,'Menu','button',3,'1602918123753242653',NULL,'2023-04-17 16:34:12','1','1574312461024690176','2023-04-17 16:35:02','超级管理员','1'),('1647884205301600256','1602918114232172552','orgRelation_saveRoleUsers','确定','/ab-org/orgRelation/saveRoleUsers',1,0,NULL,'Menu','API',1,'1640329719468388352',NULL,'2023-04-17 16:46:25','1','1574312461024690176','2023-04-17 16:57:36','超级管理员','1'),('1647899828404523008','1602918114232172552','groupUser_search','查询','/ab-org/orgRelation/queryGroupUser',1,0,NULL,'Menu','button',1,'1640326986975801344',NULL,'2023-04-17 17:48:30','1','1574312461024690176','2023-04-17 19:58:14','超级管理员','1'),('1647931158949433344','1602918114232172552','orgRelation_addGroupUser','新增岗位用户','',1,0,NULL,'Menu','button',2,'1640326986975801344',NULL,'2023-04-17 19:53:00','1','1574312461024690176','2023-04-17 19:58:14','超级管理员','1'),('1647932430666600448','1602918114232172552','groupManager_batchDel','批量删除','/ab-org/orgRelation/remove',1,0,NULL,'Menu','button',3,'1640326986975801344',NULL,'2023-04-17 19:58:03','1','1574312461024690176','2023-04-17 19:58:14','超级管理员','1'),('1658365830248841216','1602918114232172552','bpmDefinition_duplicate','流程复制','',1,0,NULL,'Menu','button',17,'1602918114232172834',NULL,'2023-03-28 15:44:14','1','410054569125740545','2023-05-15 16:36:27','系统管理员','1602918114232172634'); +/*!40000 ALTER TABLE `sys_resource` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_resource_role` +-- + +DROP TABLE IF EXISTS `sys_resource_role`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_resource_role` ( + `id_` varchar(50) NOT NULL COMMENT 'ID', + `app_id_` varchar(50) DEFAULT NULL COMMENT '应用ID', + `resource_id_` varchar(50) DEFAULT NULL COMMENT '资源权限ID', + `role_id_` varchar(50) DEFAULT NULL COMMENT '角色ID', + `half_checked_` tinyint unsigned DEFAULT '0' COMMENT '半选中', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + PRIMARY KEY (`id_`), + KEY `idx_resource_role_id_` (`role_id_`), + KEY `idx_resource_resource_id_` (`resource_id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='资源角色关联表'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_resource_role` +-- + +LOCK TABLES `sys_resource_role` WRITE; +/*!40000 ALTER TABLE `sys_resource_role` DISABLE KEYS */; +INSERT INTO `sys_resource_role` VALUES ('1605470628966105088','1602918114232172552','1602918114232172673','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628970299393','1602918114232172552','1602918114232172667','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628970299394','1602918114232172552','1602918114232172823','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628970299396','1602918114232172552','1602918114232172829','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628970299400','1602918114232172552','1602918114232172682','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628974493697','1602918114232172552','1602918114232173009','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628974493699','1602918114232172552','1602918114232172834','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628974493700','1602918114232172552','1602918114232172836','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688001','1602918114232172552','1602918114232172654','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688003','1602918114232172552','1602918123753242672','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688004','1602918114232172552','1602918123753242670','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688005','1602918114232172552','1602918123753242668','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688011','1602918114232172552','1602918123753242666','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688013','1602918114232172552','1602918123749048326','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688014','1602918114232172552','1602918123753242667','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688016','1602918114232172552','1602918114232173040','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628978688020','1602918114232172552','1602918123753242660','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882305','1602918114232172552','1602918123753242661','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882306','1602918114232172552','1602918123753242663','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882307','1602918114232172552','1602918114232172871','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882310','1602918114232172552','1602918114232172753','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882312','1602918114232172552','1602918123753242657','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882318','1602918114232172552','1602918114232172987','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882319','1602918114232172552','1602918123753242653','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882320','1602918114232172552','1602918123753242655','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882321','1602918114232172552','1602918123753242650','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882322','1602918114232172552','1602918123753242652','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628982882323','1602918114232172552','1602918114232172641','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076609','1602918114232172552','1602918123753242642','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076610','1602918114232172552','1602918123753242644','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076613','1602918114232172552','1602918114232172693','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076614','1602918114232172552','1602918123753242640','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076617','1602918114232172552','1602918114232172696','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628987076618','1602918114232172552','1602918114232172850','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270919','1602918114232172552','1602918114232172845','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270922','1602918114232172552','1602918114232172847','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270927','1602918114232172552','1602918114232173033','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270928','1602918114232172552','1602918114232172583','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270930','1602918114232172552','1602918114232173036','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270931','1602918114232172552','1602918114232172860','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628991270933','1602918114232172552','1602918114232172853','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628995465216','1602918114232172552','1602918114232172857','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605470628995465218','1602918114232172552','1602918123753242629','1605464158287495168',0,'2022-12-21 15:50:01','1602918114232172634'),('1605471010190589953','1602918114232172552','1602918114232172693','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010190589955','1602918114232172552','1602918114232172673','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010190589957','1602918114232172552','1602918114232172696','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010190589959','1602918114232172552','1602918114232172654','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010194784256','1602918114232172552','1602918114232172667','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010198978561','1602918114232172552','1602918114232172583','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010198978562','1602918114232172552','1602918114232172682','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1605471010198978563','1602918114232172552','1602918114232172641','1605464389708218368',0,'2022-12-21 15:51:32','1602918114232172634'),('1610482005446856705','1602918114232172552','1602918114232172583','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856706','1602918114232172552','1602918114232172682','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856707','1602918114232172552','1602918114232172693','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856708','1602918114232172552','1602918114232172673','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856709','1602918114232172552','1602918114232172641','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856710','1602918114232172552','1602918114232172696','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856712','1602918114232172552','1602918114232172654','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'),('1610482005446856715','1602918114232172552','1602918114232172667','1610480371538956288',0,'2023-01-04 11:43:26','1602918114232172634'); +/*!40000 ALTER TABLE `sys_resource_role` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Table structure for table `sys_script` +-- + +DROP TABLE IF EXISTS `sys_script`; +/*!40101 SET @saved_cs_client = @@character_set_client */; +/*!50503 SET character_set_client = utf8mb4 */; +CREATE TABLE `sys_script` ( + `id_` varchar(64) NOT NULL COMMENT 'ID', + `name_` varchar(128) DEFAULT NULL COMMENT '脚本名称', + `script_` text COMMENT '脚本', + `type_code_` varchar(128) DEFAULT NULL COMMENT '分类字典编码', + `desc_` varchar(512) DEFAULT NULL COMMENT '描述', + `create_time_` datetime DEFAULT NULL COMMENT '创建时间', + `create_by_` varchar(64) DEFAULT NULL COMMENT '创建人ID', + `create_org_id_` varchar(64) DEFAULT NULL COMMENT '所属组织', + `update_time_` datetime DEFAULT NULL COMMENT '更新时间', + `updater_` varchar(64) DEFAULT NULL COMMENT '更新人', + `update_by_` varchar(64) DEFAULT NULL COMMENT '更新人ID', + PRIMARY KEY (`id_`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='常用脚本'; +/*!40101 SET character_set_client = @saved_cs_client */; + +-- +-- Dumping data for table `sys_script` +-- + +LOCK TABLES `sys_script` WRITE; +/*!40000 ALTER TABLE `sys_script` DISABLE KEYS */; +INSERT INTO `sys_script` VALUES ('1602918114232172599','通过组织id 组织类型 角色 获取候选人','orgScript.getSpecificSuperPostIdentity(String groupId, int level, String roleCodes)\n','setCandidate','用于流程人员脚本,通过组织id 组织类型 角色 设置候选人,Set','2022-12-14 14:47:09','1','','2023-04-13 16:24:31','超级管理员','1'),('1602918114232172619','设置通过ID Name type 构建候选人','orgScript.constructeIdentity(String id, String name, String type)\n//orgScript.constructeIdentity(\"1\", \"管理员\",\"user\")','setCandidate','用于流程人员脚本,通过ID Name type 构建候选人,Set','2022-12-14 14:47:09','1','','2023-04-13 16:24:19','超级管理员','1'),('1602918114232172637','设置通过ID Name 构建候选人','orgScript.constructeIdentityUser(String id, String name)\n//orgScript.constructeIdentityUser(\"1\", \"管理员\")','setCandidate','用于流程人员脚本,通过ID Name 构建候选人,Set','2022-12-14 14:47:09','1','','2023-04-13 16:23:50','超级管理员','1'),('1602918114232172659','获取上级组织作为候选人','// 发起人所在组织\n//String startOrgId = bpmInstance.getCreateOrgId();\n\n// groupId 若为null 则默认获取当前组织即 currentOrgId\nSet parentIdentity = orgScript.getSuperOrgIdentity(groupId);\n\n','setCandidate','用于流程人员脚本,返回Set','2022-12-14 14:47:09','1','','2023-04-13 16:24:06','超级管理员','1'),('1602918114232172662','获取指定类型的上级组织作为候选人','// groupId 为组织ID,若为null 则使用当前组织,level为组织级别 集团0、分公司1、组织3、班组5\nSet group = orgScript.getSpecificSuperOrgIdentity(String groupId,int level)\n\n\n// 如获取发起人所在组织的分公司作为候选人\n//String startOrgId = bpmInstance.getCreateOrgId();\n//return getSpecificSuperOrgIdentity(startOrgId,1);','setCandidate','用于流程人员脚本,返回指定类型组织的Set','2022-12-14 14:47:09','1','','2023-04-13 16:23:58','超级管理员','1'),('1602918114232172670','根据角色条件获取角色候选组','//orgScript.getRoleSis(String where)\n// 获取级别大于等于50 小于等于60:\norgScript.getRoleSis(\"level_ >= 50 and level_ <= 200\")','getGroupInformation','根据角色条件获取角色候选组','2022-12-14 14:47:09','1','','2023-04-13 15:29:41','超级管理员','1'),('1602918114232172683','根据id获取组信息','orgScript.getSis(String type, String... ids)\n//orgScript.getSis(\"user\", \"1\")\n//orgScript.getSis(\"org\", \"410054569125740545\")','getGroupInformation','根据id获取组信息','2022-12-14 14:47:09','1','','2023-04-12 18:31:45','超级管理员','1'),('1602918114232172700','获取指定类型的上级组织','orgScript.getSpecificSuperOrgIdentity(String groupId, int level)\n//组织id:为空则去当前用户所在组织的id\n//调用getSpecificSuperOrg(String groupId, int level)方法获取上级组织\n// 如获取当前分公司的组织\n//orgScript.getSpecificSuperOrg(null,0);\n','getGroupInformation','如获取当前部门上级中 组织类型为分公司的组织\n即查询所属公司信息\n获取指定组织上级的人员返回SysIdentity对象的集合:接收String类型的groupId和int类型的level; 参考getSpecificSuperOrg(String groupId, int level)方法获取上级组织','2022-12-14 14:47:09','1','','2023-04-12 17:57:43','超级管理员','1'),('1602918114232172704','获取某个岗位下的某些角色的人员列表','orgScript.getPostUserByGroupAndRole(String groupIds, String roleCodes)\n//groupIds:为空,则取当前用户所在组织的id\n//orgScript.getPostUserByGroupAndRole(\"\", \"yyzj\")','getGroupInformation','获取某个岗位下的某些角色的人员列表返回SysIdentity集合:入参String类型的组织id,多个以“,”分隔;为空,则取当前用户所在主组织; String类型的角色编码,多个以“,”分隔','2022-12-14 14:47:09','1','','2023-04-12 18:31:58','超级管理员','1'),('1602918114232172709','获取当前组织下的子组织','orgScript.getOrgChildrend()','getGroupInformation','获取当前组织下的子组织返回组织id的集合','2022-12-14 14:47:09','1','','2023-04-12 17:15:12','超级管理员','1'),('1602918114232172718','获取组织类型','//orgId 若为 null 则获取当前组织\norgScript.getOrgLevel(orgId)\n\n// 获取流程发起人的组织级别\n//return orgScript.getOrgLevel(bpmInstance.getCreateOrgId())','getGroupInformation','用来判断某个组织的组织类型(集团0、分公司1、部门3、班组5)返回Int','2022-12-14 14:47:09','1','','2023-04-12 15:08:24','系统管理员','1'),('1602918114232172721','获取用户 角色/职务 级别','// userId 若为null则获取当前用户\norgScript.getUserRoleLevel(userId);\n\n// 如,流程分支中判断 发起人的职级是否为副总50以上级别。\n//String startUserId = bpmInstance.getCreateBy();\n//return orgScript.getUserRoleLevel(startUserId)>50;\n','getGroupInformation','获取用户 职级、角色级别,当有多个角色时,返回最大的职级。 返回 Int\n使用场景:如 部门经理(职级50)、副总(职级80)、普通员工(职级 20)不同角色他们拥有不同的级别。在请假流程中可以判断用户的职级,当大于某个值时作出对应的跳转','2022-12-14 14:47:09','1','','2023-04-12 15:07:51','系统管理员','1'),('1602918114232172728','检查用户是否拥有某角色','orgScript.checkUserHasRole(String userId, String roleCode)\n//orgScript.checkUserHasRole(\"1\", \"jgs\")\n//用户id:为空,则取当前用户id\n//如判断用户是否为 部门负责人。返回Boolean','systemDefault','检查用户是否拥有某角色:\n入参: userId 若为空则获取当前用户, roleCode 为角色编码\n返回: Boolean','2022-12-14 14:47:09','1','','2023-04-13 10:16:49','超级管理员','1'),('1602918114232172754','指定数据通过一个sql 获取候选人','sysScript.getIdentityBySqlOnDataSource(String dataSourceKey,String sql, Object ... params)','getCandidatesSQL','指定数据通过一个sql 获取候选人','2022-12-14 14:47:09','1','','2023-04-12 18:18:31','超级管理员','1'),('1602918114232172760','通过一个sql 获取候选人','sysScript.getIdentityBySql(String sql, Object ... params)\n','getCandidatesSQL','通过sql获取候选人用于流程节点通过脚本选择','2022-12-14 14:47:09','1','','2023-04-12 18:18:21','超级管理员','1'),('1602918114232172763','指定数据源执行Count SQL更新语句','sysScript.executeIntegerSqlOnDataSource(String dataSourceKey,String sql, Object ... params)','executeSQL','参数:\nsql:SQL 包含绑定的参数\nparams:SQL绑定的入参\n返回值:\nint : coun','2022-12-14 14:47:09','1','','2023-04-12 14:43:13','系统管理员','1'),('1602918114232172766','执行Count SQL更新语句','sysScript.executeIntegerSql(String sql, Object ... params)','executeSQL','参数:\nsql:SQL 包含绑定的参数\nparams:SQL绑定的入参\n返回值:\nint : coun','2022-12-14 14:47:09','1','','2023-04-12 14:42:08','系统管理员','1'),('1602918114232172769','指定数据源执行SQL语句','//dataSourceKey 系统数据源别名\nsysScript.executeUpdateSqlOnDataSource(String dataSourceKey,String sql, Object ... params)\n ','executeSQL','参数:\ndataSourceKey:数据源别名,可通过系统配置-数据源管理新增数据源,请确保数据源拥有更新权限\nsql:SQL 包含绑定的参数\nparams:SQL绑定的入参\n返回值:\nint: 成功更新条数\n警告: 请勿以外部数据源形式更新主数据源正在更新事物中的同一条业务数据,会导致数据库行锁。','2022-12-14 14:47:09','1','','2023-04-12 14:41:45','系统管理员','1'),('1602918114232172772','执行SQL更新语句','sysScript.executeUpdateSql(String sql, Object ... params);\n//用于流程事件中执行sql操作数据库;可根据外键更新指定字段的值\n//String sql = \"update org_user set fullname_ = ? where id_ = ? \";\n//sysScript.executeUpdateSql(sql,\"系统管理员\",\"1\");\n','executeSQL','参数:\nsql:SQL 包含绑定的参数\nparams:SQL绑定的入参\n返回值:\nint : 更新条数','2022-12-14 14:47:09','1','','2023-04-12 14:41:29','系统管理员','1'),('1602918114232172776','获取当前用户姓名','sysScript.getCurrentUserName()','currentUser','获取当前用户姓名','2022-12-14 14:47:09','1','','2023-04-12 14:38:35','系统管理员','1'),('1602918114232172786','获取当前用户组织名称','sysScript.getCurrentGroupName()','currentUser','获取当前用户组织名称','2022-12-14 14:47:09','1','','2023-04-12 14:38:17','系统管理员','1'),('1602918114232172788','获取当前用户信息','sysScript.getCurrentUser().getFullName()','currentUser','获取当前用户信息','2022-12-14 14:47:09','1','','2023-04-12 14:38:07','系统管理员','1'),('1602918114232172791','获取当前组织ID','import com.dstz.base.common.utils.UserContextUtils;\nreturn UserContextUtils.getGroupId();','currentUser','通过引入静态方法来调用系统脚本,这样不需要实现Iscript脚本 。','2022-12-14 14:47:09','1','','2023-04-12 14:37:56','系统管理员','1'),('1602918114232172793','获取当前用户信息','sysScript.getCurrentUser().getFullName()','currentUser','获取用户的某一个属性','2022-12-14 14:47:09','1','','2023-04-12 14:37:41','系统管理员','1'),('1602918114232172797','根据别名获取下一个流水号','sysScript.getNextSerialNo(\"dayNo\");','systemDefault','根据流水号别名获取下一个流水号','2022-12-14 14:47:09','1','','2023-04-12 14:24:30','系统管理员','1'),('1645995316072837120','根据别名和重编规则参数获取下一个流水号','Map paramMap = new HashMap<>();\nparamMap.put(\"prefix\",\"AB-\");\nparamMap.put(\"suffix\",\"-BPM\")\nsysScript.getNextSerialNo(\"monthNum\",paramMap);','systemDefault','参数paramMap支持设置多个重编规则;map-key为流水号重编规则的参数code设置;map-vaule用户根据需求设置','2023-04-12 11:40:39','1','','2023-04-12 14:30:38','系统管理员','1'),('1646038022367137792','根据别名和重编规则参数获取下一个流水号','sysScript.getNextSerialNo(\"codeDayNo\",\'AB-\');','systemDefault','参数param;会把param替换为流水号重编规则的参数code','2023-04-12 14:30:21','1','','2023-04-12 14:30:46','系统管理员','1'); +/*!40000 ALTER TABLE `sys_script` ENABLE KEYS */; +UNLOCK TABLES; + +-- +-- Final view structure for view `bpm_instance_view` +-- + +/*!50001 DROP VIEW IF EXISTS `bpm_instance_view`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 CREATE VIEW `bpm_instance_view` AS select `bpm_definition`.`id_` AS `id_`,`bpm_definition`.`name_` AS `name_`,`bpm_definition`.`key_` AS `key_`,`bpm_definition`.`desc_` AS `desc_`,`sys_connect_record`.`target_id_` AS `target_id_`,`biz_form`.`name_` AS `form_name_` from ((`bpm_definition` left join `sys_connect_record` on((`bpm_definition`.`id_` = `sys_connect_record`.`source_id_`))) left join `biz_form` on((`sys_connect_record`.`target_id_` = `biz_form`.`code_`))) where ((`sys_connect_record`.`type_` = 'BPM-DEF-FORM') and (`bpm_definition`.`is_main_` = 1) and (`sys_connect_record`.`target_id_` like '%_pc')) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; + +-- +-- Final view structure for view `v_sys_tables` +-- + +/*!50001 DROP VIEW IF EXISTS `v_sys_tables`*/; +/*!50001 SET @saved_cs_client = @@character_set_client */; +/*!50001 SET @saved_cs_results = @@character_set_results */; +/*!50001 SET @saved_col_connection = @@collation_connection */; +/*!50001 SET character_set_client = utf8mb4 */; +/*!50001 SET character_set_results = utf8mb4 */; +/*!50001 CREATE VIEW `v_sys_tables` AS select `information_schema`.`t`.`TABLE_NAME` AS `table_name_`,`information_schema`.`t`.`TABLE_COMMENT` AS `table_comment_` from `information_schema`.`TABLES` `t` where ((`information_schema`.`t`.`TABLE_TYPE` = 'BASE TABLE') and (`information_schema`.`t`.`TABLE_SCHEMA` = database())) */; +/*!50001 SET character_set_client = @saved_cs_client */; +/*!50001 SET character_set_results = @saved_cs_results */; +/*!50001 SET collation_connection = @saved_col_connection */; +/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; + +/*!40101 SET SQL_MODE=@OLD_SQL_MODE */; +/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; +/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; +/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; +/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; +/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; +/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..44f12052 --- /dev/null +++ b/pom.xml @@ -0,0 +1,350 @@ + + + 4.0.0 + + com.dstz + agile-bpm + 2.5.0 + pom + + + ab-base + ab-component + ab-org + ab-sys + ab-code-generator + ab-auth + ab-cms + ab-demo + ab-spring-boot + + + + + nexus-aliyun + http://maven.aliyun.com/nexus/content/groups/public/ + + + local-repo + file://${user.dir}/.m2/repository/ + + + + + + nexus-aliyun + http://maven.aliyun.com/nexus/content/groups/public/ + + + + + UTF-8 + 1.8 + 2.6.2 + 3.0.9 + 5.7.19 + 3.5.1 + 31.0.1-jre + 1.0 + 3.8.1 + 3.2.1 + 3.3.1 + 3.2.0 + 3.2.0 + 2.5.2 + 2.8.2 + 3.3.2 + 20041127.091804 + 2.5.0 + 7.1.0.M6 + 2.4 + 1.2.6 + 5.0.0 + 1.6.1 + + + + + + org.springframework.boot + spring-boot-dependencies + ${spring-boot.version} + pom + import + + + + cn.hutool + hutool-core + ${hutool.version} + + + cn.hutool + hutool-crypto + ${hutool.version} + + + cn.hutool + hutool-extra + ${hutool.version} + + + org.apache.poi + poi-ooxml + ${poi.version} + + + cn.hutool + hutool-poi + ${hutool.version} + + + cn.hutool + hutool-cron + ${hutool.version} + + + cn.hutool + hutool-captcha + ${hutool.version} + + + + cn.hutool + hutool-http + ${hutool.version} + + + + com.belerweb + pinyin4j + ${pingyin.version} + + + + com.baomidou + mybatis-plus-boot-starter + ${mybatis-plus.version} + + + + com.google.guava + guava + ${guava.version} + + + + javax.annotation + jsr206-api + ${jsr206-api.version} + + + + + com.dstz + ab-base-api + ${project.version} + + + com.dstz + ab-base-common + ${project.version} + + + com.dstz + ab-org-api + ${project.version} + + + com.dstz + ab-base-mapper + ${project.version} + + + com.dstz + ab-base-web + ${project.version} + + + com.dstz + ab-sys-api + ${project.version} + + + com.dstz + ab-biz-api + ${project.version} + + + com.dstz + ab-groovy-script-api + ${project.version} + + + com.dstz + ab-component-mq-api + ${project.version} + + + com.dstz + ab-component-msg-api + ${project.version} + + + com.dstz + ab-component-upload-api + ${project.version} + + + commons-codec + commons-codec + ${commons-codec.version} + + + + org.activiti + activiti-spring-boot-starter + ${activiti.version} + + + org.mybatis + mybatis + + + org.activiti + activiti-engine + + + + + + org.activiti + activiti-engine + ${activiti.version} + AB + + + org.mybatis + mybatis + + + + + + commons-io + commons-io + ${commons-io.version} + + + + com.alibaba + druid + ${alibaba.druid.version} + + + + dom4j + dom4j + ${dom4j.version} + + + + + + + + + maven-compiler-plugin + ${maven-compiler-plugin.version} + + ${project.build.sourceEncoding} + ${jdk.version} + ${jdk.version} + true + true + + + + + + + org.eclipse.m2e + lifecycle-mapping + 1.0.0 + + + + + + org.apache.felix + maven-bundle-plugin + [2.1.0,) + + manifest + + + + + + + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven-source-plugin.version} + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven-javadoc-plugin.version} + + ${jdk.version} + + + + org.apache.maven.plugins + maven-resources-plugin + ${maven-resources-plugin.version} + + + org.apache.maven.plugins + maven-jar-plugin + ${maven-jar-plugin.version} + + + + true + + + + + + org.apache.maven.plugins + maven-install-plugin + ${maven-install-plugin.version} + + + org.apache.maven.plugins + maven-war-plugin + ${maven-war-plugin.version} + + + org.apache.maven.plugins + maven-deploy-plugin + ${maven-deploy-plugin.version} + + + org.springframework.boot + spring-boot-maven-plugin + ${spring-boot.version} + + + + + +