Browse Source

添加狗的版本

slambb 3 years ago
commit
56dbe8c1ca
100 changed files with 6851 additions and 0 deletions
  1. 3 0
      .gitattributes
  2. 33 0
      .gitignore
  3. 322 0
      mvnw
  4. 182 0
      mvnw.cmd
  5. 167 0
      pom.xml
  6. 215 0
      src/main/java/com/td/boss/BossApplication.java
  7. 21 0
      src/main/java/com/td/boss/annotation/Between.java
  8. 12 0
      src/main/java/com/td/boss/annotation/Decrypt.java
  9. 12 0
      src/main/java/com/td/boss/annotation/Encrypt.java
  10. 16 0
      src/main/java/com/td/boss/annotation/In.java
  11. 13 0
      src/main/java/com/td/boss/annotation/Like.java
  12. 171 0
      src/main/java/com/td/boss/aspect/SafetyAspect.java
  13. 77 0
      src/main/java/com/td/boss/common/controller/CommonController.java
  14. 22 0
      src/main/java/com/td/boss/common/pojo/IpVo.java
  15. 31 0
      src/main/java/com/td/boss/common/pojo/MonitorVo.java
  16. 39 0
      src/main/java/com/td/boss/common/pojo/PageCondition.java
  17. 62 0
      src/main/java/com/td/boss/common/pojo/PageInfo.java
  18. 42 0
      src/main/java/com/td/boss/common/pojo/ParameterRequestWrapper.java
  19. 74 0
      src/main/java/com/td/boss/common/pojo/Result.java
  20. 16 0
      src/main/java/com/td/boss/common/repository/CommonRepository.java
  21. 27 0
      src/main/java/com/td/boss/common/service/CommonService.java
  22. 159 0
      src/main/java/com/td/boss/common/service/CommonServiceImpl.java
  23. 27 0
      src/main/java/com/td/boss/config/async/AsyncConfig.java
  24. 103 0
      src/main/java/com/td/boss/config/enums/ResultEnum.java
  25. 25 0
      src/main/java/com/td/boss/config/exception/AuthorizeException.java
  26. 175 0
      src/main/java/com/td/boss/config/logback/LoggingWSServer.java
  27. 104 0
      src/main/java/com/td/boss/config/monitor/MonitorWSServer.java
  28. 164 0
      src/main/java/com/td/boss/config/security/CaptchaFilterConfig.java
  29. 88 0
      src/main/java/com/td/boss/config/security/DynamicallyUrlInterceptor.java
  30. 55 0
      src/main/java/com/td/boss/config/security/ErrorPageConfig.java
  31. 68 0
      src/main/java/com/td/boss/config/security/LoginFailureHandlerConfig.java
  32. 151 0
      src/main/java/com/td/boss/config/security/LoginSuccessHandlerConfig.java
  33. 37 0
      src/main/java/com/td/boss/config/security/LogoutHandlerConfig.java
  34. 56 0
      src/main/java/com/td/boss/config/security/MyAccessDecisionManager.java
  35. 77 0
      src/main/java/com/td/boss/config/security/MyFilterInvocationSecurityMetadataSource.java
  36. 51 0
      src/main/java/com/td/boss/config/security/MyInvalidSessionStrategy.java
  37. 20 0
      src/main/java/com/td/boss/config/security/PasswordConfig.java
  38. 148 0
      src/main/java/com/td/boss/config/security/SecurityConfig.java
  39. 51 0
      src/main/java/com/td/boss/config/security/UserConfig.java
  40. 51 0
      src/main/java/com/td/boss/config/security/gameConfig.java
  41. 34 0
      src/main/java/com/td/boss/config/token/WebConfig.java
  42. 26 0
      src/main/java/com/td/boss/config/websocket/MyEndpointConfigure.java
  43. 33 0
      src/main/java/com/td/boss/config/websocket/WebSocketConfig.java
  44. 110 0
      src/main/java/com/td/boss/filter/gameFilter.java
  45. 15 0
      src/main/java/com/td/boss/game/comcntorder/controller/ComCntOrderController.java
  46. 33 0
      src/main/java/com/td/boss/game/comcntorder/pojo/ComCntOrder.java
  47. 41 0
      src/main/java/com/td/boss/game/comcntorder/repository/ComCntOrderRepository.java
  48. 26 0
      src/main/java/com/td/boss/game/comcntorder/service/ComCntOrderService.java
  49. 54 0
      src/main/java/com/td/boss/game/comcntorder/service/ComCntOrderServiceImpl.java
  50. 22 0
      src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderDateVo.java
  51. 24 0
      src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderPayTypeSumVo.java
  52. 28 0
      src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderVo.java
  53. 31 0
      src/main/java/com/td/boss/game/comconfigland/controller/ComConfigLandController.java
  54. 54 0
      src/main/java/com/td/boss/game/comconfigland/pojo/ComConfigLand.java
  55. 9 0
      src/main/java/com/td/boss/game/comconfigland/repository/ComConfigLandRepository.java
  56. 13 0
      src/main/java/com/td/boss/game/comconfigland/service/ComConfigLandService.java
  57. 33 0
      src/main/java/com/td/boss/game/comconfigland/service/ComConfigLandServiceImpl.java
  58. 49 0
      src/main/java/com/td/boss/game/comconfigland/vo/ComConfigLandVo.java
  59. 15 0
      src/main/java/com/td/boss/game/comexplainland/controller/ComExplainLandController.java
  60. 47 0
      src/main/java/com/td/boss/game/comexplainland/pojo/ComExplainLand.java
  61. 9 0
      src/main/java/com/td/boss/game/comexplainland/repository/ComExplainLandRepository.java
  62. 8 0
      src/main/java/com/td/boss/game/comexplainland/service/ComExplainLandService.java
  63. 21 0
      src/main/java/com/td/boss/game/comexplainland/service/ComExplainLandServiceImpl.java
  64. 43 0
      src/main/java/com/td/boss/game/comexplainland/vo/ComExplainLandVo.java
  65. 15 0
      src/main/java/com/td/boss/game/comfruit/controller/ComFruitController.java
  66. 30 0
      src/main/java/com/td/boss/game/comfruit/pojo/ComFruit.java
  67. 9 0
      src/main/java/com/td/boss/game/comfruit/repository/ComFruitRepository.java
  68. 13 0
      src/main/java/com/td/boss/game/comfruit/service/ComFruitService.java
  69. 40 0
      src/main/java/com/td/boss/game/comfruit/service/ComFruitServiceImpl.java
  70. 30 0
      src/main/java/com/td/boss/game/comfruit/vo/ComFruitAmountVo.java
  71. 25 0
      src/main/java/com/td/boss/game/comfruit/vo/ComFruitVo.java
  72. 35 0
      src/main/java/com/td/boss/game/commallfood/controller/ComMallFoodController.java
  73. 36 0
      src/main/java/com/td/boss/game/commallfood/pojo/ComMallFood.java
  74. 9 0
      src/main/java/com/td/boss/game/commallfood/repository/ComMallFoodRepository.java
  75. 8 0
      src/main/java/com/td/boss/game/commallfood/service/ComMallFoodService.java
  76. 21 0
      src/main/java/com/td/boss/game/commallfood/service/ComMallFoodServiceImpl.java
  77. 33 0
      src/main/java/com/td/boss/game/commallfood/vo/ComMallFoodSimpleVo.java
  78. 31 0
      src/main/java/com/td/boss/game/commallfood/vo/ComMallFoodVo.java
  79. 34 0
      src/main/java/com/td/boss/game/commallother/controller/ComMallOtherController.java
  80. 36 0
      src/main/java/com/td/boss/game/commallother/pojo/ComMallOther.java
  81. 9 0
      src/main/java/com/td/boss/game/commallother/repository/ComMallOtherRepository.java
  82. 11 0
      src/main/java/com/td/boss/game/commallother/service/ComMallOtherService.java
  83. 21 0
      src/main/java/com/td/boss/game/commallother/service/ComMallOtherServiceImpl.java
  84. 33 0
      src/main/java/com/td/boss/game/commallother/vo/ComMallOtherSimpleVo.java
  85. 31 0
      src/main/java/com/td/boss/game/commallother/vo/ComMallOtherVo.java
  86. 274 0
      src/main/java/com/td/boss/game/commallseed/controller/ComMallSeedController.java
  87. 50 0
      src/main/java/com/td/boss/game/commallseed/pojo/ComMallSeed.java
  88. 9 0
      src/main/java/com/td/boss/game/commallseed/repository/ComMallSeedRepository.java
  89. 14 0
      src/main/java/com/td/boss/game/commallseed/service/ComMallSeedService.java
  90. 41 0
      src/main/java/com/td/boss/game/commallseed/service/ComMallSeedServiceImpl.java
  91. 45 0
      src/main/java/com/td/boss/game/commallseed/vo/ComMallSeedVo.java
  92. 423 0
      src/main/java/com/td/boss/game/complayerdog/controller/ComPlayerDogController.java
  93. 43 0
      src/main/java/com/td/boss/game/complayerdog/pojo/ComPlayerDog.java
  94. 17 0
      src/main/java/com/td/boss/game/complayerdog/repository/ComPlayerDogRepository.java
  95. 14 0
      src/main/java/com/td/boss/game/complayerdog/service/ComPlayerDogService.java
  96. 35 0
      src/main/java/com/td/boss/game/complayerdog/service/ComPlayerDogServiceImpl.java
  97. 45 0
      src/main/java/com/td/boss/game/complayerdog/vo/ComPlayerDogSimpleVo.java
  98. 39 0
      src/main/java/com/td/boss/game/complayerdog/vo/ComPlayerDogVo.java
  99. 1316 0
      src/main/java/com/td/boss/game/complayergoods/controller/ComPlayerGoodsController.java
  100. 31 0
      src/main/java/com/td/boss/game/complayergoods/pojo/ComPlayerGoods.java

+ 3 - 0
.gitattributes

@@ -0,0 +1,3 @@
+*.js linguist-language=Java
+*.css linguist-language=Java
+*.html linguist-language=Java

+ 33 - 0
.gitignore

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

+ 322 - 0
mvnw

@@ -0,0 +1,322 @@
+#!/bin/sh
+# ----------------------------------------------------------------------------
+# Licensed to the Apache Software Foundation (ASF) under one
+# or more contributor license agreements.  See the NOTICE file
+# distributed with this work for additional information
+# regarding copyright ownership.  The ASF licenses this file
+# to you under the Apache License, Version 2.0 (the
+# "License"); you may not use this file except in compliance
+# with the License.  You may obtain a copy of the License at
+#
+#    https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied.  See the License for the
+# specific language governing permissions and limitations
+# under the License.
+# ----------------------------------------------------------------------------
+
+# ----------------------------------------------------------------------------
+# Maven Start Up Batch script
+#
+# Required ENV vars:
+# ------------------
+#   JAVA_HOME - location of a JDK home dir
+#
+# Optional ENV vars
+# -----------------
+#   M2_HOME - location of maven2's installed home dir
+#   MAVEN_OPTS - parameters passed to the Java VM when running Maven
+#     e.g. to debug Maven itself, use
+#       set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+#   MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+# ----------------------------------------------------------------------------
+
+if [ -z "$MAVEN_SKIP_RC" ]; then
+
+  if [ -f /etc/mavenrc ]; then
+    . /etc/mavenrc
+  fi
+
+  if [ -f "$HOME/.mavenrc" ]; then
+    . "$HOME/.mavenrc"
+  fi
+
+fi
+
+# OS specific support.  $var _must_ be set to either true or false.
+cygwin=false
+darwin=false
+mingw=false
+case "$(uname)" in
+CYGWIN*) cygwin=true ;;
+MINGW*) mingw=true ;;
+Darwin*)
+  darwin=true
+  # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home
+  # See https://developer.apple.com/library/mac/qa/qa1170/_index.html
+  if [ -z "$JAVA_HOME" ]; then
+    if [ -x "/usr/libexec/java_home" ]; then
+      export JAVA_HOME="$(/usr/libexec/java_home)"
+    else
+      export JAVA_HOME="/Library/Java/Home"
+    fi
+  fi
+  ;;
+esac
+
+if [ -z "$JAVA_HOME" ]; then
+  if [ -r /etc/gentoo-release ]; then
+    JAVA_HOME=$(java-config --jre-home)
+  fi
+fi
+
+if [ -z "$M2_HOME" ]; then
+  ## resolve links - $0 may be a link to maven's home
+  PRG="$0"
+
+  # need this for relative symlinks
+  while [ -h "$PRG" ]; do
+    ls=$(ls -ld "$PRG")
+    link=$(expr "$ls" : '.*-> \(.*\)$')
+    if expr "$link" : '/.*' >/dev/null; then
+      PRG="$link"
+    else
+      PRG="$(dirname "$PRG")/$link"
+    fi
+  done
+
+  saveddir=$(pwd)
+
+  M2_HOME=$(dirname "$PRG")/..
+
+  # make it fully qualified
+  M2_HOME=$(cd "$M2_HOME" && pwd)
+
+  cd "$saveddir"
+  # echo Using m2 at $M2_HOME
+fi
+
+# For Cygwin, ensure paths are in UNIX format before anything is touched
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=$(cygpath --unix "$M2_HOME")
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=$(cygpath --unix "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=$(cygpath --path --unix "$CLASSPATH")
+fi
+
+# For Mingw, ensure paths are in UNIX format before anything is touched
+if $mingw; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME="$( (
+      cd "$M2_HOME"
+      pwd
+    ))"
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME="$( (
+      cd "$JAVA_HOME"
+      pwd
+    ))"
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  javaExecutable="$(which javac)"
+  if [ -n "$javaExecutable" ] && ! [ "$(expr \"$javaExecutable\" : '\([^ ]*\)')" = "no" ]; then
+    # readlink(1) is not available as standard on Solaris 10.
+    readLink=$(which readlink)
+    if [ ! $(expr "$readLink" : '\([^ ]*\)') = "no" ]; then
+      if $darwin; then
+        javaHome="$(dirname \"$javaExecutable\")"
+        javaExecutable="$(cd \"$javaHome\" && pwd -P)/javac"
+      else
+        javaExecutable="$(readlink -f \"$javaExecutable\")"
+      fi
+      javaHome="$(dirname \"$javaExecutable\")"
+      javaHome=$(expr "$javaHome" : '\(.*\)/bin')
+      JAVA_HOME="$javaHome"
+      export JAVA_HOME
+    fi
+  fi
+fi
+
+if [ -z "$JAVACMD" ]; then
+  if [ -n "$JAVA_HOME" ]; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ]; then
+      # IBM's JDK on AIX uses strange locations for the executables
+      JAVACMD="$JAVA_HOME/jre/sh/java"
+    else
+      JAVACMD="$JAVA_HOME/bin/java"
+    fi
+  else
+    JAVACMD="$(which java)"
+  fi
+fi
+
+if [ ! -x "$JAVACMD" ]; then
+  echo "Error: JAVA_HOME is not defined correctly." >&2
+  echo "  We cannot execute $JAVACMD" >&2
+  exit 1
+fi
+
+if [ -z "$JAVA_HOME" ]; then
+  echo "Warning: JAVA_HOME environment variable is not set."
+fi
+
+CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher
+
+# traverses directory structure from process work directory to filesystem root
+# first directory with .mvn subdirectory is considered project base directory
+find_maven_basedir() {
+
+  if [ -z "$1" ]; then
+    echo "Path not specified to find_maven_basedir"
+    return 1
+  fi
+
+  basedir="$1"
+  wdir="$1"
+  while [ "$wdir" != '/' ]; do
+    if [ -d "$wdir"/.mvn ]; then
+      basedir=$wdir
+      break
+    fi
+    # workaround for JBEAP-8937 (on Solaris 10/Sparc)
+    if [ -d "${wdir}" ]; then
+      wdir=$(
+        cd "$wdir/.."
+        pwd
+      )
+    fi
+    # end of workaround
+  done
+  echo "${basedir}"
+}
+
+# concatenates all lines of a file
+concat_lines() {
+  if [ -f "$1" ]; then
+    echo "$(tr -s '\n' ' ' <"$1")"
+  fi
+}
+
+BASE_DIR=$(find_maven_basedir "$(pwd)")
+if [ -z "$BASE_DIR" ]; then
+  exit 1
+fi
+
+##########################################################################################
+# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+# This allows using the maven wrapper in projects that prohibit checking in binary data.
+##########################################################################################
+if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then
+  if [ "$MVNW_VERBOSE" = true ]; then
+    echo "Found .mvn/wrapper/maven-wrapper.jar"
+  fi
+else
+  if [ "$MVNW_VERBOSE" = true ]; then
+    echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..."
+  fi
+  if [ -n "$MVNW_REPOURL" ]; then
+    jarUrl="$MVNW_REPOURL/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+  else
+    jarUrl="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+  fi
+  while IFS="=" read key value; do
+    case "$key" in wrapperUrl)
+      jarUrl="$value"
+      break
+      ;;
+    esac
+  done <"$BASE_DIR/.mvn/wrapper/maven-wrapper.properties"
+  if [ "$MVNW_VERBOSE" = true ]; then
+    echo "Downloading from: $jarUrl"
+  fi
+  wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar"
+  if $cygwin; then
+    wrapperJarPath=$(cygpath --path --windows "$wrapperJarPath")
+  fi
+
+  if command -v wget >/dev/null; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found wget ... using wget"
+    fi
+    if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+      wget "$jarUrl" -O "$wrapperJarPath"
+    else
+      wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath"
+    fi
+  elif command -v curl >/dev/null; then
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Found curl ... using curl"
+    fi
+    if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then
+      curl -o "$wrapperJarPath" "$jarUrl" -f
+    else
+      curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f
+    fi
+
+  else
+    if [ "$MVNW_VERBOSE" = true ]; then
+      echo "Falling back to using Java to download"
+    fi
+    javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java"
+    # For Cygwin, switch paths to Windows format before running javac
+    if $cygwin; then
+      javaClass=$(cygpath --path --windows "$javaClass")
+    fi
+    if [ -e "$javaClass" ]; then
+      if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo " - Compiling MavenWrapperDownloader.java ..."
+        fi
+        # Compiling the Java class
+        ("$JAVA_HOME/bin/javac" "$javaClass")
+      fi
+      if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then
+        # Running the downloader
+        if [ "$MVNW_VERBOSE" = true ]; then
+          echo " - Running MavenWrapperDownloader.java ..."
+        fi
+        ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR")
+      fi
+    fi
+  fi
+fi
+##########################################################################################
+# End of extension
+##########################################################################################
+
+export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"}
+if [ "$MVNW_VERBOSE" = true ]; then
+  echo $MAVEN_PROJECTBASEDIR
+fi
+MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS"
+
+# For Cygwin, switch paths to Windows format before running java
+if $cygwin; then
+  [ -n "$M2_HOME" ] &&
+    M2_HOME=$(cygpath --path --windows "$M2_HOME")
+  [ -n "$JAVA_HOME" ] &&
+    JAVA_HOME=$(cygpath --path --windows "$JAVA_HOME")
+  [ -n "$CLASSPATH" ] &&
+    CLASSPATH=$(cygpath --path --windows "$CLASSPATH")
+  [ -n "$MAVEN_PROJECTBASEDIR" ] &&
+    MAVEN_PROJECTBASEDIR=$(cygpath --path --windows "$MAVEN_PROJECTBASEDIR")
+fi
+
+# Provide a "standardized" way to retrieve the CLI args that will
+# work with both Windows and non-Windows executions.
+MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@"
+export MAVEN_CMD_LINE_ARGS
+
+WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+exec "$JAVACMD" \
+  $MAVEN_OPTS \
+  -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \
+  "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \
+  ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@"

+ 182 - 0
mvnw.cmd

@@ -0,0 +1,182 @@
+@REM ----------------------------------------------------------------------------
+@REM Licensed to the Apache Software Foundation (ASF) under one
+@REM or more contributor license agreements.  See the NOTICE file
+@REM distributed with this work for additional information
+@REM regarding copyright ownership.  The ASF licenses this file
+@REM to you under the Apache License, Version 2.0 (the
+@REM "License"); you may not use this file except in compliance
+@REM with the License.  You may obtain a copy of the License at
+@REM
+@REM    https://www.apache.org/licenses/LICENSE-2.0
+@REM
+@REM Unless required by applicable law or agreed to in writing,
+@REM software distributed under the License is distributed on an
+@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+@REM KIND, either express or implied.  See the License for the
+@REM specific language governing permissions and limitations
+@REM under the License.
+@REM ----------------------------------------------------------------------------
+
+@REM ----------------------------------------------------------------------------
+@REM Maven Start Up Batch script
+@REM
+@REM Required ENV vars:
+@REM JAVA_HOME - location of a JDK home dir
+@REM
+@REM Optional ENV vars
+@REM M2_HOME - location of maven2's installed home dir
+@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands
+@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending
+@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven
+@REM     e.g. to debug Maven itself, use
+@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000
+@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files
+@REM ----------------------------------------------------------------------------
+
+@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on'
+@echo off
+@REM set title of command window
+title %0
+@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on'
+@if "%MAVEN_BATCH_ECHO%" == "on"  echo %MAVEN_BATCH_ECHO%
+
+@REM set %HOME% to equivalent of $HOME
+if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%")
+
+@REM Execute a user defined script before this one
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre
+@REM check for pre script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat"
+if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd"
+:skipRcPre
+
+@setlocal
+
+set ERROR_CODE=0
+
+@REM To isolate internal variables from possible post scripts, we use another setlocal
+@setlocal
+
+@REM ==== START VALIDATION ====
+if not "%JAVA_HOME%" == "" goto OkJHome
+
+echo.
+echo Error: JAVA_HOME not found in your environment. >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+:OkJHome
+if exist "%JAVA_HOME%\bin\java.exe" goto init
+
+echo.
+echo Error: JAVA_HOME is set to an invalid directory. >&2
+echo JAVA_HOME = "%JAVA_HOME%" >&2
+echo Please set the JAVA_HOME variable in your environment to match the >&2
+echo location of your Java installation. >&2
+echo.
+goto error
+
+@REM ==== END VALIDATION ====
+
+:init
+
+@REM Find the project base dir, i.e. the directory that contains the folder ".mvn".
+@REM Fallback to current working directory if not found.
+
+set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR%
+IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir
+
+set EXEC_DIR=%CD%
+set WDIR=%EXEC_DIR%
+:findBaseDir
+IF EXIST "%WDIR%"\.mvn goto baseDirFound
+cd ..
+IF "%WDIR%"=="%CD%" goto baseDirNotFound
+set WDIR=%CD%
+goto findBaseDir
+
+:baseDirFound
+set MAVEN_PROJECTBASEDIR=%WDIR%
+cd "%EXEC_DIR%"
+goto endDetectBaseDir
+
+:baseDirNotFound
+set MAVEN_PROJECTBASEDIR=%EXEC_DIR%
+cd "%EXEC_DIR%"
+
+:endDetectBaseDir
+
+IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig
+
+@setlocal EnableExtensions EnableDelayedExpansion
+for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a
+@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS%
+
+:endReadAdditionalConfig
+
+SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe"
+set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar"
+set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain
+
+set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+
+FOR /F "tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO (
+    IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B
+)
+
+@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central
+@REM This allows using the maven wrapper in projects that prohibit checking in binary data.
+if exist %WRAPPER_JAR% (
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Found %WRAPPER_JAR%
+    )
+) else (
+    if not "%MVNW_REPOURL%" == "" (
+        SET DOWNLOAD_URL="%MVNW_REPOURL%/io/takari/maven-wrapper/0.5.6/maven-wrapper-0.5.6.jar"
+    )
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Couldn't find %WRAPPER_JAR%, downloading it ...
+        echo Downloading from: %DOWNLOAD_URL%
+    )
+
+    powershell -Command "&{"^
+		"$webclient = new-object System.Net.WebClient;"^
+		"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^
+		"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^
+		"}"^
+		"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^
+		"}"
+    if "%MVNW_VERBOSE%" == "true" (
+        echo Finished downloading %WRAPPER_JAR%
+    )
+)
+@REM End of extension
+
+@REM Provide a "standardized" way to retrieve the CLI args that will
+@REM work with both Windows and non-Windows executions.
+set MAVEN_CMD_LINE_ARGS=%*
+
+%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %*
+if ERRORLEVEL 1 goto error
+goto end
+
+:error
+set ERROR_CODE=1
+
+:end
+@endlocal & set ERROR_CODE=%ERROR_CODE%
+
+if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost
+@REM check for post script, once with legacy .bat ending and once with .cmd ending
+if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat"
+if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd"
+:skipRcPost
+
+@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on'
+if "%MAVEN_BATCH_PAUSE%" == "on" pause
+
+if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE%
+
+exit /B %ERROR_CODE%

+ 167 - 0
pom.xml

@@ -0,0 +1,167 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>2.4.3</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>com.td</groupId>
+    <artifactId>boss</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <name>boss</name>
+    <description>Demo project for Spring Boot</description>
+    <properties>
+        <java.version>1.8</java.version>
+    </properties>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter</artifactId>
+        </dependency>
+
+        <dependency>
+            <groupId>org.projectlombok</groupId>
+            <artifactId>lombok</artifactId>
+            <optional>true</optional>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+		
+		        <!--热部署工具dev-tools-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-devtools</artifactId>
+            <optional>true</optional>
+            <scope>runtime</scope>
+        </dependency>
+
+        <!-- security安全校验 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-security</artifactId>
+        </dependency>
+
+        <!--jwt-->
+        <dependency>
+            <groupId>io.jsonwebtoken</groupId>
+            <artifactId>jjwt</artifactId>
+            <version>0.9.1</version>
+        </dependency>
+
+        <!-- thymeleaf模板 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-thymeleaf</artifactId>
+        </dependency>
+
+        <!--添加springdata-jpa依赖 -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-jpa</artifactId>
+        </dependency>
+
+        <!--添加MySQL驱动依赖 -->
+        <dependency>
+            <groupId>mysql</groupId>
+            <artifactId>mysql-connector-java</artifactId>
+            <version>8.0.13</version>
+        </dependency>
+
+        <!-- CopyUtil需要用到 -->
+        <dependency>
+            <groupId>commons-beanutils</groupId>
+            <artifactId>commons-beanutils</artifactId>
+            <version>1.8.0</version>
+        </dependency>
+        <dependency>
+            <groupId>commons-collections</groupId>
+            <artifactId>commons-collections</artifactId>
+            <version>3.2.2</version>
+        </dependency>
+
+        <!-- springboot websocket -->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-websocket</artifactId>
+        </dependency>
+
+        <!-- Base64编码需要  -->
+        <dependency>
+            <groupId>org.apache.directory.studio</groupId>
+            <artifactId>org.apache.commons.codec</artifactId>
+            <version>1.8</version>
+        </dependency>
+
+        <!--提供更多的加密、填充方式-->
+        <dependency>
+            <groupId>org.bouncycastle</groupId>
+            <artifactId>bcprov-jdk16</artifactId>
+            <version>1.46</version>
+        </dependency>
+
+        <!--aop 面向切面-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-aop</artifactId>
+        </dependency>
+
+        <!--https://github.com/oshi/oshi。-->
+        <dependency>
+            <groupId>com.github.oshi</groupId>
+            <artifactId>oshi-json</artifactId>
+            <version>3.6.1</version>
+        </dependency>
+
+        <dependency>
+            <groupId>com.alibaba</groupId>
+            <artifactId>fastjson</artifactId>
+            <version>1.2.60</version>
+        </dependency>
+
+        <dependency>
+            <groupId>org.apache.commons</groupId>
+            <artifactId>commons-lang3</artifactId>
+            <version>3.9</version>
+        </dependency>
+
+        <!--redis 处理分布式锁等相关问题-->
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-data-redis</artifactId>
+        </dependency>
+
+    </dependencies>
+
+    <build>
+        <finalName>SNTown</finalName>
+        <plugins>
+            <plugin>
+                <groupId>org.springframework.boot</groupId>
+                <artifactId>spring-boot-maven-plugin</artifactId>
+                <configuration>
+                    <excludes>
+                        <exclude>
+                            <groupId>org.projectlombok</groupId>
+                            <artifactId>lombok</artifactId>
+                        </exclude>
+                    </excludes>
+                </configuration>
+            </plugin>
+			<!-- 跳过启动测试 -->
+            <plugin>
+                <groupId>org.apache.maven.plugins</groupId>
+                <artifactId>maven-surefire-plugin</artifactId>
+                <configuration>
+                    <skipTests>true</skipTests>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
+
+</project>

+ 215 - 0
src/main/java/com/td/boss/BossApplication.java

@@ -0,0 +1,215 @@
+package com.td.boss;
+import com.td.boss.game.comusers.pojo.ComUsers;
+import com.td.boss.game.comusers.service.ComUsersService;
+import com.td.boss.sys.sysmenu.vo.SysMenuVo;
+import com.td.boss.sys.syssetting.service.SysSettingService;
+import com.td.boss.sys.syssetting.vo.SysSettingVo;
+import com.td.boss.sys.sysshortcutmenu.service.SysShortcutMenuService;
+import com.td.boss.sys.sysshortcutmenu.vo.SysShortcutMenuVo;
+import com.td.boss.sys.sysuser.service.SysUserService;
+import com.td.boss.sys.sysuser.vo.SysUserVo;
+import com.td.boss.sys.sysusermenu.service.SysUserMenuService;
+import com.td.boss.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.ApplicationRunner;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.scheduling.annotation.EnableAsync;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.session.SessionRegistryImpl;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+import javax.imageio.ImageIO;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.awt.*;
+import java.awt.image.BufferedImage;
+import java.io.IOException;
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+import java.util.List;
+
+@EnableAsync//开启异步调用
+@EnableCaching// 开启缓存,需要显示的指定
+@SpringBootApplication
+public class BossApplication {
+
+    public static void main(String[] args) {
+        SpringApplication.run(BossApplication.class, args);
+    }
+
+    /**
+     * 解决不能注入session注册表问题
+     */
+    @Bean
+    SessionRegistry sessionRegistry() {
+        return new SessionRegistryImpl();
+    }
+}
+
+@Slf4j
+@Controller
+@RequestMapping("/")
+@Configuration
+class IndexController {
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Autowired
+    private SysSettingService sysSettingService;
+
+    @Autowired
+    private SysUserMenuService sysUserMenuService;
+
+    @Autowired
+    private SysShortcutMenuService sysShortcutMenuService;
+
+    @Autowired
+    private ComUsersService comUsersService;
+
+    @Value("${server.servlet.context-path:}")
+    private String contextPath;
+
+
+    /**
+     * 端口
+     */
+    @Value("${server.port}")
+    private String port;
+
+    /**
+     * 启动成功
+     */
+    @Bean
+    public ApplicationRunner applicationRunner() {
+        return applicationArguments -> {
+            try {
+                //系统启动时获取数据库数据,设置到公用静态集合sysSettingMap
+                SysSettingVo sysSettingVo = sysSettingService.get("1").getData();
+                SysSettingUtil.setSysSettingMap(sysSettingVo);
+
+                //获取本机内网IP
+                log.info("启动成功:" + "http://" + InetAddress.getLocalHost().getHostAddress() + ":" + port + contextPath);
+            } catch (UnknownHostException e) {
+                //输出到日志文件中
+                log.error(ErrorUtil.errorInfoToString(e));
+            }
+        };
+    }
+
+    /**
+     * 跳转登录页面
+     */
+    @GetMapping("loginPage")
+    public ModelAndView login(){
+        ModelAndView modelAndView = new ModelAndView("login");
+
+        //系统信息
+        modelAndView.addObject("sys", SysSettingUtil.getSysSetting());
+
+        //后端公钥
+        String publicKey = RsaUtil.getPublicKey();
+        log.info("login后端公钥:" + publicKey);
+        modelAndView.addObject("publicKey", publicKey);
+
+        return modelAndView;
+    }
+
+    /**
+     * 跳转首页
+     */
+    @GetMapping("")
+    public void index1(HttpServletResponse response){
+        //内部重定向
+        try {
+            response.sendRedirect(contextPath+"/index");
+        } catch (IOException e) {
+            //输出到日志文件中
+            log.error(ErrorUtil.errorInfoToString(e));
+        }
+    }
+    @GetMapping("index")
+    public ModelAndView index(){
+        ModelAndView modelAndView = new ModelAndView("index");
+
+        //系统信息
+        modelAndView.addObject("sys", SysSettingUtil.getSysSetting());
+
+        //登录用户
+        SysUserVo sysUserVo = sysUserService.findByLoginName(SecurityUtil.getLoginUser().getUsername()).getData();
+        sysUserVo.setPassword(null);//隐藏部分属性
+        modelAndView.addObject( "loginUser", sysUserVo);
+
+        //登录用户系统菜单
+        List<SysMenuVo> menuVoList = sysUserMenuService.findByUserId(sysUserVo.getUserId()).getData();
+        modelAndView.addObject("menuList",menuVoList);
+
+        //登录用户快捷菜单
+        List<SysShortcutMenuVo> shortcutMenuVoList= sysShortcutMenuService.findByUserId(sysUserVo.getUserId()).getData();
+        modelAndView.addObject("shortcutMenuList",shortcutMenuVoList);
+
+        //后端公钥
+        String publicKey = RsaUtil.getPublicKey();
+        log.info("index后端公钥:" + publicKey);
+        modelAndView.addObject("publicKey", publicKey);
+
+        //获取一个全平台的用户数据
+        Long allComUserCount =  comUsersService.findAllCount();
+        modelAndView.addObject("allComUserCount", allComUserCount);
+
+        Long todayComUserCount = comUsersService.newToday();
+        modelAndView.addObject("todayComUserCount", todayComUserCount);
+
+
+        return modelAndView;
+    }
+
+    /**
+     * 获取验证码图片和文本(验证码文本会保存在HttpSession中)
+     */
+    @RequestMapping("getVerifyCodeImage")
+    public void getVerifyCodeImage(HttpServletRequest request, HttpServletResponse response) throws IOException {
+        //设置页面不缓存
+        response.setHeader("Pragma", "no-cache");
+        response.setHeader("Cache-Control", "no-cache");
+        response.setDateHeader("Expires", 0);
+        response.getOutputStream();
+        String verifyCode = VerifyCodeImageUtil.generateTextCode(VerifyCodeImageUtil.TYPE_NUM_UPPER, 4, null);
+
+        //将验证码放到HttpSession里面
+        request.getSession().setAttribute("verifyCode", verifyCode);
+         log.info("本次生成的验证码为:" + verifyCode + ",已存放到HttpSession中");
+
+        //设置输出的内容的类型为JPEG图像
+        response.setContentType("image/jpeg");
+        BufferedImage bufferedImage = VerifyCodeImageUtil.generateImageCode(verifyCode, 90, 30, 3, true, Color.WHITE, Color.BLACK, null);
+
+        //写给浏览器
+        ImageIO.write(bufferedImage, "JPEG", response.getOutputStream());
+    }
+
+    /**
+     * 跳转实时系统硬件监控
+     */
+    @GetMapping("monitor")
+    public ModelAndView monitor() {
+        return new ModelAndView("monitor.html","port",port);
+    }
+
+    /**
+     * 跳转实时日志
+     */
+    @GetMapping("logging")
+    public ModelAndView logging() {
+        return new ModelAndView("logging.html","port",port);
+    }
+}

+ 21 - 0
src/main/java/com/td/boss/annotation/Between.java

@@ -0,0 +1,21 @@
+package com.td.boss.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Between {
+
+    /**
+     * 最小值的实体属性名
+     */
+    String min();
+
+    /**
+     * 最大值的实体属性名
+     */
+    String max();
+}

+ 12 - 0
src/main/java/com/td/boss/annotation/Decrypt.java

@@ -0,0 +1,12 @@
+package com.td.boss.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Decrypt {
+
+}

+ 12 - 0
src/main/java/com/td/boss/annotation/Encrypt.java

@@ -0,0 +1,12 @@
+package com.td.boss.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target({ElementType.METHOD, ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Encrypt {
+
+}

+ 16 - 0
src/main/java/com/td/boss/annotation/In.java

@@ -0,0 +1,16 @@
+package com.td.boss.annotation;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface In {
+
+    /**
+     * in的具体集合的属性名
+     */
+    String values();
+}

+ 13 - 0
src/main/java/com/td/boss/annotation/Like.java

@@ -0,0 +1,13 @@
+package com.td.boss.annotation;
+
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+@Target(ElementType.FIELD)
+@Retention(RetentionPolicy.RUNTIME)
+public @interface Like {
+
+}

+ 171 - 0
src/main/java/com/td/boss/aspect/SafetyAspect.java

@@ -0,0 +1,171 @@
+package com.td.boss.aspect;
+
+import com.td.boss.annotation.Decrypt;
+import com.td.boss.annotation.Encrypt;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.config.enums.ResultEnum;
+import com.td.boss.config.exception.AuthorizeException;
+import com.td.boss.util.*;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.ExpiredJwtException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.aspectj.lang.JoinPoint;
+import org.aspectj.lang.ProceedingJoinPoint;
+import org.aspectj.lang.annotation.Around;
+import org.aspectj.lang.annotation.Aspect;
+import org.aspectj.lang.annotation.Before;
+import org.aspectj.lang.annotation.Pointcut;
+import org.aspectj.lang.reflect.MethodSignature;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.text.SimpleDateFormat;
+
+/**
+ * AES + RSA 加解密AOP处理
+ */
+@Slf4j
+@Aspect
+@Component
+public class SafetyAspect {
+
+    private final JwtTokenUtil jwtTokenUtil;
+    private final String tokenHeader;
+
+    public SafetyAspect(JwtTokenUtil jwtTokenUtil, @Value("${jwt.token}") String tokenHeader) {
+        this.jwtTokenUtil = jwtTokenUtil;
+        this.tokenHeader = tokenHeader;
+    }
+    /**
+     * Pointcut 切入点
+     * 匹配
+     * com.td.boss.sys.*.controller、
+     * com.td.boss.*.controller包下面的所有方法
+     * "execution(public * com.td.boss.*.controller.*.*(..))" && ! execution(public * com.td.boss.game.*.controller.*.*(..))
+     *
+     * 下面需要验证加密
+     *  "execution(public * com.td.boss.game.*.controller.*.*(..))"
+     */
+    @Pointcut(value = "execution(public * com.td.boss.sys.*.controller.*.*(..)) || " +
+            "execution(public * com.td.boss.*.controller.*.*(..))")
+    public void safetyAspect() {}
+
+    /**
+     * 环绕通知
+     */
+    @Around(value = "safetyAspect()")
+    public Object around(ProceedingJoinPoint pjp) {
+       try {
+            log.info("---------------进入aop---------------");
+           //判断api加密开关是否开启
+           if("N".equals(SysSettingUtil.getSysSetting().getSysApiEncrypt())){
+               return pjp.proceed(pjp.getArgs());
+           }
+
+
+            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
+            assert attributes != null;
+            //request对象
+            HttpServletRequest request = attributes.getRequest();
+
+            //http请求方法  post get
+            String httpMethod = request.getMethod().toLowerCase();
+
+            //method方法
+            Method method = ((MethodSignature) pjp.getSignature()).getMethod();
+
+            //method方法上面的注解
+            Annotation[] annotations = method.getAnnotations();
+
+            //方法的形参参数
+            Object[] args = pjp.getArgs();
+
+            //是否有@Decrypt
+            boolean hasDecrypt = false;
+            //是否有@Encrypt
+            boolean hasEncrypt = false;
+            for (Annotation annotation : annotations) {
+                if (annotation.annotationType() == Decrypt.class) {
+                    hasDecrypt = true;
+                }
+                if (annotation.annotationType() == Encrypt.class) {
+                    hasEncrypt = true;
+                }
+            }
+
+            //前端公钥
+            String publicKey = null;
+
+            //jackson
+            ObjectMapper mapper = new ObjectMapper();
+            //jackson 序列化和反序列化 date处理
+            mapper.setDateFormat( new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+
+            //执行方法之前解密,且只拦截post请求
+            if ("post".equals(httpMethod) && hasDecrypt) {
+                //AES加密后的数据
+                String data = request.getParameter("data");
+                //后端RSA公钥加密后的AES的key
+                String aesKey = request.getParameter("aesKey");
+                //前端公钥
+                publicKey = request.getParameter("publicKey");
+
+                log.info("前端公钥:" + publicKey);
+
+                //后端私钥解密的到AES的key
+                byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(aesKey), RsaUtil.getPrivateKey());
+                aesKey = new String(plaintext);
+                log.info("解密出来的AES的key:" + aesKey);
+
+
+                //AES解密得到明文data数据
+                String decrypt = AesUtil.decrypt(data, aesKey);
+                log.info("解密出来的data数据:" + decrypt);
+
+                //设置到方法的形参中,目前只能设置只有一个参数的情况
+                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+                //注:参数最好用Vo对象来接参,单用String来接,args有长度但获取为空,很奇怪不知道为什么
+                if(args.length > 0){
+                    args[0] = mapper.readValue(decrypt, args[0].getClass());
+                }
+            }
+
+            //执行并替换最新形参参数   PS:这里有一个需要注意的地方,method方法必须是要public修饰的才能设置值,private的设置不了
+            Object o = pjp.proceed(args);
+
+            //返回结果之前加密
+            if (hasEncrypt) {
+                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                //每次响应之前随机获取AES的key,加密data数据
+                String key = AesUtil.getKey();
+                log.info("AES的key:" + key);
+                String dataString = mapper.writeValueAsString(o);
+                log.info("需要加密的data数据:" + dataString);
+                String data = AesUtil.encrypt(dataString, key);
+
+                //用前端的公钥来解密AES的key,并转成Base64
+                String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), publicKey));
+
+                //转json字符串并转成Object对象,设置到Result中并赋值给返回值o
+                o = Result.of(mapper.readValue("{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}", Object.class));
+            }
+
+            //返回
+            return o;
+
+        } catch (Throwable e) {
+           //输出到日志文件中
+           log.error(ErrorUtil.errorInfoToString(e));
+            return Result.of(null, false, "加解密异常:\n\t" + e.getMessage());
+        }
+    }
+
+}

+ 77 - 0
src/main/java/com/td/boss/common/controller/CommonController.java

@@ -0,0 +1,77 @@
+package com.td.boss.common.controller;
+
+import com.td.boss.annotation.Decrypt;
+import com.td.boss.annotation.Encrypt;
+import com.td.boss.common.pojo.PageInfo;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.common.service.CommonService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PostMapping;
+
+import java.util.List;
+
+/**
+ * 通用Controller
+ *
+ * @param <V> 实体类Vo
+ * @param <E> 实体类
+ * @param <T> id主键类型
+ */
+public class CommonController<V, E, T> {
+
+    @Autowired
+    private CommonService<V, E, T> commonService;
+
+    /*
+        CRUD、分页、排序测试
+     */
+    @PostMapping("page")
+    @Decrypt
+    @Encrypt
+    public Result<PageInfo<V>> page(V entityVo) {
+        return commonService.page(entityVo);
+    }
+
+    @PostMapping("list")
+    @Decrypt
+    @Encrypt
+    public Result<List<V>> list(V entityVo) {
+        return commonService.list(entityVo);
+    }
+
+    @GetMapping("get/{id}")
+    public Result<V> get(@PathVariable("id") T id) {
+        return commonService.get(id);
+    }
+
+    @PostMapping("save")
+    @Decrypt
+    @Encrypt
+    public Result<V> save(V entityVo) {
+        return commonService.save(entityVo);
+    }
+
+    @DeleteMapping("delete/{id}")
+    public Result<T> delete( @PathVariable("id") T id) {
+        /*
+        批量删除
+        @DeleteMapping("deleteBatch")
+        public Result<T> deleteBatch(@RequestBody List<String> ids){}
+        前端调用:
+        $.ajax({
+            url: ctx + "deleteBatch",
+            type: "DELETE",
+            data: JSON.stringify([id1,id2]),
+            dataType: "JSON",
+            contentType: 'application/json',
+            success: function (data) {
+
+            }
+        });
+         */
+        return commonService.delete(id);
+    }
+}

+ 22 - 0
src/main/java/com/td/boss/common/pojo/IpVo.java

@@ -0,0 +1,22 @@
+package com.td.boss.common.pojo;
+
+import lombok.Data;
+
+/**
+ * ip
+ */
+@Data
+public class IpVo {
+    private String ip;//IP地址
+    private String pro;//省
+    private String proCode;//省编码
+    private String city;//城市
+    private String cityCode;//城市编码
+    private String region;//区
+    private String regionCode;//区编码
+    private String addr;//详细地址 + 运营商
+
+    //主要用于接参,无实际意义
+    private String regionNames;
+    private String err;
+}

+ 31 - 0
src/main/java/com/td/boss/common/pojo/MonitorVo.java

@@ -0,0 +1,31 @@
+package com.td.boss.common.pojo;
+
+import lombok.Data;
+
+/**
+ * 系统监控信息Vo
+ */
+@Data
+public class MonitorVo {
+    private String os;//操作系统
+    private String runTime;//程序启动时间
+    private String jvmJavaVersion;//java版本
+
+    //jvm
+    private String jvmHeapInit;//jvm内存的初始大小
+    private String jvmHeapMax;//jvm最大可用内存量
+    private String jvmHeapUsed;//jvm已使用的内存量
+    private String jvmHeapCommitted;//jvm已申请的内存量
+    private String jvmNonHeapInit;//jvm内存的初始大小
+    private String jvmNonHeapMax;//jvm最大可用内存量
+    private String jvmNonHeapUsed;//jvm已使用的内存量
+    private String jvmNonHeapCommitted;//jvm已申请的内存量
+
+    //硬件信息
+    private String cpuInfo;//CPU信息
+    private String cpuUseRate;//CPU使用率
+    private String ramTotal;//系统内存总量
+    private String ramUsed;//已使用的系统内存量
+    private String diskTotal;//系统磁盘总量
+    private String diskUsed;//已使用的系统磁盘量
+}

+ 39 - 0
src/main/java/com/td/boss/common/pojo/PageCondition.java

@@ -0,0 +1,39 @@
+package com.td.boss.common.pojo;
+
+import lombok.Data;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.domain.Sort.Direction;
+import org.thymeleaf.util.StringUtils;
+
+/**
+ * 分页条件(参考JqGrid插件)
+ */
+@Data
+public class PageCondition {
+    private int page = 1;//当前页码
+    private int rows = 10;//页面大小
+    private String sidx;//排序字段
+    private String sord;//排序方式
+
+    /**
+     * 获取JPA的分页查询对象
+     */
+    public Pageable getPageable() {
+        //处理非法页码
+        if (page < 0) {
+            page = 1;
+        }
+        //处理非法页面大小
+        if (rows < 0) {
+            rows = 10;
+        }
+        //处理排序
+        if(!StringUtils.isEmpty(sidx) && !StringUtils.isEmpty(sord)){
+            Direction direction = "desc".equals(sidx.toLowerCase()) ? Direction.DESC : Direction.ASC;
+            return PageRequest.of(page - 1, rows, Sort.by(direction, sord));
+        }
+        return PageRequest.of(page - 1, rows);
+    }
+}

+ 62 - 0
src/main/java/com/td/boss/common/pojo/PageInfo.java

@@ -0,0 +1,62 @@
+package com.td.boss.common.pojo;
+
+import com.td.boss.util.CopyUtil;
+import lombok.Data;
+import org.hibernate.query.internal.NativeQueryImpl;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.repository.support.PageableExecutionUtils;
+
+import javax.persistence.EntityManager;
+import javax.persistence.Query;
+import java.util.List;
+
+/**
+ * 分页对象(参考JqGrid插件)
+ */
+@Data
+public class PageInfo<M> {
+    private int page;//当前页码
+    private int pageSize;//页面大小
+    private String sidx;//排序字段
+    private String sord;//排序方式
+
+    private List<M> rows;//分页结果
+    private int records;//总记录数
+    private int total;//总页数
+
+    /**
+     * 获取统一分页结果
+     */
+    public static <M> PageInfo<M> of(Page page, Class<M> entityModelClass) {
+        int records = (int) page.getTotalElements();
+        int pageSize = page.getSize();
+        int total = records % pageSize == 0 ? records / pageSize : records / pageSize + 1;
+
+        PageInfo<M> pageInfo = new PageInfo<>();
+        pageInfo.setPage(page.getNumber() + 1);//页码
+        pageInfo.setPageSize(pageSize);//页面大小
+        pageInfo.setRows(CopyUtil.copyList(page.getContent(), entityModelClass));//分页结果
+        pageInfo.setRecords(records);//总记录数
+        pageInfo.setTotal(total);//总页数
+        return pageInfo;
+    }
+
+    /**
+     * 获取JPA的分页对象
+     */
+    public static Page getJPAPage(Query query, PageRequest pageRequest, EntityManager em) {
+        query.setFirstResult((int) pageRequest.getOffset());
+        query.setMaxResults(pageRequest.getPageSize());
+
+        //获取分页结果
+        return PageableExecutionUtils.getPage(query.getResultList(), pageRequest, () -> {
+            //设置countQuerySQL语句
+            Query countQuery = em.createNativeQuery("select count(*) from ( " + ((NativeQueryImpl) query).getQueryString() + " ) count_table");
+            //设置countQuerySQL参数
+            query.getParameters().forEach(parameter -> countQuery.setParameter(parameter.getName(), query.getParameterValue(parameter.getName())));
+            //返回一个总数
+            return Long.valueOf(countQuery.getResultList().get(0).toString());
+        });
+    }
+}

+ 42 - 0
src/main/java/com/td/boss/common/pojo/ParameterRequestWrapper.java

@@ -0,0 +1,42 @@
+package com.td.boss.common.pojo;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletRequestWrapper;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * 重写一个RequestWrapper,实现可修改Parameter的值
+ */
+public class ParameterRequestWrapper extends HttpServletRequestWrapper {
+
+    private Map<String , String[]> params = new HashMap<String, String[]>();
+
+    public ParameterRequestWrapper(HttpServletRequest request) {
+        super(request);
+        this.params.putAll(request.getParameterMap());
+    }
+
+    //重写getParameter,从当前类中的map获取(查看UsernamePasswordAuthenticationFilter可知)
+    @Override
+    public String getParameter(String name) {
+        String[]values = params.get(name);
+        if(values == null || values.length == 0) {
+            return null;
+        }
+        return values[0];
+    }
+
+    //增加参数
+    public void addParameter(String name, Object value) {
+        if(value != null) {
+            if(value instanceof String[]) {
+                params.put(name , (String[])value);
+            }else if(value instanceof String) {
+                params.put(name , new String[] {(String)value});
+            }else {
+                params.put(name , new String[] {String.valueOf(value)});
+            }
+        }
+    }
+}

+ 74 - 0
src/main/java/com/td/boss/common/pojo/Result.java

@@ -0,0 +1,74 @@
+package com.td.boss.common.pojo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+/**
+ * 统一返回对象
+ */
+
+@Data
+public class Result<T> implements Serializable {
+    /**
+     * 通信数据
+     */
+    private T data;
+    /**
+     * 通信状态
+     */
+    private boolean flag = true;
+    /**
+     * 通信描述
+     */
+    private String msg = "操作成功";
+
+    /**
+     * 通信code
+     */
+    private Integer code = 0;
+
+    /**
+     * 通过静态方法获取实例
+     */
+    public static <T> Result<T> of(T data) {
+        return new Result<>(data);
+    }
+
+    public static <T> Result<T> of(T data, boolean flag) {
+        return new Result<>(data, flag);
+    }
+
+    public static <T> Result<T> of(T data, boolean flag, String msg) {
+        return new Result<>(data, flag, msg);
+    }
+    public static <T> Result<T> of(T data, boolean flag, String msg,Integer code) {
+        return new Result<>(data, flag, msg,code);
+    }
+    @Deprecated
+    public Result() {
+
+    }
+
+    private Result(T data) {
+        this.data = data;
+    }
+
+    private Result(T data, boolean flag) {
+        this.data = data;
+        this.flag = flag;
+    }
+
+    private Result(T data, boolean flag, String msg) {
+        this.data = data;
+        this.flag = flag;
+        this.msg = msg;
+    }
+
+    private Result(T data, boolean flag, String msg ,Integer code) {
+        this.data = data;
+        this.flag = flag;
+        this.msg = msg;
+        this.code = code;
+    }
+}

+ 16 - 0
src/main/java/com/td/boss/common/repository/CommonRepository.java

@@ -0,0 +1,16 @@
+package com.td.boss.common.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.repository.NoRepositoryBean;
+
+/**
+ * 通用Repository
+ *
+ * @param <E> 实体类
+ * @param <T> id主键类型
+ */
+@NoRepositoryBean
+public interface CommonRepository<E,T> extends JpaRepository<E,T>, JpaSpecificationExecutor<E> {
+
+}

+ 27 - 0
src/main/java/com/td/boss/common/service/CommonService.java

@@ -0,0 +1,27 @@
+package com.td.boss.common.service;
+
+
+import com.td.boss.common.pojo.PageInfo;
+import com.td.boss.common.pojo.Result;
+
+import java.util.List;
+
+/**
+ * 通用Service
+ *
+ * @param <V> 实体类Vo
+ * @param <E> 实体类
+ * @param <T> id主键类型
+ */
+public interface CommonService<V, E,T> {
+
+    Result<PageInfo<V>> page(V entityVo);
+
+    Result<List<V>> list(V entityVo);
+
+    Result<V> get(T id);
+
+    Result<V> save(V entityVo);
+
+    Result<T> delete(T id);
+}

+ 159 - 0
src/main/java/com/td/boss/common/service/CommonServiceImpl.java

@@ -0,0 +1,159 @@
+package com.td.boss.common.service;
+
+import com.td.boss.common.pojo.PageCondition;
+import com.td.boss.common.pojo.PageInfo;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.common.repository.CommonRepository;
+import com.td.boss.util.CopyUtil;
+import com.td.boss.util.ErrorUtil;
+import com.td.boss.util.UUIDUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.hibernate.annotations.NotFound;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Example;
+import org.springframework.data.domain.Page;
+import org.springframework.util.StringUtils;
+
+import javax.persistence.Id;
+import java.lang.reflect.Field;
+import java.lang.reflect.ParameterizedType;
+import java.lang.reflect.Type;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+/**
+ * 通用Service实现类
+ *
+ * @param <V> 实体类Vo
+ * @param <E> 实体类
+ * @param <T> id主键类型
+ */
+@Slf4j
+public class CommonServiceImpl<V, E, T> implements CommonService<V, E, T> {
+
+    private Class<V> entityVoClass;//实体类Vo
+
+    private Class<E> entityClass;//实体类
+
+    @Autowired
+    private CommonRepository<E, T> commonRepository;//注入实体类仓库
+
+    public CommonServiceImpl() {
+        Type[] types = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
+        this.entityVoClass = (Class<V>) types[0];
+        this.entityClass = (Class<E>) types[1];
+    }
+
+    @Override
+    public Result<PageInfo<V>> page(V entityVo) {
+        //实体类缺失分页信息
+        if (!(entityVo instanceof PageCondition)) {
+            throw new RuntimeException("实体类" + entityVoClass.getName() + "未继承PageCondition。");
+        }
+        PageCondition pageCondition = (PageCondition) entityVo;
+        Page<E> page = commonRepository.findAll(Example.of(CopyUtil.copy(entityVo, entityClass)), pageCondition.getPageable());
+        return Result.of(PageInfo.of(page, entityVoClass));
+    }
+
+    @Override
+    public Result<List<V>> list(V entityVo) {
+        List<E> entityList = commonRepository.findAll(Example.of(CopyUtil.copy(entityVo, entityClass)));
+        List<V> entityModelList = CopyUtil.copyList(entityList, entityVoClass);
+        return Result.of(entityModelList);
+    }
+
+    @Override
+    public Result<V> get(T id) {
+        Optional<E> optionalE = commonRepository.findById(id);
+        if (!optionalE.isPresent()) {
+            return Result.of(null,false,"ID不存在!");
+        }
+        return Result.of(CopyUtil.copy(optionalE.get(), entityVoClass));
+    }
+
+    @Override
+    public Result<V> save(V entityVo) {
+        //传进来的对象(属性可能残缺)
+        E entity = CopyUtil.copy(entityVo, entityClass);
+
+        //最终要保存的对象
+        E entityFull = entity;
+
+        //为空的属性值,忽略属性,BeanUtils复制的时候用到
+        List<String> ignoreProperties = new ArrayList<String>();
+
+        //获取最新数据,解决部分更新时jpa其他字段设置null问题
+        try {
+            //新增 true,更新 false,要求实体类的Id属性排在第一位,因为for循环读取是按照顺序的
+            boolean isInsert = false;
+
+            //反射获取Class的属性(Field表示类中的成员变量)
+            for (Field field : entity.getClass().getDeclaredFields()) {
+                //获取授权
+                field.setAccessible(true);
+                //属性名称
+                String fieldName = field.getName();
+                //属性的值
+                Object fieldValue = field.get(entity);
+
+                //找出Id主键
+                if (field.isAnnotationPresent(Id.class)) {
+                    if(!StringUtils.isEmpty(fieldValue)){
+                        //如果Id主键不为空,则为更新
+                        Optional<E> one = commonRepository.findById((T) fieldValue);
+                        if (one.isPresent()) {
+                            entityFull = one.get();
+                        }
+                    }else{
+                        //如果Id主键为空,则为新增
+                        fieldValue = UUIDUtil.getUUID();
+                        //set方法,第一个参数是对象
+                        field.set(entity, fieldValue);
+                        isInsert = true;
+                    }
+                }
+                //如果前端不传这两个值,后台来维护创建时间、更新时间
+                if(isInsert && "createTime".equals(fieldName) && StringUtils.isEmpty(fieldValue)){
+                    //先赋值给fieldValue,以免后续进行copy对象判断属性是否为忽略属性是出错
+                    fieldValue = new Date();
+
+                    //set方法,第一个参数是对象
+                    field.set(entity, fieldValue);
+                }
+                if("updateTime".equals(fieldName) && StringUtils.isEmpty(fieldValue)){
+                    //先赋值给fieldValue,以免后续进行copy对象判断属性是否为忽略属性是出错
+                    fieldValue = new Date();
+
+                    //set方法,第一个参数是对象
+                    field.set(entity, fieldValue);
+                }
+
+                //找出值为空的属性,值为空则为忽略属性,或者被NotFound标注,我们复制的时候不进行赋值
+                if(null == fieldValue || field.isAnnotationPresent(NotFound.class)){
+                    ignoreProperties.add(fieldName);
+                }
+            }
+            /*
+                org.springframework.beans BeanUtils.copyProperties(A,B); 是A中的值付给B
+                org.apache.commons.beanutils; BeanUtils.copyProperties(A,B);是B中的值付给A
+                把entity的值赋给entityFull,第三个参数是忽略属性,表示不进行赋值
+             */
+            BeanUtils.copyProperties(entity, entityFull, ignoreProperties.toArray(new String[0]));
+        } catch (IllegalAccessException e) {
+            //输出到日志文件中
+            log.error(ErrorUtil.errorInfoToString(e));
+        }
+
+        E e = commonRepository.save(entityFull);
+        return Result.of(CopyUtil.copy(e, entityVoClass));
+    }
+
+    @Override
+    public Result<T> delete(T id) {
+        commonRepository.deleteById(id);
+        return Result.of(id);
+    }
+}

+ 27 - 0
src/main/java/com/td/boss/config/async/AsyncConfig.java

@@ -0,0 +1,27 @@
+package com.td.boss.config.async;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.task.AsyncTaskExecutor;
+import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
+
+/**
+ * 线程池的配置
+ */
+@Configuration
+public class AsyncConfig {
+
+    private static final int MAX_POOL_SIZE = 50;
+
+    private static final int CORE_POOL_SIZE = 20;
+
+    @Bean("asyncTaskExecutor")
+    public AsyncTaskExecutor asyncTaskExecutor() {
+        ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
+        asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
+        asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
+        asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
+        asyncTaskExecutor.initialize();
+        return asyncTaskExecutor;
+    }
+}

+ 103 - 0
src/main/java/com/td/boss/config/enums/ResultEnum.java

@@ -0,0 +1,103 @@
+package com.td.boss.config.enums;
+
+import lombok.Getter;
+
+/**
+ * 返回的
+ * @author:slambb
+ * @date:2019/12/5
+ */
+@Getter
+public enum ResultEnum {
+    SUCCESS(0,"成功"),
+    //token数据
+    TOKEN_SUCCESS(300,"用户授权认证通过!"),
+    TOKEN_IS_ILLEGAL(301,"用户授权认证没有通过!客户端请求参数TOKEN信息无效"),
+    TOKEN_DOES_NOT_EXIST(302,"用户授权认证没有通过!客户端请求参数中无TOKEN信息"),
+    TOKEN_OUT_OF_DATE(303,"用户授权认证没有通过!TOKEN过期,重新获取"),
+    TOKEN_DOES_NOT_HAVE_USER_ID(304,"用户授权认证没有通过!TOKEN不存在用户id"),
+    NO_PARAMETERS_CARRIED(305,"输入对应参数"),
+    //setting部分
+    SETTING_IS_NULL(306,"配置信息不存在"),
+    //redis部分
+    REDIS_IS_LOCK(307,"操作过于频繁,稍后再试!"),
+    //用户数据
+    USER_DOES_NOT_EXIST(400,"不能存在用户信息!"),
+    USER_ENERGY_IS_INSUFFICIENT(401,"用户能量不足!"),
+    USER_VERIFICATION_ERROR(402,"签名校验失败"),
+
+    USER_NOT_ADDRESS(403,"用户地址不存在!"),
+    USER_LIMIT_LOGIN(404,"用户限制登录!"),
+
+    USER_LOGIN_LOCK(405,"用户保存繁忙,请重试操作!"),
+    //角色数据
+    PLAYER_DOES_NOT_EXIST(500,"不存在角色信息!"),
+    PLAYER_DOES_NOT_ATTRIBUTES(501,"battle player 数据不存在!"),
+
+    PLAYER_GOODS_LIMIT_ERROR(510,"背包超过预设长度!"),
+    PLAYER_NO_CHEST_INFO(511,"没有对应宝箱类型信息"),
+    PLAYER_NO_EQUIP_TYPE(512,"没有对应装备信息"),
+    PLAYER_GOODS_NO_CHEST(513,"背包没有对应宝箱"),
+    PLAYER_GOODS_NOT_CHEST_TYPE(514,"不是宝箱类型对象"),
+    PLAYER_GOODS_WEAR_NUM_ERROR(515,"请输入对应的参数,isWear:0或者1"),
+    PLAYER_GOODS_NO_QUIP_REQUIRED(516,"不用获取多余装备"),
+
+    PLAYER_GOODS_SEEDS_AMOUNT_ERROR(517,"土地消耗种子数量不足"),
+
+    PLAYER_ATTRIBUTE_STRENGTH_IS_NOT(518,"用户体力不足"),
+
+    PLAYER_GOODS_SAVE_LOCK(520,"操作繁忙,保存背包失败,稍后再试!"),
+    //钱包数据
+    WALLET_CNT_PLAY_ERROR(601,"钱包CNT支付参数数据错误!"),
+    WALLET_SNB_INSUFFICIENT_QUANTITY(602,"snb不足以支付!"),
+    WALLET_SNB_SUCCESS_NOT_ZERO(603,"snb不可操作"),
+    WALLET_SNB_SIGN_ERROR(604,"钱包SNB参数签名错误!"),
+
+    //土地数据
+    LAND_DATA_ERROR(701,"土地数据不能初始化!"),
+    LAND_NOT_LEASE(702,"土地未租赁!"),
+    LAND_IS_LEASE(703,"土地已租赁!"),
+    LAND_IS_PLANT(704,"土地已种植!"),
+    LAND_NOT_PLANT(705,"土地未种植!"),
+    LAND_LEASE_EXPIRED(706,"土地租赁已过期!"),
+    LAND_HARVEST_STOLEN(707,"土地已收成过!"),
+    LAND_STEAL_ALL(708,"土地已无多余收成!"),
+    LAND_STEAL_LOCK(709,"当前偷取人数过多,稍后再试!"),
+    LAND_PLANT_FLAG_IS_NULL(710,"土地标识为空,不能偷取"),
+    LAND_CAN_STEAL_IS_ZERO(711,"没有可收取果实的土地不存在!"),
+    LAND_CAN_STEAL_IS_NULL(712,"没有可收取果实的土地!"),
+
+    LAND_CAN_STEAL_IS_MAX(713,"已无多余利润!"),//达到最大值
+    //种子数据不能存在
+    SEED_DATA_ERROR(801,"种子数据不存在!"),
+    FRUIT_DATA_ERROR(802,"果实数据不存在!"),
+    FRUIT_AMOUNT_ERROR(803,"果实数据出错!"),
+    FRUIT_SEND_SELF(804,"不能赠送给自己!"),
+    SEED_DATE_ERROR(805,"未到成熟时间!"),
+    SEED_AMOUNT_ERROR(806,"背包数量不对!"),
+    SEED_PLAY_ERROR(807,"支付价格不对!"),
+    SEED_PLAY_DIAMOND_ERROR(808,"钻石不足以兑换支付!"),
+    SEED_IS_EXCHANGE(809,"种子已经兑换过!"),
+
+    FOOD_DATA_ERROR(810,"食物数据不存在"),
+    FOOD_DOT_NOT(811,"狗粮不足!"),
+
+    SEED_SALE_SAVE_LOCK(810,"操作繁忙,出售失败,稍后再试!"),
+    //装备相关
+    MALL_OTHER_IS_NULL(810,"商城装备不存在!"),
+    MALL_OTHER_IS_HAVE(811,"商城装备已经存在!"),
+    PLAYER_OTHER_IS_NULL(812,"商城装备不存在!"),
+    PLAYER_OTHER_IS_HAVE(813,"商城装备已经存在!"),
+
+
+    FRUIT_AMOUNT_IS_ZERO_AND_NOT_MULTIPLE(808,"请输入出售果实的数量!"),
+    ;
+    private Integer code;
+
+    private String message;
+
+    ResultEnum(Integer code, String message) {
+        this.code = code;
+        this.message = message;
+    }
+}

+ 25 - 0
src/main/java/com/td/boss/config/exception/AuthorizeException.java

@@ -0,0 +1,25 @@
+package com.td.boss.config.exception;
+
+import com.td.boss.config.enums.ResultEnum;
+import lombok.Getter;
+
+/**
+ * @author:slambb
+ * @date:2019/12/13
+ */
+@Getter
+public class AuthorizeException extends RuntimeException {
+
+    private Integer code;
+
+    public AuthorizeException(ResultEnum resultEnum) {
+        super(resultEnum.getMessage());
+
+        this.code = resultEnum.getCode();
+    }
+
+    public AuthorizeException(Integer code, String message) {
+        super(message);
+        this.code = code;
+    }
+}

+ 175 - 0
src/main/java/com/td/boss/config/logback/LoggingWSServer.java

@@ -0,0 +1,175 @@
+package com.td.boss.config.logback;
+
+
+import com.td.boss.config.websocket.MyEndpointConfigure;
+import com.td.boss.util.ErrorUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+import org.thymeleaf.util.StringUtils;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.IOException;
+import java.text.SimpleDateFormat;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * WebSocket获取实时日志并输出到Web页面
+ */
+@Slf4j
+@Component
+@ServerEndpoint(value = "/websocket/logging", configurator = MyEndpointConfigure.class)
+public class LoggingWSServer {
+
+    @Value("${spring.application.name}")
+    private String applicationName;
+
+    /**
+     * 连接集合
+     */
+    private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
+    private static Map<String, Integer> lengthMap = new ConcurrentHashMap<String, Integer>();
+
+    /**
+     * 连接建立成功调用的方法
+     */
+    @OnOpen
+    public void onOpen(Session session) {
+        //添加到集合中
+        sessionMap.put(session.getId(), session);
+        lengthMap.put(session.getId(), 1);//默认从第一行开始
+
+        //获取日志信息
+        new Thread(()->{
+            log.info("LoggingWebSocketServer 任务开始");
+            boolean first = true;
+            BufferedReader reader = null;
+            while (sessionMap.get(session.getId()) != null) {
+                try {
+                    //日志文件,获取最新的
+                    FileReader fileReader = new FileReader(System.getProperty("user.home") + "/log/" + new SimpleDateFormat("yyyyMMdd").format(new Date()) + "/" + applicationName + ".log");
+
+                    //字符流
+                    reader = new BufferedReader(fileReader);
+                    Object[] lines = reader.lines().toArray();
+
+                    //只取从上次之后产生的日志
+                    Object[] copyOfRange = Arrays.copyOfRange(lines, lengthMap.get(session.getId()), lines.length);
+
+                    //对日志进行着色,更加美观  PS:注意,这里要根据日志生成规则来操作
+                    for (int i = 0; i < copyOfRange.length; i++) {
+                        String line = String.valueOf(copyOfRange[i]);
+                        //先转义
+                        line = line.replaceAll("&", "&amp;")
+                                .replaceAll("<", "&lt;")
+                                .replaceAll(">", "&gt;")
+                                .replaceAll("\"", "&quot;");
+
+                        //处理等级
+                        line = line.replace("DEBUG", "<span style='color: blue;'>DEBUG</span>");
+                        line = line.replace("INFO", "<span style='color: green;'>INFO</span>");
+                        line = line.replace("WARN", "<span style='color: orange;'>WARN</span>");
+                        line = line.replace("ERROR", "<span style='color: red;'>ERROR</span>");
+
+                        //处理类名
+                        String[] split = line.split("]");
+                        if (split.length >= 2) {
+                            String[] split1 = split[1].split("-");
+                            if (split1.length >= 2) {
+                                line = split[0] + "]" + "<span style='color: #298a8a;'>" + split1[0] + "</span>" + "-" + split1[1];
+                            }
+                        }
+
+                        // 匹配日期开头加换行,2019-08-12 14:15:04
+                        Pattern r = Pattern.compile("[\\d+][\\d+][\\d+][\\d+]-[\\d+][\\d+]-[\\d+][\\d+] [\\d+][\\d+]:[\\d+][\\d+]:[\\d+][\\d+]");
+                        Matcher m = r.matcher(line);
+                        if (m.find( )) {
+                            //找到下标
+                            int start = m.start();
+                            //插入
+                            StringBuilder  sb = new StringBuilder (line);
+                            sb.insert(start,"<br/><br/>");
+                            line = sb.toString();
+                        }
+
+                        copyOfRange[i] = line;
+                    }
+
+                    //存储最新一行开始
+                    lengthMap.replace(session.getId(), lines.length);
+
+                    //第一次如果太大,截取最新的200行就够了,避免传输的数据太大
+                    if(first && copyOfRange.length > 200){
+                        copyOfRange = Arrays.copyOfRange(copyOfRange, copyOfRange.length - 200, copyOfRange.length);
+                        first = false;
+                    }
+
+                    String result = StringUtils.join(copyOfRange, "<br/>");
+
+                    //发送
+                    send(session, result);
+
+                    //休眠一秒
+                    Thread.sleep(1000);
+                } catch (Exception e) {
+                    //输出到日志文件中
+                    log.error(ErrorUtil.errorInfoToString(e));
+                }
+            }
+            try {
+                reader.close();
+            } catch (IOException e) {
+                //输出到日志文件中
+                log.error(ErrorUtil.errorInfoToString(e));
+            }
+            log.info("LoggingWebSocketServer 任务结束");
+        }).start();
+    }
+
+    /**
+     * 连接关闭调用的方法
+     */
+    @OnClose
+    public void onClose(Session session) {
+        //从集合中删除
+        sessionMap.remove(session.getId());
+        lengthMap.remove(session.getId());
+    }
+
+    /**
+     * 发生错误时调用
+     */
+    @OnError
+    public void onError(Session session, Throwable error) {
+        //输出到日志文件中
+        log.error(ErrorUtil.errorInfoToString(error));
+    }
+
+    /**
+     * 服务器接收到客户端消息时调用的方法
+     */
+    @OnMessage
+    public void onMessage(String message, Session session) {
+
+    }
+
+    /**
+     * 封装一个send方法,发送消息到前端
+     */
+    private void send(Session session, String message) {
+        try {
+            session.getBasicRemote().sendText(message);
+        } catch (Exception e) {
+            //输出到日志文件中
+            log.error(ErrorUtil.errorInfoToString(e));
+        }
+    }
+}

+ 104 - 0
src/main/java/com/td/boss/config/monitor/MonitorWSServer.java

@@ -0,0 +1,104 @@
+package com.td.boss.config.monitor;
+
+
+import com.td.boss.config.websocket.MyEndpointConfigure;
+import com.td.boss.util.ErrorUtil;
+import com.td.boss.util.SystemMonitorUtil;
+import com.fasterxml.jackson.annotation.JsonInclude;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Component;
+
+import javax.websocket.*;
+import javax.websocket.server.ServerEndpoint;
+import java.text.SimpleDateFormat;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * WebSocket获取实时系统监控并输出到Web页面
+ */
+@Slf4j
+@Component
+@ServerEndpoint(value = "/websocket/monitor", configurator = MyEndpointConfigure.class)
+public class MonitorWSServer {
+
+    @Value("${spring.application.name}")
+    private String applicationName;
+
+    /**
+     * 连接集合
+     */
+    private static Map<String, Session> sessionMap = new ConcurrentHashMap<String, Session>();
+
+    /**
+     * 连接建立成功调用的方法
+     */
+    @OnOpen
+    public void onOpen(Session session) {
+        //添加到集合中
+        sessionMap.put(session.getId(), session);
+
+        //获取系统监控信息
+        new Thread(()->{
+            log.info("MonitorWSServer 任务开始");
+            ObjectMapper mapper = new ObjectMapper();
+            //当属性的值为空(null或者"")时,不进行序列化,可以减少数据传输
+            mapper.setSerializationInclusion(JsonInclude.Include.NON_EMPTY);
+            //设置日期格式
+            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+            while (sessionMap.get(session.getId()) != null) {
+                try {
+                    //获取系统监控信息 发送
+                    send(session,  mapper.writeValueAsString(SystemMonitorUtil.getSysMonitor()));
+
+                    //休眠一秒
+                    Thread.sleep(1000);
+                } catch (Exception e) {
+                    //输出到日志文件中
+                    log.error(ErrorUtil.errorInfoToString(e));
+                }
+            }
+            log.info("MonitorWSServer 任务结束");
+        }).start();
+    }
+
+    /**
+     * 连接关闭调用的方法
+     */
+    @OnClose
+    public void onClose(Session session) {
+        //从集合中删除
+        sessionMap.remove(session.getId());
+    }
+
+    /**
+     * 发生错误时调用
+     */
+    @OnError
+    public void onError(Session session, Throwable error) {
+        //输出到日志文件中
+        log.error(ErrorUtil.errorInfoToString(error));
+    }
+
+    /**
+     * 服务器接收到客户端消息时调用的方法
+     */
+    @OnMessage
+    public void onMessage(String message, Session session) {
+
+    }
+
+    /**
+     * 封装一个send方法,发送消息到前端
+     */
+    private void send(Session session, String message) {
+        try {
+            session.getBasicRemote().sendText(message);
+        } catch (Exception e) {
+            //输出到日志文件中
+            log.error(ErrorUtil.errorInfoToString(e));
+        }
+    }
+}

+ 164 - 0
src/main/java/com/td/boss/config/security/CaptchaFilterConfig.java

@@ -0,0 +1,164 @@
+package com.td.boss.config.security;
+
+import com.td.boss.common.pojo.ParameterRequestWrapper;
+import com.td.boss.util.*;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import io.jsonwebtoken.ExpiredJwtException;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.userdetails.UserDetails;
+import org.springframework.security.core.userdetails.UserDetailsService;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.HashMap;
+
+/**
+ * 校验账号、密码前,先进行验证码处理,需要在这里进行登录解密操作
+ */
+@Component
+@Slf4j
+public class CaptchaFilterConfig implements Filter {
+
+    @Value("${captcha.enable}")
+    private Boolean captchaEnable;
+
+    @Value("${server.servlet.context-path:}")
+    private String contextPath;
+
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Autowired
+    private UserConfig userConfig;
+
+    @Autowired
+    private JwtTokenUtil jwtTokenUtil;
+    @Value("${jwt.token}")
+    private String tokenHeader;
+
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        HttpServletRequest request = (HttpServletRequest) servletRequest;
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        HttpSession session = request.getSession();
+
+        /*
+            注:详情可在SessionManagementFilter中进行断点调试查看
+            security框架会在session的attribute存储登录信息,先从session.getAttribute(this.springSecurityContextKey)中获取登录用户信息
+            ,如果没有,再从本地上下文SecurityContextHolder.getContext().getAuthentication()获取,因此想要强制用户下线得进行如下操作
+
+            另外,虽然重启了服务,sessionRegistry.getAllSessions()为空,但之前的用户session未过期同样能访问系统,也是这个原因
+         */
+        SessionInformation sessionInformation = sessionRegistry.getSessionInformation(session.getId());
+        if (sessionInformation == null && session.getAttribute("SPRING_SECURITY_CONTEXT") != null) {
+            //直接输出js脚本跳转强制用户下线
+            response.setContentType("text/html;charset=UTF-8");
+            response.getWriter().print("<script type='text/javascript'>window.location.href = '" + contextPath + "/logout'</script>");
+        }
+
+        //只拦截登录请求,且开发环境下不拦截
+        if ("POST".equals(request.getMethod()) && "/login".equals(request.getRequestURI().replaceFirst(contextPath, ""))) {
+            //前端公钥
+            String publicKey = null;
+
+            //jackson
+            ObjectMapper mapper = new ObjectMapper();
+            //jackson 序列化和反序列化 date处理
+            mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+
+            //判断api加密开关是否开启
+            if ("Y".equals(SysSettingUtil.getSysSetting().getSysApiEncrypt())) {
+                //解密
+                try {
+                    //AES加密后的数据
+                    String data = request.getParameter("data");
+                    //后端RSA公钥加密后的AES的key
+                    String aesKey = request.getParameter("aesKey");
+                    //前端公钥
+                    publicKey = request.getParameter("publicKey");
+
+                    log.info("前端公钥:" + publicKey);
+
+                    //后端私钥解密的到AES的key
+                    byte[] plaintext = RsaUtil.decryptByPrivateKey(Base64.decodeBase64(aesKey), RsaUtil.getPrivateKey());
+                    aesKey = new String(plaintext);
+                    log.info("解密出来的AES的key:" + aesKey);
+
+
+                    //AES解密得到明文data数据
+                    String decrypt = AesUtil.decrypt(data, aesKey);
+                    log.info("解密出来的data数据:" + decrypt);
+
+                    //设置到方法的形参中,目前只能设置只有一个参数的情况
+                    mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+
+                    //new一个自定义RequestWrapper
+                    HashMap hashMap = mapper.readValue(decrypt, HashMap.class);
+                    ParameterRequestWrapper parameterRequestWrapper = new ParameterRequestWrapper(request);
+                    for (Object key : hashMap.keySet()) {
+                        parameterRequestWrapper.addParameter(String.valueOf(key), hashMap.get(key));
+                    }
+
+                    servletRequest = parameterRequestWrapper;
+                    request = (HttpServletRequest) servletRequest;
+                } catch (Throwable e) {
+                    //输出到日志文件中
+                    log.error(ErrorUtil.errorInfoToString(e));
+                }
+            }
+
+            //从session中获取生成的验证码
+            String verifyCode = session.getAttribute("verifyCode").toString();
+
+            if (captchaEnable && !verifyCode.toLowerCase().equals(request.getParameter("captcha").toLowerCase())) {
+                String dataString = "{\"code\":\"400\",\"msg\":\"验证码错误\"}";
+
+                //判断api加密开关是否开启
+                if ("Y".equals(SysSettingUtil.getSysSetting().getSysApiEncrypt())) {
+                    //加密
+                    try {
+                        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                        //每次响应之前随机获取AES的key,加密data数据
+                        String key = AesUtil.getKey();
+                        log.info("AES的key:" + key);
+                        log.info("需要加密的data数据:" + dataString);
+                        String data = AesUtil.encrypt(dataString, key);
+
+                        //用前端的公钥来解密AES的key,并转成Base64
+                        String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), publicKey));
+                        dataString = "{\"data\":{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}}";
+                    } catch (Throwable e) {
+                        //输出到日志文件中
+                        log.error(ErrorUtil.errorInfoToString(e));
+                    }
+                }
+
+                //转json字符串并转成Object对象,设置到Result中并赋值给返回值o
+                response.setCharacterEncoding("UTF-8");
+                response.setContentType("application/json; charset=utf-8");
+                PrintWriter out = response.getWriter();
+                out.print(dataString);
+                out.flush();
+                out.close();
+                return;
+            }
+        }
+
+        filterChain.doFilter(servletRequest, servletResponse);
+    }
+}

+ 88 - 0
src/main/java/com/td/boss/config/security/DynamicallyUrlInterceptor.java

@@ -0,0 +1,88 @@
+package com.td.boss.config.security;
+
+import org.springframework.security.access.AccessDecisionManager;
+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;
+
+/**
+ * 自定义动态数据拦截器
+ */
+public class DynamicallyUrlInterceptor extends AbstractSecurityInterceptor implements Filter {
+
+    //标记自定义的url拦截器已经加载
+    private static final String FILTER_APPLIED = "__spring_security_filterSecurityInterceptor_filterApplied_dynamically";
+
+    private FilterInvocationSecurityMetadataSource securityMetadataSource;
+    private boolean observeOncePerRequest = true;
+
+
+    @Override
+    public Class<?> getSecureObjectClass() {
+        return FilterInvocation.class;
+    }
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+        FilterInvocation fi = new FilterInvocation(request, response, chain);
+        invoke(fi);
+    }
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+
+    @Override
+    public void destroy() {
+    }
+
+    public FilterInvocationSecurityMetadataSource getSecurityMetadataSource() {
+        return this.securityMetadataSource;
+    }
+
+    public SecurityMetadataSource obtainSecurityMetadataSource() {
+        return this.securityMetadataSource;
+    }
+
+    public void setSecurityMetadataSource(FilterInvocationSecurityMetadataSource newSource) {
+        this.securityMetadataSource = newSource;
+    }
+
+    @Override
+    public void setAccessDecisionManager(AccessDecisionManager accessDecisionManager) {
+        super.setAccessDecisionManager(accessDecisionManager);
+    }
+
+    public void invoke(FilterInvocation fi) throws IOException, ServletException {
+
+        if ((fi.getRequest() != null)
+                && (fi.getRequest().getAttribute(FILTER_APPLIED) != null)
+                && observeOncePerRequest) {
+            // filter already applied to this request and user wants us to observe
+            // once-per-request handling, so don't re-do security checking
+            fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+        }
+        else {
+            // first time this request being called, so perform security checking
+            if (fi.getRequest() != null) {
+                fi.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
+            }
+
+            InterceptorStatusToken token = super.beforeInvocation(fi);
+
+            try {
+                fi.getChain().doFilter(fi.getRequest(), fi.getResponse());
+            }
+            finally {
+                super.finallyInvocation(token);
+            }
+
+            super.afterInvocation(token, null);
+        }
+    }
+}

+ 55 - 0
src/main/java/com/td/boss/config/security/ErrorPageConfig.java

@@ -0,0 +1,55 @@
+package com.td.boss.config.security;
+
+import org.springframework.boot.web.server.ErrorPage;
+import org.springframework.boot.web.server.ErrorPageRegistrar;
+import org.springframework.boot.web.server.ErrorPageRegistry;
+import org.springframework.http.HttpStatus;
+import org.springframework.stereotype.Component;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.servlet.ModelAndView;
+
+/**
+ * 自定义errorPage
+ */
+@Component
+public class ErrorPageConfig implements ErrorPageRegistrar {
+
+    @Override
+    public void registerErrorPages(ErrorPageRegistry registry) {
+
+        /**
+         * ErrorPage 有两个参数
+         * 参数1 响应状态码
+         * 参数2 出现响应状态码的时候的跳转路径  可以自定义跳转路径
+         */
+
+        //将ErrorPage 注册到注册器中
+        registry.addErrorPages(
+                new ErrorPage(HttpStatus.FORBIDDEN, "/error/403"),
+                new ErrorPage(HttpStatus.NOT_FOUND, "/error/404"),
+                new ErrorPage(HttpStatus.INTERNAL_SERVER_ERROR, "/error/500")
+                );
+    }
+}
+
+/**
+ * 错误页面跳转
+ */
+@Controller
+class ErrorPageController {
+    @GetMapping("/error/403")
+    public ModelAndView error403(){
+        return new ModelAndView("common/error/403");
+    }
+
+    @GetMapping("/error/404")
+    public ModelAndView error404(){
+        return new ModelAndView("common/error/404");
+    }
+
+    @GetMapping("/error/500")
+    public ModelAndView error500(){
+        return new ModelAndView("common/error/500");
+    }
+}

+ 68 - 0
src/main/java/com/td/boss/config/security/LoginFailureHandlerConfig.java

@@ -0,0 +1,68 @@
+package com.td.boss.config.security;
+
+import com.td.boss.util.AesUtil;
+import com.td.boss.util.ErrorUtil;
+import com.td.boss.util.RsaUtil;
+import com.td.boss.util.SysSettingUtil;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.web.authentication.AuthenticationFailureHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+
+/**
+ * 登录失败处理
+ */
+@Component
+@Slf4j
+public class LoginFailureHandlerConfig implements AuthenticationFailureHandler {
+    @Override
+    public void onAuthenticationFailure(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, AuthenticationException e) throws IOException {
+        String msg = "{\"code\":\"400\",\"msg\":\"用户名或密码错误\"}";
+
+        //判断api加密开关是否开启
+        if("Y".equals(SysSettingUtil.getSysSetting().getSysApiEncrypt())){
+            //加密
+            try {
+                //前端公钥
+                String publicKey = httpServletRequest.getParameter("publicKey");
+
+                log.info("前端公钥:" + publicKey);
+
+                //jackson
+                ObjectMapper mapper = new ObjectMapper();
+                //jackson 序列化和反序列化 date处理
+                mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                //每次响应之前随机获取AES的key,加密data数据
+                String key = AesUtil.getKey();
+                log.info("AES的key:" + key);
+                log.info("需要加密的data数据:" + msg);
+                String data = AesUtil.encrypt(msg, key);
+
+                //用前端的公钥来解密AES的key,并转成Base64
+                String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), publicKey));
+                msg = "{\"data\":{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}}";
+            } catch (Throwable ee) {
+                //输出到日志文件中
+                log.error(ErrorUtil.errorInfoToString(ee));
+            }
+        }
+
+        //转json字符串并转成Object对象,设置到Result中并赋值给返回值o
+        httpServletResponse.setCharacterEncoding("UTF-8");
+        httpServletResponse.setContentType("application/json; charset=utf-8");
+        PrintWriter out = httpServletResponse.getWriter();
+        out.print(msg);
+        out.flush();
+        out.close();
+    }
+}

+ 151 - 0
src/main/java/com/td/boss/config/security/LoginSuccessHandlerConfig.java

@@ -0,0 +1,151 @@
+package com.td.boss.config.security;
+
+import com.td.boss.sys.sysuser.service.SysUserService;
+import com.td.boss.sys.sysuser.vo.SysUserVo;
+import com.td.boss.util.*;
+import com.fasterxml.jackson.databind.DeserializationFeature;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.codec.binary.Base64;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
+import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
+import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.sql.DataSource;
+import java.io.IOException;
+import java.io.PrintWriter;
+import java.text.SimpleDateFormat;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * 登录成功处理,登陆成功后还需要验证账号的有效性
+ */
+@Component
+@Slf4j
+public class LoginSuccessHandlerConfig implements AuthenticationSuccessHandler {
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Autowired
+    private DataSource dataSource;
+
+    @Override
+    public void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
+        //从全局注册表中获取所有登陆用户,做用户单登陆判断
+        ArrayList<String> allSessionIdList = new ArrayList<>();
+        List<SessionInformation> allSessions = sessionRegistry.getAllSessions(authentication.getPrincipal(), false);
+        for (SessionInformation sessionInformation : allSessions) {
+            allSessionIdList.add(sessionInformation.getSessionId());
+        }
+
+        //查询当前与系统交互的用户,存储在本地线程安全上下文,校验账号有效性
+        User user = (User) SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        SysUserVo sysUserVo = sysUserService.findByLoginName(user.getUsername()).getData();
+
+        //默认登陆成功
+        String msg = "{\"code\":\"300\",\"msg\":\"登录成功\",\"url\":\"/index\"}";
+        boolean flag = false;
+
+        //登陆IP不在白名单
+        String ipAddr = IpUtil.getIpAddr(httpServletRequest);
+        String limitedIp = sysUserVo.getLimitedIp();
+        if(!StringUtils.isEmpty(limitedIp) && !Arrays.asList(limitedIp.split(",")).contains(ipAddr)){
+            msg = "{\"code\":\"400\",\"msg\":\"登陆IP不在白名单,请联系管理员\"}";
+            flag = true;
+        }
+
+        //禁止多人在线
+        if("N".equals(sysUserVo.getLimitMultiLogin()) &&  allSessionIdList.size() > 0){
+            msg = "{\"code\":\"400\",\"msg\":\"该账号禁止多人在线,请联系管理员\"}";
+            flag = true;
+        }
+
+        //超出有效时间
+        if(!StringUtils.isEmpty(sysUserVo.getExpiredTime()) && new Date().getTime() > sysUserVo.getExpiredTime().getTime()){
+            msg = "{\"code\":\"400\",\"msg\":\"该账号已失效,请联系管理员\"}";
+            flag = true;
+        }
+
+        //禁止登陆系统
+        if("N".equals(sysUserVo.getValid())){
+            msg = "{\"code\":\"400\",\"msg\":\"该账号已被禁止登陆系统,请联系管理员\"}";
+            flag = true;
+        }
+
+        //校验不通过
+        if(flag){
+            //清除当前的上下文
+            SecurityContextHolder.clearContext();
+
+            //清除remember-me持久化tokens
+            persistentTokenRepository1().removeUserTokens(user.getUsername());
+        }
+        else{
+            //校验通过,注册session
+            sessionRegistry.registerNewSession(httpServletRequest.getSession().getId(),user);
+        }
+
+        //判断api加密开关是否开启
+        if("Y".equals(SysSettingUtil.getSysSetting().getSysApiEncrypt())) {
+            //加密
+            try {
+                //前端公钥
+                String publicKey = httpServletRequest.getParameter("publicKey");
+
+                log.info("前端公钥:" + publicKey);
+
+                //jackson
+                ObjectMapper mapper = new ObjectMapper();
+                //jackson 序列化和反序列化 date处理
+                mapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));
+                mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
+                //每次响应之前随机获取AES的key,加密data数据
+                String key = AesUtil.getKey();
+                log.info("AES的key:" + key);
+                log.info("需要加密的data数据:" + msg);
+                String data = AesUtil.encrypt(msg, key);
+
+                //用前端的公钥来解密AES的key,并转成Base64
+                String aesKey = Base64.encodeBase64String(RsaUtil.encryptByPublicKey(key.getBytes(), publicKey));
+
+                msg = "{\"data\":{\"data\":\"" + data + "\",\"aesKey\":\"" + aesKey + "\"}}";
+            } catch (Throwable e) {
+                //输出到日志文件中
+                log.error(ErrorUtil.errorInfoToString(e));
+            }
+        }
+
+        //转json字符串并转成Object对象,设置到Result中并赋值给返回值o
+        httpServletResponse.setCharacterEncoding("UTF-8");
+        httpServletResponse.setContentType("application/json; charset=utf-8");
+        PrintWriter out = httpServletResponse.getWriter();
+        out.print(msg);
+        out.flush();
+        out.close();
+    }
+
+    @Bean
+    public PersistentTokenRepository persistentTokenRepository1() {
+        JdbcTokenRepositoryImpl persistentTokenRepository = new JdbcTokenRepositoryImpl();
+        persistentTokenRepository.setDataSource(dataSource);
+        return persistentTokenRepository;
+    }
+}

+ 37 - 0
src/main/java/com/td/boss/config/security/LogoutHandlerConfig.java

@@ -0,0 +1,37 @@
+package com.td.boss.config.security;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.Authentication;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.web.authentication.logout.LogoutHandler;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.List;
+
+/**
+ * 注销处理
+ */
+@Component
+public class LogoutHandlerConfig implements LogoutHandler {
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Override
+    public void logout(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) {
+        //剔除退出用户
+        Object principal = SecurityContextHolder.getContext().getAuthentication().getPrincipal();
+        if (principal !=null){
+            List<SessionInformation> allSessions = sessionRegistry.getAllSessions(principal, false);
+            if (allSessions != null) {
+                for (SessionInformation sessionInformation : allSessions) {
+                    sessionInformation.expireNow();
+                    sessionRegistry.removeSessionInformation(sessionInformation.getSessionId());
+                }
+            }
+        }
+    }
+}

+ 56 - 0
src/main/java/com/td/boss/config/security/MyAccessDecisionManager.java

@@ -0,0 +1,56 @@
+package com.td.boss.config.security;
+
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.AccessDeniedException;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.vote.AbstractAccessDecisionManager;
+import org.springframework.security.authentication.InsufficientAuthenticationException;
+import org.springframework.security.core.Authentication;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ * 提供一个认证管理器
+ */
+public class MyAccessDecisionManager extends AbstractAccessDecisionManager {
+
+    MyAccessDecisionManager(List<AccessDecisionVoter<?>> decisionVoters) {
+        super(decisionVoters);
+    }
+
+    @Override
+    public void decide(Authentication authentication, Object object, Collection<ConfigAttribute> configAttributes)
+            throws AccessDeniedException, InsufficientAuthenticationException {
+        int deny = 0;
+
+        for (AccessDecisionVoter voter : getDecisionVoters()) {
+            int result = voter.vote(authentication, object, configAttributes);
+
+            if (logger.isDebugEnabled()) {
+                logger.debug("Voter: " + voter + ", returned: " + result);
+            }
+
+            switch (result) {
+                case AccessDecisionVoter.ACCESS_GRANTED:
+                    return;
+
+                case AccessDecisionVoter.ACCESS_DENIED:
+                    deny++;
+
+                    break;
+
+                default:
+                    break;
+            }
+        }
+
+        if (deny > 0) {
+            throw new AccessDeniedException(messages.getMessage(
+                    "AbstractAccessDecisionManager.accessDenied", "Access is denied"));
+        }
+
+        // To get this far, every AccessDecisionVoter abstained
+        checkAllowIfAllAbstainDecisions();
+    }
+}

+ 77 - 0
src/main/java/com/td/boss/config/security/MyFilterInvocationSecurityMetadataSource.java

@@ -0,0 +1,77 @@
+package com.td.boss.config.security;
+
+import com.td.boss.sys.sysauthority.vo.SysAuthorityVo;
+import org.springframework.security.access.ConfigAttribute;
+import org.springframework.security.access.SecurityConfig;
+import org.springframework.security.web.FilterInvocation;
+import org.springframework.security.web.access.intercept.FilterInvocationSecurityMetadataSource;
+import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
+import org.springframework.security.web.util.matcher.RequestMatcher;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * 配置认证数据源,实现动态权限加载(注意:不要手动new,把它交给spring管理,spring默认单例)
+ */
+@Component
+public class MyFilterInvocationSecurityMetadataSource implements FilterInvocationSecurityMetadataSource {
+    //权限数据
+    private Map<RequestMatcher, Collection<ConfigAttribute>> requestMap;
+
+    /**
+     * 在我们初始化的权限数据中找到对应当前url的权限数据
+     */
+    @Override
+    public Collection<ConfigAttribute> getAttributes(Object object) throws IllegalArgumentException {
+        FilterInvocation fi = (FilterInvocation) object;
+        HttpServletRequest request = fi.getRequest();
+
+        //遍历我们初始化的权限数据,找到对应的url对应的权限
+        for (Map.Entry<RequestMatcher, Collection<ConfigAttribute>> entry : requestMap
+                .entrySet()) {
+            if (entry.getKey().matches(request)) {
+                return entry.getValue();
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public Collection<ConfigAttribute> getAllConfigAttributes() {
+        return null;
+    }
+
+    @Override
+    public boolean supports(Class<?> clazz) {
+        return FilterInvocation.class.isAssignableFrom(clazz);
+    }
+
+    /**
+     * 更新权限集合
+     */
+    public void setRequestMap(List<SysAuthorityVo> authorityVoList){
+        Map<RequestMatcher, Collection<ConfigAttribute>> map = new ConcurrentHashMap<>();
+        for (SysAuthorityVo sysAuthorityVo : authorityVoList) {
+            String authorityName = sysAuthorityVo.getAuthorityName();
+            if (StringUtils.isEmpty(sysAuthorityVo.getAuthorityContent())) continue;
+            for (String url : sysAuthorityVo.getAuthorityContent().split(",")) {
+                Collection<ConfigAttribute> value = map.get(new AntPathRequestMatcher(url));
+                if (StringUtils.isEmpty(value)) {
+                    ArrayList<ConfigAttribute> configs = new ArrayList<>();
+                    configs.add(new SecurityConfig(authorityName));
+                    map.put(new AntPathRequestMatcher(url), configs);
+                } else {
+                    value.add(new SecurityConfig(authorityName));
+                }
+            }
+        }
+        this.requestMap = map;
+    }
+}

+ 51 - 0
src/main/java/com/td/boss/config/security/MyInvalidSessionStrategy.java

@@ -0,0 +1,51 @@
+package com.td.boss.config.security;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.core.session.SessionInformation;
+import org.springframework.security.core.session.SessionRegistry;
+import org.springframework.security.core.userdetails.User;
+import org.springframework.security.web.session.InvalidSessionStrategy;
+import org.springframework.stereotype.Component;
+
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import javax.servlet.http.HttpSession;
+import java.io.IOException;
+
+/**
+ * 自定义session失效策略
+ */
+@Component
+@Slf4j
+public class MyInvalidSessionStrategy implements InvalidSessionStrategy {
+    @Autowired
+    private SessionRegistry sessionRegistry;
+
+    @Value("${server.servlet.context-path:}")
+    private String contextPath;
+
+    @Override
+    public void onInvalidSessionDetected(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws IOException, ServletException {
+        HttpSession session = httpServletRequest.getSession();
+        String sessionId = httpServletRequest.getRequestedSessionId();
+        if(!session.isNew()){
+            //内部重定向
+            httpServletResponse.sendRedirect("/loginPage");
+        }else{
+            //直接输出js脚本跳转
+            httpServletResponse.setContentType("text/html;charset=UTF-8");
+            httpServletResponse.getWriter().print("<script type='text/javascript'>window.location.href = \"" + contextPath + "/loginPage\"</script>");
+        }
+        SessionInformation sessionInformation = sessionRegistry.getSessionInformation(sessionId);
+        if(sessionInformation != null){
+            User user = (User) sessionInformation.getPrincipal();
+            sessionRegistry.removeSessionInformation(sessionId);
+            log.info("剔除过期用户:"+user.getUsername());
+        }
+        log.info("session失效处理 " + sessionRegistry.getAllPrincipals().size()+"");
+        httpServletResponse.flushBuffer();
+    }
+}

+ 20 - 0
src/main/java/com/td/boss/config/security/PasswordConfig.java

@@ -0,0 +1,20 @@
+package com.td.boss.config.security;
+
+import com.td.boss.util.MD5Util;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Component;
+
+@Component
+public class PasswordConfig implements PasswordEncoder {
+
+    @Override
+    public String encode(CharSequence charSequence) {
+        //charSequence是用户输入的密码,password是存库的密码
+        return MD5Util.getMD5(charSequence.toString());
+    }
+
+    @Override
+    public boolean matches(CharSequence charSequence, String password) {
+        return password.contentEquals(encode(charSequence));
+    }
+}

+ 148 - 0
src/main/java/com/td/boss/config/security/SecurityConfig.java

@@ -0,0 +1,148 @@
+package com.td.boss.config.security;
+
+import com.td.boss.sys.sysauthority.service.SysAuthorityService;
+import com.td.boss.sys.sysauthority.vo.SysAuthorityVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.security.access.AccessDecisionVoter;
+import org.springframework.security.access.vote.RoleVoter;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+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.EnableWebSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.security.web.authentication.rememberme.JdbcTokenRepositoryImpl;
+import org.springframework.security.web.authentication.rememberme.PersistentTokenRepository;
+
+import javax.sql.DataSource;
+import java.util.ArrayList;
+import java.util.List;
+
+@EnableWebSecurity
+public class SecurityConfig extends WebSecurityConfigurerAdapter {
+
+    @Autowired
+    private CaptchaFilterConfig captchaFilterConfig;
+
+    @Autowired
+    private UserConfig userConfig;
+
+    @Autowired
+    private PasswordConfig passwordConfig;
+
+    @Autowired
+    private LoginFailureHandlerConfig loginFailureHandlerConfig;
+
+    @Autowired
+    private LoginSuccessHandlerConfig loginSuccessHandlerConfig;
+
+    @Autowired
+    private LogoutHandlerConfig logoutHandlerConfig;
+
+    @Autowired
+    private SysAuthorityService sysAuthorityService;
+
+    @Autowired
+    private MyFilterInvocationSecurityMetadataSource myFilterInvocationSecurityMetadataSource;
+
+    @Autowired
+    private MyInvalidSessionStrategy myInvalidSessionStrategy;
+
+    @Autowired
+    private DataSource dataSource;
+
+    @Override
+    public void configure(WebSecurity web) throws Exception {
+        // game 走 token验证流程
+        web.ignoring().antMatchers("/game/**","/game_websocket/**","/static/**");
+    }
+
+    @Override
+    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
+        auth
+                //用户认证处理
+                .userDetailsService(userConfig)
+                //密码处理
+                .passwordEncoder(passwordConfig);
+    }
+
+    @Override
+    protected void configure(HttpSecurity http) throws Exception {
+        http
+                // 关闭csrf防护
+                .csrf().disable()
+                .headers().frameOptions().disable()
+                .and();
+
+        http
+                //登录处理
+                .addFilterBefore(captchaFilterConfig, UsernamePasswordAuthenticationFilter.class)
+                .formLogin()
+                .loginProcessingUrl("/login")
+                //未登录时默认跳转页面
+                .loginPage("/loginPage")
+                .failureHandler(loginFailureHandlerConfig)
+                .successHandler(loginSuccessHandlerConfig)
+                .permitAll()
+                .and();
+        http
+                //登出处理
+                .logout()
+                .addLogoutHandler(logoutHandlerConfig)
+                .logoutUrl("/logout")
+                .logoutSuccessUrl("/loginPage")
+                .permitAll()
+                .and();
+        http
+                //"play_back"
+                //.mvcMatcher("/game")
+                //定制url访问权限,动态权限读取,参考:https://www.jianshu.com/p/0a06496e75ea
+                .addFilterAfter(dynamicallyUrlInterceptor(), FilterSecurityInterceptor.class)
+                .authorizeRequests()
+                //无需权限访问
+                .antMatchers("/favicon.ico","/common/**", "/webjars/**", "/getVerifyCodeImage","/error/*"
+                ).permitAll()
+                 //其他接口需要登录后才能访问
+                .anyRequest().authenticated()
+                .and();
+        http.sessionManagement()
+                //session无效处理策略
+                .invalidSessionStrategy(myInvalidSessionStrategy)
+                .and();
+        http
+                //开启记住我
+                .rememberMe()
+                .tokenValiditySeconds(604800)//七天免登陆
+                .tokenRepository(persistentTokenRepository())
+                .userDetailsService(userConfig)
+                .and();
+    }
+
+    @Bean
+    public PersistentTokenRepository persistentTokenRepository() {
+        JdbcTokenRepositoryImpl persistentTokenRepository = new JdbcTokenRepositoryImpl();
+        persistentTokenRepository.setDataSource(dataSource);
+        return persistentTokenRepository;
+    }
+
+    //配置filter
+    @Bean
+    public DynamicallyUrlInterceptor dynamicallyUrlInterceptor(){
+        //首次获取
+        List<SysAuthorityVo> authorityVoList = sysAuthorityService.list(new SysAuthorityVo()).getData();
+        myFilterInvocationSecurityMetadataSource.setRequestMap(authorityVoList);
+        //初始化拦截器并添加数据源
+        DynamicallyUrlInterceptor interceptor = new DynamicallyUrlInterceptor();
+        interceptor.setSecurityMetadataSource(myFilterInvocationSecurityMetadataSource);
+
+        //配置RoleVoter决策
+        List<AccessDecisionVoter<? extends Object>> decisionVoters = new ArrayList<>();
+        decisionVoters.add(new RoleVoter());
+
+        //设置认证决策管理器
+        interceptor.setAccessDecisionManager(new MyAccessDecisionManager(decisionVoters));
+        return interceptor;
+    }
+}

+ 51 - 0
src/main/java/com/td/boss/config/security/UserConfig.java

@@ -0,0 +1,51 @@
+package com.td.boss.config.security;
+
+import com.td.boss.sys.sysuser.service.SysUserService;
+import com.td.boss.sys.sysuser.vo.SysUserVo;
+import com.td.boss.sys.sysuserauthority.service.SysUserAuthorityService;
+import com.td.boss.sys.sysuserauthority.vo.SysUserAuthorityVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.authority.AuthorityUtils;
+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.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+@Component
+public class UserConfig implements UserDetailsService {
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Autowired
+    private SysUserAuthorityService sysUserAuthorityService;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        //查询用户
+        SysUserVo sysUserVo = sysUserService.findByLoginName(username).getData();
+        //查询权限
+        List<SysUserAuthorityVo> sysUserAuthorityVoList = sysUserAuthorityService.findByUserId(sysUserVo.getUserId()).getData();
+        StringBuilder authorityList = new StringBuilder();
+        for (int i = 0; i < sysUserAuthorityVoList.size(); i++) {
+            SysUserAuthorityVo sysUserAuthorityVo = sysUserAuthorityVoList.get(i);
+            authorityList.append(sysUserAuthorityVo.getSysAuthority().getAuthorityName());
+            if (i != sysUserAuthorityVoList.size() - 1) {
+                authorityList.append(",");
+            }
+        }
+
+        //查无此用户
+        if(StringUtils.isEmpty(sysUserVo.getUserId())){
+            sysUserVo.setLoginName("查无此用户");
+            sysUserVo.setPassword("查无此用户");
+        }
+
+        // 封装用户信息,并返回。参数分别是:用户名,密码,用户权限
+        return new User(sysUserVo.getLoginName(), sysUserVo.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(authorityList.toString()));
+    }
+}

+ 51 - 0
src/main/java/com/td/boss/config/security/gameConfig.java

@@ -0,0 +1,51 @@
+package com.td.boss.config.security;
+
+import com.td.boss.sys.sysuser.service.SysUserService;
+import com.td.boss.sys.sysuser.vo.SysUserVo;
+import com.td.boss.sys.sysuserauthority.service.SysUserAuthorityService;
+import com.td.boss.sys.sysuserauthority.vo.SysUserAuthorityVo;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.authority.AuthorityUtils;
+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.core.userdetails.UsernameNotFoundException;
+import org.springframework.stereotype.Component;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+
+@Component
+public class gameConfig implements UserDetailsService {
+
+    @Autowired
+    private SysUserService sysUserService;
+
+    @Autowired
+    private SysUserAuthorityService sysUserAuthorityService;
+
+    @Override
+    public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
+        //查询用户
+        SysUserVo sysUserVo = sysUserService.findByLoginName(username).getData();
+        //查询权限
+        List<SysUserAuthorityVo> sysUserAuthorityVoList = sysUserAuthorityService.findByUserId(sysUserVo.getUserId()).getData();
+        StringBuilder authorityList = new StringBuilder();
+        for (int i = 0; i < sysUserAuthorityVoList.size(); i++) {
+            SysUserAuthorityVo sysUserAuthorityVo = sysUserAuthorityVoList.get(i);
+            authorityList.append(sysUserAuthorityVo.getSysAuthority().getAuthorityName());
+            if (i != sysUserAuthorityVoList.size() - 1) {
+                authorityList.append(",");
+            }
+        }
+
+        //查无此用户
+        if(StringUtils.isEmpty(sysUserVo.getUserId())){
+            sysUserVo.setLoginName("查无此用户");
+            sysUserVo.setPassword("查无此用户");
+        }
+
+        // 封装用户信息,并返回。参数分别是:用户名,密码,用户权限
+        return new User(sysUserVo.getLoginName(), sysUserVo.getPassword(), AuthorityUtils.commaSeparatedStringToAuthorityList(authorityList.toString()));
+    }
+}

+ 34 - 0
src/main/java/com/td/boss/config/token/WebConfig.java

@@ -0,0 +1,34 @@
+package com.td.boss.config.token;
+
+import com.td.boss.filter.gameFilter;
+import org.springframework.boot.web.servlet.FilterRegistrationBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.filter.DelegatingFilterProxy;
+
+import javax.servlet.Filter;
+
+/**
+ * @author:slambb
+ * @date:2021/03/27
+ */
+@Configuration
+public class WebConfig {
+
+    @Bean
+    public Filter gameFilter() {
+        return new gameFilter();
+    }
+
+
+    @Bean
+    public FilterRegistrationBean authorizeFilterRegistration() {
+        FilterRegistrationBean registration = new FilterRegistrationBean();
+        registration.setFilter(new DelegatingFilterProxy("gameFilter"));
+        registration.addUrlPatterns("/game/*");
+        registration.setName("gameFilter");
+        registration.setOrder(1);
+        return registration;
+    }
+
+}

+ 26 - 0
src/main/java/com/td/boss/config/websocket/MyEndpointConfigure.java

@@ -0,0 +1,26 @@
+package com.td.boss.config.websocket;
+
+import org.springframework.beans.BeansException;
+import org.springframework.beans.factory.BeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ApplicationContextAware;
+
+import javax.websocket.server.ServerEndpointConfig;
+
+/**
+ * 解决注入其他类的问题,详情参考这篇帖子:webSocket无法注入其他类:https://blog.csdn.net/tornadojava/article/details/78781474
+ */
+public class MyEndpointConfigure extends ServerEndpointConfig.Configurator implements ApplicationContextAware {
+
+    private static volatile BeanFactory context;
+
+    @Override
+    public <T> T getEndpointInstance(Class<T> clazz){
+        return context.getBean(clazz);
+    }
+
+    @Override
+    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
+        MyEndpointConfigure.context = applicationContext;
+    }
+}

+ 33 - 0
src/main/java/com/td/boss/config/websocket/WebSocketConfig.java

@@ -0,0 +1,33 @@
+package com.td.boss.config.websocket;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.socket.config.annotation.AbstractWebSocketMessageBrokerConfigurer;
+import org.springframework.web.socket.server.standard.ServerEndpointExporter;
+
+/**
+ * WebSocket配置
+ */
+@Configuration
+public class WebSocketConfig {
+
+
+    /**
+     * 用途:扫描并注册所有携带@ServerEndpoint注解的实例。 @ServerEndpoint("/websocket")
+     * PS:如果使用外部容器 则无需提供ServerEndpointExporter。
+     */
+    @Bean
+    public ServerEndpointExporter serverEndpointExporter() {
+        return new ServerEndpointExporter();
+    }
+
+    /**
+     * 支持注入其他类
+     */
+    @Bean
+    public MyEndpointConfigure newMyEndpointConfigure (){
+        return new MyEndpointConfigure ();
+    }
+
+
+}

+ 110 - 0
src/main/java/com/td/boss/filter/gameFilter.java

@@ -0,0 +1,110 @@
+package com.td.boss.filter;
+
+
+import com.td.boss.common.pojo.Result;
+import com.td.boss.config.enums.ResultEnum;
+import com.td.boss.util.JsonUtils;
+import com.td.boss.util.JwtTokenUtil;
+import com.td.boss.util.TokenRequestWrapper;
+import io.jsonwebtoken.ExpiredJwtException;
+import io.jsonwebtoken.SignatureException;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.util.StringUtils;
+
+import javax.servlet.*;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+
+/**
+ * @author:slambb
+ * @date:2021/03/27
+ */
+@Slf4j
+public class gameFilter implements Filter {
+    @Value("${jwt.token}")
+    private String tokenHeader;
+
+    @Autowired
+    private JwtTokenUtil jwtTokenUtil;
+
+    @Override
+    public void init(FilterConfig filterConfig) throws ServletException {
+    }
+    @Override
+    public void destroy() {
+    }
+    @Override
+    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
+        //处理跨域请求
+        HttpServletResponse response = (HttpServletResponse) servletResponse;
+        response.setHeader("Access-Control-Allow-Origin", "*");
+        response.setHeader("Access-Control-Allow-Credentials", "true");
+        response.setHeader("Access-Control-Allow-Methods", "POST, GET, PATCH, DELETE, PUT");
+        response.setHeader("Access-Control-Max-Age", "3600");
+        response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept , Authorization");
+
+        log.info("********进入GameFilter过滤器********");
+        HttpServletRequest req = (HttpServletRequest) servletRequest;
+        //String method = req.getMethod();
+        //if (method.equals("OPTIONS")) {
+        //    response.setStatus(200);
+        //    response.flushBuffer();
+        //    return;
+        //}
+        String token = req.getHeader(tokenHeader);
+        String path = req.getServletPath();
+        //登录,获取验证码,不用登录的接口
+        if (!(path.indexOf("game") > -1) // 存在game字段,需要检验token
+                || path.indexOf("loginToken") > -1
+               // || path.indexOf("publicKeyAndSys") > -1
+               // || path.indexOf("playback")>-1
+                || path.indexOf("game_websocket") > -1 // todo 暂时放行
+                //这里三个接口给DApp后台调用
+                || path.indexOf("getSNBAndFreeze") >-1
+                || path.indexOf("setSNBAndUnfreeze") >-1
+                || path.indexOf("cntPlayback") >-1
+           ) {
+            //登录情况直接放行
+            filterChain.doFilter(servletRequest, response);
+            return;
+        }
+        if (null == token || token.isEmpty() || !token.startsWith("Bearer ")) {
+            //没有token信息
+            response.setCharacterEncoding("utf-8");
+            response.getWriter().print( JsonUtils.toJson (Result.of(null,false,ResultEnum.TOKEN_DOES_NOT_EXIST.getMessage(),ResultEnum.TOKEN_DOES_NOT_EXIST.getCode())));
+            return;
+        } else {
+            String authToken = token.substring(7);
+            try {
+                String userId = jwtTokenUtil.getUserIdFromToken(authToken);
+                log.info("userId:{}", userId);
+                //todo 后面需要校验时间,加入可切换 secret 校验
+                log.info("validateToken:{}", jwtTokenUtil.validateToken(authToken, userId));
+                // 加入secret校验
+                if(!jwtTokenUtil.validateToken(authToken, userId)){
+                    log.info("secret 不可靠,validateToken:{}", jwtTokenUtil.validateToken(authToken, userId));
+                }
+                if (!StringUtils.isEmpty(userId)) {
+                    filterChain.doFilter(new TokenRequestWrapper((HttpServletRequest) servletRequest, userId), response);
+                } else {
+                    //不存在用户信息
+                    response.setCharacterEncoding("utf-8");
+                    response.getWriter().print(JsonUtils.toJson(Result.of(authToken,false,ResultEnum.TOKEN_DOES_NOT_HAVE_USER_ID.getMessage(),ResultEnum.TOKEN_DOES_NOT_HAVE_USER_ID.getCode())));
+                }
+            } catch (ExpiredJwtException e) {
+                //token过期
+                response.setCharacterEncoding("utf-8");
+                response.getWriter().print(JsonUtils.toJson(Result.of(null,false,ResultEnum.TOKEN_OUT_OF_DATE.getMessage(), ResultEnum.TOKEN_OUT_OF_DATE.getCode())));
+            } catch (SignatureException e){
+                //secret校验密钥不对
+                response.setCharacterEncoding("utf-8");
+                response.getWriter().print(JsonUtils.toJson(Result.of(authToken,false,ResultEnum.TOKEN_IS_ILLEGAL.getMessage(),ResultEnum.TOKEN_IS_ILLEGAL.getCode())));
+            }
+
+        }
+
+    }
+}

+ 15 - 0
src/main/java/com/td/boss/game/comcntorder/controller/ComCntOrderController.java

@@ -0,0 +1,15 @@
+package com.td.boss.game.comcntorder.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.game.comcntorder.pojo.ComCntOrder;
+import com.td.boss.game.comcntorder.vo.ComCntOrderVo;
+import com.td.boss.game.comcntorder.service.ComCntOrderService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/game/comCntOrder/")
+public class ComCntOrderController extends CommonController<ComCntOrderVo, ComCntOrder, String> {
+    @Autowired
+    private ComCntOrderService comCntOrderService;
+}

+ 33 - 0
src/main/java/com/td/boss/game/comcntorder/pojo/ComCntOrder.java

@@ -0,0 +1,33 @@
+package com.td.boss.game.comcntorder.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_cnt_order")
+@Data
+public class ComCntOrder implements Serializable {
+    @Id
+    private String id;//
+
+    private String userId;//
+
+    private String address;//
+
+    private Integer payType;//
+
+    private String payAmount;//
+
+    private String itemType;
+
+    private String txHash;//
+
+    private String cntDescribe;
+
+    private Date createTime;//创建时间
+
+    private Date updateTime;//修改时间
+
+}

+ 41 - 0
src/main/java/com/td/boss/game/comcntorder/repository/ComCntOrderRepository.java

@@ -0,0 +1,41 @@
+package com.td.boss.game.comcntorder.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.comcntorder.pojo.ComCntOrder;
+import com.td.boss.game.comcntorder.vo.ComCntOrderPayTypeSumVo;
+import com.td.boss.game.comcntorder.vo.ComCntOrderVo;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface ComCntOrderRepository extends CommonRepository<ComCntOrder, String> {
+
+    Optional<ComCntOrder> findByTxHash(String tx);
+
+    List<ComCntOrder> findByUserIdAndPayType(String userId,Integer payType);
+
+    //SELECT pay_type,sum(pay_amount) from com_cnt_order WHERE pay_type = 4
+
+    /**
+     * 返回一个降序的pay_type 统计
+     *
+     * @return
+     */
+    @Query(value = "SELECT new com.td.boss.game.comcntorder.vo.ComCntOrderPayTypeSumVo(u.payType,sum(u.payAmount)) from ComCntOrder u GROUP BY u.payType ORDER BY sum(u.payAmount) desc", nativeQuery = false)
+    List<ComCntOrderPayTypeSumVo> findCNTSumByPayType();
+
+
+    /**
+     * 根据开始结束时间获取payType 统计数据
+     * @param startTime
+     * @param endTime
+     * @return
+     */
+    @Query(value = "SELECT new com.td.boss.game.comcntorder.vo.ComCntOrderPayTypeSumVo(u.payType,sum(u.payAmount)) FROM  ComCntOrder u  where u.createTime >= :startTime and u.createTime<= :endTime GROUP BY u.payType ORDER BY sum(u.payAmount) desc")
+    List<ComCntOrderPayTypeSumVo> findCNTSumByPayTypeFromDate(Date startTime, Date endTime);
+
+}

+ 26 - 0
src/main/java/com/td/boss/game/comcntorder/service/ComCntOrderService.java

@@ -0,0 +1,26 @@
+package com.td.boss.game.comcntorder.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comcntorder.pojo.ComCntOrder;
+import com.td.boss.game.comcntorder.vo.ComCntOrderPayTypeSumVo;
+import com.td.boss.game.comcntorder.vo.ComCntOrderVo;
+
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+public interface ComCntOrderService extends CommonService<ComCntOrderVo, ComCntOrder, String> {
+
+    ComCntOrderVo findByTx(String tx);
+
+    List<ComCntOrder>  findByUserIdAndPayType(String userId,Integer payType);
+
+    //返回一个全部总量
+    List<ComCntOrderPayTypeSumVo> findAllCNTSumGroupByPayType();
+
+    //返回一个日期查找的总量
+    List<ComCntOrderPayTypeSumVo> findAllCNTSumGroupByPayTypeFromDate(Date startTime, Date endTime);
+
+
+
+}

+ 54 - 0
src/main/java/com/td/boss/game/comcntorder/service/ComCntOrderServiceImpl.java

@@ -0,0 +1,54 @@
+package com.td.boss.game.comcntorder.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comcntorder.pojo.ComCntOrder;
+import com.td.boss.game.comcntorder.vo.ComCntOrderPayTypeSumVo;
+import com.td.boss.game.comcntorder.vo.ComCntOrderVo;
+import com.td.boss.game.comcntorder.repository.ComCntOrderRepository;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.Date;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+@Transactional
+public class ComCntOrderServiceImpl extends CommonServiceImpl<ComCntOrderVo, ComCntOrder, String> implements ComCntOrderService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComCntOrderRepository comCntOrderRepository;
+
+
+    @Override
+    public ComCntOrderVo findByTx(String tx) {
+        Optional<ComCntOrder> comCntOrder =  comCntOrderRepository.findByTxHash(tx);
+        if(comCntOrder.orElse(null) == null){
+            return null;
+        }else{
+            return CopyUtil.copy(comCntOrder.get(), ComCntOrderVo.class);
+        }
+    }
+
+    @Override
+    public List<ComCntOrder> findByUserIdAndPayType(String userId, Integer payType) {
+        return comCntOrderRepository.findByUserIdAndPayType(userId,payType);
+    }
+
+    @Override
+    public List<ComCntOrderPayTypeSumVo> findAllCNTSumGroupByPayType() {
+        return comCntOrderRepository.findCNTSumByPayType();
+    }
+
+
+    @Override
+    public List<ComCntOrderPayTypeSumVo> findAllCNTSumGroupByPayTypeFromDate(Date startTime,Date endTime) {
+        return comCntOrderRepository.findCNTSumByPayTypeFromDate(startTime, endTime);
+    }
+
+}

+ 22 - 0
src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderDateVo.java

@@ -0,0 +1,22 @@
+package com.td.boss.game.comcntorder.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+import org.springframework.format.annotation.DateTimeFormat;
+
+import java.util.Date;
+
+/**
+ * @author:slambb
+ * @date:2022/1/25
+ */
+@Data
+public class ComCntOrderDateVo {
+    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
+    private Date startTime;
+    @JsonFormat(locale="zh", timezone="GMT+8", pattern="yyyy-MM-dd HH:mm:ss")
+    private Date endTime;
+}

+ 24 - 0
src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderPayTypeSumVo.java

@@ -0,0 +1,24 @@
+package com.td.boss.game.comcntorder.vo;
+
+import lombok.Data;
+import lombok.Getter;
+import lombok.Setter;
+import lombok.ToString;
+
+/**
+ * @author:slambb
+ * @date:2022/1/25
+ */
+@Getter
+@Setter
+@ToString
+public class ComCntOrderPayTypeSumVo {
+    private Integer payType;
+    private String cntSum;
+
+    public ComCntOrderPayTypeSumVo(Integer payType, String cntSum) {
+        this.payType = payType;
+        this.cntSum = cntSum;
+    }
+
+}

+ 28 - 0
src/main/java/com/td/boss/game/comcntorder/vo/ComCntOrderVo.java

@@ -0,0 +1,28 @@
+package com.td.boss.game.comcntorder.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComCntOrderVo extends PageCondition implements Serializable {
+    private String id;//
+
+    private String userId;//
+
+    private String address;//
+
+    private Integer payType;//
+
+    private String payAmount;//
+
+    private String itemType;
+
+    private String txHash;//
+
+    private String cntDescribe;
+
+    private Date createTime;//创建时间
+
+    private Date updateTime;//修改时间
+}

+ 31 - 0
src/main/java/com/td/boss/game/comconfigland/controller/ComConfigLandController.java

@@ -0,0 +1,31 @@
+package com.td.boss.game.comconfigland.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.game.comconfigland.pojo.ComConfigLand;
+import com.td.boss.game.comconfigland.vo.ComConfigLandVo;
+import com.td.boss.game.comconfigland.service.ComConfigLandService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/game/configLand/")
+public class ComConfigLandController extends CommonController<ComConfigLandVo, ComConfigLand, Integer> {
+    @Autowired
+    private ComConfigLandService comConfigLandService;
+
+
+    /**
+     * 获取土地数据
+     * @param userId
+     * @return
+     */
+    @GetMapping("getList")
+    public Result<List<ComConfigLandVo>> getSNBFunction(@RequestParam(value = "userId") String userId) {
+        ComConfigLandVo comConfigLandVo = new ComConfigLandVo();
+        Result result = comConfigLandService.list(comConfigLandVo);
+        return result;
+    }
+}

+ 54 - 0
src/main/java/com/td/boss/game/comconfigland/pojo/ComConfigLand.java

@@ -0,0 +1,54 @@
+package com.td.boss.game.comconfigland.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_config_land")
+@Data
+public class ComConfigLand implements Serializable {
+    @Id
+    @GeneratedValue(strategy= GenerationType.IDENTITY)
+    private Integer id;//
+
+    private String name;//
+
+    private Integer posX;//
+
+    private Integer posY;//
+
+    private Integer sizeX;//
+
+    private Integer sizeY;//
+
+    private Integer isInit;//是否在游戏中初始化租赁等数据
+
+    private Integer initMultiple;//初始化土地的倍数
+
+    private Integer isLease;//是否租凭
+
+    private Date leaseTime;//租凭时间
+
+    private Double leaseMultiple;//租赁的倍数
+
+    private Integer rental1;//3个月租赁费用CNT
+
+    private Integer rental2;//1年租赁费用CNT
+
+    private Integer rental3;//5年月租赁费用CNT
+
+    private Integer isPlant;//是否种植
+
+    private Date plantStart;//种植开始时间
+
+    private Integer plantMature;//成熟期
+
+    private String landDescribe;//土地描述
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/comconfigland/repository/ComConfigLandRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.comconfigland.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.comconfigland.pojo.ComConfigLand;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComConfigLandRepository extends CommonRepository<ComConfigLand, Integer> {
+}

+ 13 - 0
src/main/java/com/td/boss/game/comconfigland/service/ComConfigLandService.java

@@ -0,0 +1,13 @@
+package com.td.boss.game.comconfigland.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comconfigland.pojo.ComConfigLand;
+import com.td.boss.game.comconfigland.vo.ComConfigLandVo;
+
+import java.util.List;
+
+public interface ComConfigLandService extends CommonService<ComConfigLandVo, ComConfigLand, Integer> {
+    ComConfigLand findById(Integer id);
+
+    List<ComConfigLand> findConfigLandAll();
+}

+ 33 - 0
src/main/java/com/td/boss/game/comconfigland/service/ComConfigLandServiceImpl.java

@@ -0,0 +1,33 @@
+package com.td.boss.game.comconfigland.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comconfigland.pojo.ComConfigLand;
+import com.td.boss.game.comconfigland.vo.ComConfigLandVo;
+import com.td.boss.game.comconfigland.repository.ComConfigLandRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+@Service
+@Transactional
+public class ComConfigLandServiceImpl extends CommonServiceImpl<ComConfigLandVo, ComConfigLand, Integer> implements ComConfigLandService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComConfigLandRepository comConfigLandRepository;
+
+
+    @Override
+    public ComConfigLand findById(Integer id) {
+        return comConfigLandRepository.findById(id).orElse(null);
+    }
+
+    @Override
+    public List<ComConfigLand> findConfigLandAll() {
+        return comConfigLandRepository.findAll();
+    }
+}

+ 49 - 0
src/main/java/com/td/boss/game/comconfigland/vo/ComConfigLandVo.java

@@ -0,0 +1,49 @@
+package com.td.boss.game.comconfigland.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComConfigLandVo  implements Serializable { //extends PageCondition
+    private Integer id;//
+
+    private String name;//
+
+    private Integer posX;//
+
+    private Integer posY;//
+
+    private Integer sizeX;//
+
+    private Integer sizeY;//
+
+    private Integer isInit;//是否在游戏中初始化租赁等数据
+
+    private Integer initMultiple;//初始化土地的倍数
+
+    private Integer isLease;//是否租凭
+
+    private Date leaseTime;//租凭时间
+
+    private Double leaseMultiple;//租赁的倍数
+
+    private Integer rental1;//3个月租赁费用CNT
+
+    private Integer rental2;//1年租赁费用CNT
+
+    private Integer rental3;//5年月租赁费用CNT
+
+    private Integer isPlant;//是否种植
+
+    private Date plantStart;//种植开始时间
+
+    private Integer plantMature;//成熟期
+
+    private String landDescribe;//土地描述
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 15 - 0
src/main/java/com/td/boss/game/comexplainland/controller/ComExplainLandController.java

@@ -0,0 +1,15 @@
+package com.td.boss.game.comexplainland.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.game.comexplainland.pojo.ComExplainLand;
+import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
+import com.td.boss.game.comexplainland.service.ComExplainLandService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/game/comExplainLand/")
+public class ComExplainLandController extends CommonController<ComExplainLandVo, ComExplainLand, String> {
+    @Autowired
+    private ComExplainLandService comExplainLandService;
+}

+ 47 - 0
src/main/java/com/td/boss/game/comexplainland/pojo/ComExplainLand.java

@@ -0,0 +1,47 @@
+package com.td.boss.game.comexplainland.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_explain_land")
+@Data
+public class ComExplainLand implements Serializable {
+    @Id
+    private String eId;//
+
+    private String userId;//
+
+    private Integer landId;//过期的租赁土地的id
+
+    private Date landCreate;//过期的租赁土地时候土地创建的日期
+
+    private Date leaseTime;//过期的土地租赁的时间
+
+    private Integer leaseMultiple;//过期的土地租赁倍数
+
+    private Integer leaseDate;//过期的土地租赁日期,1是3个月,2是1年,3是5年
+
+    private Integer isPlant;//过期的土地种子状态 是否种植:N:0,Y:1
+
+    private Integer plantId;//过期土地的种植id
+
+    private Date plantStart;//过期土地开始种植的时间
+
+    private Integer plantMature;//过期土地种植成熟期
+
+    private String landDescribe;//过期土地的描述
+
+    private Double rentalExpenses;//过期的土地的租赁费用
+
+    private Integer explainType;//过期类型。0:租赁到期
+
+    private String explainDescribe;//记录描述
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/comexplainland/repository/ComExplainLandRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.comexplainland.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.comexplainland.pojo.ComExplainLand;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComExplainLandRepository extends CommonRepository<ComExplainLand, String> {
+}

+ 8 - 0
src/main/java/com/td/boss/game/comexplainland/service/ComExplainLandService.java

@@ -0,0 +1,8 @@
+package com.td.boss.game.comexplainland.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comexplainland.pojo.ComExplainLand;
+import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
+
+public interface ComExplainLandService extends CommonService<ComExplainLandVo, ComExplainLand, String> {
+}

+ 21 - 0
src/main/java/com/td/boss/game/comexplainland/service/ComExplainLandServiceImpl.java

@@ -0,0 +1,21 @@
+package com.td.boss.game.comexplainland.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comexplainland.pojo.ComExplainLand;
+import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
+import com.td.boss.game.comexplainland.repository.ComExplainLandRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+
+@Service
+@Transactional
+public class ComExplainLandServiceImpl extends CommonServiceImpl<ComExplainLandVo, ComExplainLand, String> implements ComExplainLandService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComExplainLandRepository comExplainLandRepository;
+}

+ 43 - 0
src/main/java/com/td/boss/game/comexplainland/vo/ComExplainLandVo.java

@@ -0,0 +1,43 @@
+package com.td.boss.game.comexplainland.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComExplainLandVo extends PageCondition implements Serializable {
+    private String eId;//
+
+    private String userId;//
+
+    private Integer landId;//过期的租赁土地的id
+
+    private Date landCreate;//过期的租赁土地时候土地创建的日期
+
+    private Date leaseTime;//过期的土地租赁的时间
+
+    private Integer leaseMultiple;//过期的土地租赁倍数
+
+    private Integer leaseDate;//过期的土地租赁日期,1是3个月,2是1年,3是5年
+
+    private Integer isPlant;//过期的土地种子状态 是否种植:N:0,Y:1
+
+    private Integer plantId;//过期土地的种植id
+
+    private Date plantStart;//过期土地开始种植的时间
+
+    private Integer plantMature;//过期土地种植成熟期
+
+    private String landDescribe;//过期土地的描述
+
+    private Double rentalExpenses;//过期的土地的租赁费用
+
+    private Integer explainType;//过期类型。0:租赁到期
+
+    private String explainDescribe;//记录描述
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 15 - 0
src/main/java/com/td/boss/game/comfruit/controller/ComFruitController.java

@@ -0,0 +1,15 @@
+package com.td.boss.game.comfruit.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.game.comfruit.pojo.ComFruit;
+import com.td.boss.game.comfruit.vo.ComFruitVo;
+import com.td.boss.game.comfruit.service.ComFruitService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/game/comFruit/")
+public class ComFruitController extends CommonController<ComFruitVo, ComFruit, Integer> {
+    @Autowired
+    private ComFruitService comFruitService;
+}

+ 30 - 0
src/main/java/com/td/boss/game/comfruit/pojo/ComFruit.java

@@ -0,0 +1,30 @@
+package com.td.boss.game.comfruit.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_fruit")
+@Data
+public class ComFruit implements Serializable {
+    @Id
+    @GeneratedValue(strategy= GenerationType.IDENTITY)
+    private Integer id;//
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer priceCnt;//
+
+    private String fruitDescribe;
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/comfruit/repository/ComFruitRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.comfruit.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.comfruit.pojo.ComFruit;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComFruitRepository extends CommonRepository<ComFruit, Integer> {
+}

+ 13 - 0
src/main/java/com/td/boss/game/comfruit/service/ComFruitService.java

@@ -0,0 +1,13 @@
+package com.td.boss.game.comfruit.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comfruit.pojo.ComFruit;
+import com.td.boss.game.comfruit.vo.ComFruitVo;
+
+import java.util.List;
+
+public interface ComFruitService extends CommonService<ComFruitVo, ComFruit, Integer> {
+    ComFruitVo findById(Integer id);
+
+    List<ComFruit> findFruitAll();
+}

+ 40 - 0
src/main/java/com/td/boss/game/comfruit/service/ComFruitServiceImpl.java

@@ -0,0 +1,40 @@
+package com.td.boss.game.comfruit.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.comfruit.pojo.ComFruit;
+import com.td.boss.game.comfruit.vo.ComFruitVo;
+import com.td.boss.game.comfruit.repository.ComFruitRepository;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+@Transactional
+public class ComFruitServiceImpl extends CommonServiceImpl<ComFruitVo, ComFruit, Integer> implements ComFruitService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComFruitRepository comFruitRepository;
+
+    @Override
+    public ComFruitVo findById(Integer id) {
+        Optional<ComFruit> comMallSeed = comFruitRepository.findById(id);
+        if(comMallSeed.orElse(null) == null){
+            return null;
+        }else{
+            return CopyUtil.copy(comMallSeed.get(), ComFruitVo.class);
+        }
+    }
+
+
+    @Override
+    public List<ComFruit> findFruitAll() {
+        return comFruitRepository.findAll();
+    }
+}

+ 30 - 0
src/main/java/com/td/boss/game/comfruit/vo/ComFruitAmountVo.java

@@ -0,0 +1,30 @@
+package com.td.boss.game.comfruit.vo;
+
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComFruitAmountVo implements Serializable {
+    private Integer id;//
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer priceCnt;//
+
+    private String fruitDescribe;
+
+    private Integer amount;//这里添加一个数量,但是数据库是没有的,以背包为主
+
+    private Double amountPart;//用户收取其他的部分数据
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 25 - 0
src/main/java/com/td/boss/game/comfruit/vo/ComFruitVo.java

@@ -0,0 +1,25 @@
+package com.td.boss.game.comfruit.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComFruitVo implements Serializable {
+    private Integer id;//
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer priceCnt;//
+
+    private String fruitDescribe;
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 35 - 0
src/main/java/com/td/boss/game/commallfood/controller/ComMallFoodController.java

@@ -0,0 +1,35 @@
+package com.td.boss.game.commallfood.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.game.commallfood.pojo.ComMallFood;
+import com.td.boss.game.commallfood.vo.ComMallFoodSimpleVo;
+import com.td.boss.game.commallfood.vo.ComMallFoodVo;
+import com.td.boss.game.commallfood.service.ComMallFoodService;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/game/comMallFood/")
+public class ComMallFoodController extends CommonController<ComMallFoodVo, ComMallFood, Integer> {
+    @Autowired
+    private ComMallFoodService comMallFoodService;
+
+
+
+    @GetMapping("getMallFoodList")
+    public Result<List<ComMallFoodSimpleVo>> getEquipmentByTypeFunction(@RequestParam(value = "userId") String userId) {
+
+        ComMallFoodVo comMallFoodVo = new ComMallFoodVo();
+
+        List<ComMallFoodSimpleVo> comMallFoodSimpleVoList = comMallFoodService.list(comMallFoodVo).getData().stream().map(e->{
+            return  CopyUtil.copy(e, ComMallFoodSimpleVo.class);
+        }).collect(Collectors.toList());
+
+        return Result.of(comMallFoodSimpleVoList);
+    }
+}

+ 36 - 0
src/main/java/com/td/boss/game/commallfood/pojo/ComMallFood.java

@@ -0,0 +1,36 @@
+package com.td.boss.game.commallfood.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_mall_food")
+@Data
+public class ComMallFood implements Serializable {
+    @Id
+    @GeneratedValue(strategy= GenerationType.IDENTITY)
+    private Integer id;//
+
+    private String mallType;//商城类型,默认是狗粮0
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer weight;//大包食物
+
+    private Integer priceCnt;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String foodDescribe;//描述一下
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/commallfood/repository/ComMallFoodRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.commallfood.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.commallfood.pojo.ComMallFood;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComMallFoodRepository extends CommonRepository<ComMallFood, Integer> {
+}

+ 8 - 0
src/main/java/com/td/boss/game/commallfood/service/ComMallFoodService.java

@@ -0,0 +1,8 @@
+package com.td.boss.game.commallfood.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallfood.pojo.ComMallFood;
+import com.td.boss.game.commallfood.vo.ComMallFoodVo;
+
+public interface ComMallFoodService extends CommonService<ComMallFoodVo, ComMallFood, Integer> {
+}

+ 21 - 0
src/main/java/com/td/boss/game/commallfood/service/ComMallFoodServiceImpl.java

@@ -0,0 +1,21 @@
+package com.td.boss.game.commallfood.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallfood.pojo.ComMallFood;
+import com.td.boss.game.commallfood.vo.ComMallFoodVo;
+import com.td.boss.game.commallfood.repository.ComMallFoodRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+
+@Service
+@Transactional
+public class ComMallFoodServiceImpl extends CommonServiceImpl<ComMallFoodVo, ComMallFood, Integer> implements ComMallFoodService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComMallFoodRepository comMallFoodRepository;
+}

+ 33 - 0
src/main/java/com/td/boss/game/commallfood/vo/ComMallFoodSimpleVo.java

@@ -0,0 +1,33 @@
+package com.td.boss.game.commallfood.vo;
+
+import com.td.boss.common.pojo.PageCondition;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComMallFoodSimpleVo implements Serializable {
+    private Integer id;//
+
+    private String mallType;//商城类型,默认是狗粮0
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer weight;//大包食物
+
+    private Integer priceCnt;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String foodDescribe;//描述一下
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 31 - 0
src/main/java/com/td/boss/game/commallfood/vo/ComMallFoodVo.java

@@ -0,0 +1,31 @@
+package com.td.boss.game.commallfood.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComMallFoodVo extends PageCondition implements Serializable {
+    private Integer id;//
+
+    private String mallType;//商城类型,默认是狗粮0
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer weight;//大包食物
+
+    private Integer priceCnt;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String foodDescribe;//描述一下
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 34 - 0
src/main/java/com/td/boss/game/commallother/controller/ComMallOtherController.java

@@ -0,0 +1,34 @@
+package com.td.boss.game.commallother.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import com.td.boss.game.commallother.vo.ComMallOtherSimpleVo;
+import com.td.boss.game.commallother.vo.ComMallOtherVo;
+import com.td.boss.game.commallother.service.ComMallOtherService;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/game/comMallOther/")
+public class ComMallOtherController extends CommonController<ComMallOtherVo, ComMallOther, Integer> {
+    @Autowired
+    private ComMallOtherService comMallOtherService;
+
+    /**
+     * 获取商城装备栏列表
+     *
+     * @param userId
+     * @return
+     */
+    @GetMapping("getMallEquipmentList")
+    public Result<List<ComMallOtherSimpleVo>> getEquipmentListFunction(@RequestParam(value = "userId") String userId) {
+        ComMallOtherVo comMallOtherVo = new ComMallOtherVo();
+        Result result = comMallOtherService.list(comMallOtherVo);
+        List<ComMallOtherSimpleVo> comMallOtherSimpleVos = CopyUtil.copyList((List<ComMallOtherSimpleVo>) result.getData(), ComMallOtherSimpleVo.class);
+        return Result.of(comMallOtherSimpleVos);
+    }
+}

+ 36 - 0
src/main/java/com/td/boss/game/commallother/pojo/ComMallOther.java

@@ -0,0 +1,36 @@
+package com.td.boss.game.commallother.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_mall_other")
+@Data
+public class ComMallOther implements Serializable {
+    @Id
+    @GeneratedValue(strategy= GenerationType.IDENTITY)
+    private Integer id;//
+
+    private Integer otherType;//商城类型,默认是小狗0,1打狗棒
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String itemDescribe;//描述一下生产周期
+
+    private Integer effectiveDay;//物品的有效时间
+
+    private Double triggerPro;//触发概率,狗就是触发咬的事件
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/commallother/repository/ComMallOtherRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.commallother.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComMallOtherRepository extends CommonRepository<ComMallOther, Integer> {
+}

+ 11 - 0
src/main/java/com/td/boss/game/commallother/service/ComMallOtherService.java

@@ -0,0 +1,11 @@
+package com.td.boss.game.commallother.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import com.td.boss.game.commallother.vo.ComMallOtherVo;
+
+public interface ComMallOtherService extends CommonService<ComMallOtherVo, ComMallOther, Integer> {
+
+
+
+}

+ 21 - 0
src/main/java/com/td/boss/game/commallother/service/ComMallOtherServiceImpl.java

@@ -0,0 +1,21 @@
+package com.td.boss.game.commallother.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import com.td.boss.game.commallother.vo.ComMallOtherVo;
+import com.td.boss.game.commallother.repository.ComMallOtherRepository;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+
+@Service
+@Transactional
+public class ComMallOtherServiceImpl extends CommonServiceImpl<ComMallOtherVo, ComMallOther, Integer> implements ComMallOtherService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComMallOtherRepository comMallOtherRepository;
+}

+ 33 - 0
src/main/java/com/td/boss/game/commallother/vo/ComMallOtherSimpleVo.java

@@ -0,0 +1,33 @@
+package com.td.boss.game.commallother.vo;
+
+import com.td.boss.common.pojo.PageCondition;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComMallOtherSimpleVo  implements Serializable {
+    private Integer id;//
+
+    private Integer otherType;//商城类型,默认是小狗0,1打狗棒
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String itemDescribe;//描述一下生产周期
+
+    private Integer effectiveDay;//物品的有效时间
+
+    private Double triggerPro;//触发概率,狗就是触发咬的事件
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 31 - 0
src/main/java/com/td/boss/game/commallother/vo/ComMallOtherVo.java

@@ -0,0 +1,31 @@
+package com.td.boss.game.commallother.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComMallOtherVo extends PageCondition implements Serializable {
+    private Integer id;//
+
+    private Integer otherType;//商城类型,默认是小狗0,1打狗棒
+
+    private String picture;//图片名字,前端存储
+
+    private String name;//
+
+    private Integer priceSnb;//
+
+    private Integer amount;//库存数量
+
+    private String itemDescribe;//描述一下生产周期
+
+    private Integer effectiveDay;//物品的有效时间
+
+    private Double triggerPro;//触发概率,狗就是触发咬的事件
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 274 - 0
src/main/java/com/td/boss/game/commallseed/controller/ComMallSeedController.java

@@ -0,0 +1,274 @@
+package com.td.boss.game.commallseed.controller;
+
+import com.alibaba.fastjson.JSONObject;
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.config.enums.ResultEnum;
+import com.td.boss.game.commallseed.pojo.ComMallSeed;
+import com.td.boss.game.commallseed.service.ComMallSeedService;
+import com.td.boss.game.commallseed.vo.ComMallSeedVo;
+import com.td.boss.game.complayergoods.pojo.ComPlayerGoods;
+import com.td.boss.game.complayergoods.service.ComPlayerGoodsService;
+import com.td.boss.game.complayergoods.vo.ComPlayerGoodsSimpleVo;
+import com.td.boss.game.complayergoods.vo.ComPlayerGoodsVo;
+import com.td.boss.game.complayerlog.pojo.ComPlayerLog;
+import com.td.boss.game.complayerlog.service.ComPlayerLogService;
+import com.td.boss.game.complayerlog.vo.ComPlayerLogVo;
+import com.td.boss.game.complayers.controller.ComPlayersController;
+import com.td.boss.game.complayers.pojo.ComPlayers;
+import com.td.boss.game.complayers.service.ComPlayersService;
+import com.td.boss.game.comsnbtran.service.ComSnbTranService;
+import com.td.boss.game.comsnbtran.vo.ComSnbTranVo;
+import com.td.boss.game.comusers.service.ComUsersService;
+import com.td.boss.game.comusers.vo.ComUsersVo;
+import com.td.boss.util.CopyUtil;
+import com.td.boss.util.RedisData;
+import com.td.boss.util.RedisLock;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+@RestController
+@RequestMapping("/game/comMallSeed/")
+public class ComMallSeedController extends CommonController<ComMallSeedVo, ComMallSeed, Integer> {
+    @Autowired
+    private ComMallSeedService comMallSeedService;
+
+    @Autowired
+    private ComPlayerGoodsService comPlayerGoodsService;
+
+    @Autowired
+    private ComUsersService comUsersService;
+
+    @Autowired
+    private ComSnbTranService comSnbTranService;
+
+    @Autowired
+    private ComPlayerLogService comPlayerLogService;
+
+    @Autowired
+    private ComPlayersService comPlayersService;
+
+    @Autowired
+    private RedisLock redisLock;
+    /**
+     * 获取商城种子
+     *
+     * @param userId
+     * @return
+     */
+    @GetMapping("getMallSeed")
+    public Result<List<ComMallSeedVo>> getSNBFunction(@RequestParam(value = "userId") String userId) {
+        //log.info(userId);
+        ComMallSeedVo comMallSeedVo = new ComMallSeedVo();
+        Result result = comMallSeedService.list(comMallSeedVo);
+        return result;
+    }
+
+    /**
+     * 神农呗购买种子
+     *
+     * @param userId
+     * @return
+     */
+    @PostMapping("snbBuySeeds")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> snbBuySeedsFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "payAmount") Integer payAmount,
+            @RequestParam(value = "seedId") Integer seedId
+    ) {
+
+        // 处理itemType,种子id ,获取种子类型,设置种子不同的数据
+        Integer itemType = seedId;
+        ComMallSeedVo seedVo = comMallSeedService.findById(seedId);
+        Map map = new HashMap();
+        if (seedVo == null) {
+
+            map.put("msg", "非法操作:所修改的种子不存在服务器中!");
+            //return Result.of(map);
+            return Result.of(map, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
+        }
+        if (!StringUtils.hasText(payAmount.toString())||payAmount.equals(0) || payAmount % seedVo.getPriceSnb() != 0 ) {
+            //支付价格不能为零,如果价格 不和种子相当,也不是种子的倍数,也是不行的
+            map.put("msg", "订单非法操作,已记录相关信息!");
+            //return Result.of(map);
+            return Result.of(map, false, ResultEnum.SEED_PLAY_ERROR.getMessage(), ResultEnum.SEED_PLAY_ERROR.getCode());
+        }
+
+        //todo snb冻结加锁
+        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
+        String _redisKey = "SNB_SAVE_" + userId;
+
+        try{
+            //todo snb冻结加锁
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                return Result.of(null, false, ResultEnum.SEED_SALE_SAVE_LOCK.getMessage(), ResultEnum.SEED_SALE_SAVE_LOCK.getCode());
+            }
+
+            //todo 操作snb数量
+            ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+            if (comUsersVo.getSnb() < payAmount) {
+                map.put("msg", "账户snb不足");
+                //WALLET_SNB_INSUFFICIENT_QUANTITY(602,"snb不足以支付!")
+                return Result.of(map, false, ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getMessage(), ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getCode());
+            }
+            //如果数量够,写入修改对应操作
+            Integer _oldSnb = comUsersVo.getSnb();
+            Integer _snb = comUsersVo.getSnb() - payAmount;
+            comUsersVo.setSnb(_snb);
+            comUsersService.save(comUsersVo);
+
+            //合法操作记录到仓库中
+            ComPlayerGoods entityVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, itemType, 0);
+            //这里计算一个购买种子的数量,用种子除去单价
+            int _paySeedAmount = (int) (payAmount / seedVo.getPriceSnb());
+            if (entityVo == null) {
+                //创建一个新种子
+                entityVo = new ComPlayerGoods();
+                entityVo.setGoodsIndex(itemType);
+                entityVo.setGoodsType(0);
+                entityVo.setAmount(_paySeedAmount);
+                entityVo.setAmountPart(0d);
+                entityVo.setName(seedVo.getName());
+                entityVo.setPictureName(seedVo.getPicture());
+                entityVo.setUserId(userId);
+            } else {
+                Integer _amount = entityVo.getAmount() + _paySeedAmount;
+                entityVo.setAmount(_amount);
+            }
+            /** 购买成功后,记录到仓库中*/
+            ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(entityVo, ComPlayerGoodsVo.class);
+            comPlayerGoodsService.save(comPlayerGoodsVo);
+
+            /**
+             * todo 购买种子的交易记录
+             */
+            ComSnbTranVo _snbTran = new ComSnbTranVo();
+            //记录兑换id
+            _snbTran.setTranId(itemType.toString());
+            _snbTran.setUserId(userId);
+            _snbTran.setTranName("使用SNB购买种子");
+            _snbTran.setTranType(3);
+            _snbTran.setTranAmount(_paySeedAmount); //此数量会操作背包记录
+            _snbTran.setTranPrice(payAmount);
+            _snbTran.setTranDescribe("SNB购买种子的价格:"+seedVo.getPriceSnb()+",支付金额:"+payAmount);
+            _snbTran.setIsAdd(0);//减少snb
+            _snbTran.setBeforeSnb(_oldSnb);
+            _snbTran.setTranSnb(payAmount);
+            _snbTran.setAfterSnb(_snb);
+            //不涉及交易的设置为0
+            _snbTran.setTranAmountPart(0d);
+            _snbTran.setTranSnbPart(0d);
+            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
+            _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
+            comSnbTranService.save(_snbTran);
+
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            map.put("seed", entityVo.getGoodsIndex());
+            map.put("snb",comUsersVo.getSnb());
+            return Result.of(map);
+        }catch (Exception e) {
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+
+    }
+
+
+
+    /**
+     * 用钻石兑换一份辣椒种子
+     *
+     * @param userId
+     * @return
+     */
+    @PostMapping("exchangeSeeds")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> exchangeSeedsFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "diamondAmount") Integer diamondAmount,
+            @RequestParam(value = "seedId") Integer seedId
+    ) {
+        // 处理itemType,种子id ,获取种子类型,设置种子不同的数据
+        Integer itemType = seedId;
+        ComMallSeedVo seedVo = comMallSeedService.findById(seedId);
+        Map map = new HashMap();
+        if (seedVo == null) {
+            map.put("msg", "非法操作:所修改的种子不存在服务器中!");
+            //return Result.of(map);
+            return Result.of(map, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
+        }
+
+        ComPlayers player = comPlayersService.findByUserId(userId);
+        if(player.getIsExchange().equals(1)){
+            map.put("msg", "非法操作:已兑换过种子!");
+            return Result.of(map, false, ResultEnum.SEED_IS_EXCHANGE.getMessage(), ResultEnum.SEED_IS_EXCHANGE.getCode());
+        }
+
+        JSONObject jsonObject = JSONObject.parseObject(player.getData());
+        Integer _diamond = Integer.parseInt(jsonObject.getString("Diamond"));
+        if(_diamond<diamondAmount){
+            return Result.of(map, false, ResultEnum.SEED_PLAY_DIAMOND_ERROR.getMessage(), ResultEnum.SEED_PLAY_DIAMOND_ERROR.getCode());
+        }
+        jsonObject.put("Diamond",_diamond - diamondAmount);
+        //设置已经兑换的状态
+        player.setIsExchange(1);
+        //更新游戏状态
+        player.setData(jsonObject.toString());
+        comPlayersService.addComPlayers(player);
+        //合法操作记录到仓库中
+        //todo 添加一个辣椒种子
+        ComPlayerGoods entityVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, itemType, 0);
+        Integer _selfBeforeAmount = 0;
+        if (entityVo == null) {
+            //创建一个新种子
+            entityVo = new ComPlayerGoods();
+            entityVo.setGoodsIndex(itemType);
+            entityVo.setGoodsType(0);
+            entityVo.setAmount(1);
+            entityVo.setAmountPart(0d);
+            entityVo.setName(seedVo.getName());
+            entityVo.setPictureName(seedVo.getPicture());
+            entityVo.setUserId(userId);
+        } else {
+            _selfBeforeAmount = entityVo.getAmount();
+            Integer _amount = entityVo.getAmount() + 1;
+            entityVo.setAmount(_amount);
+        }
+        /** 购买成功后,记录到仓库中*/
+        ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(entityVo, ComPlayerGoodsVo.class);
+        comPlayerGoodsService.save(comPlayerGoodsVo);
+
+        /**
+         * todo 记录 PlayerLog
+         */
+        //当前用户
+        ComPlayerLog _selfPlayerLog = new ComPlayerLog();
+        _selfPlayerLog.setUserId(userId);
+        _selfPlayerLog.setTId(entityVo.getGoodsIndex());
+        _selfPlayerLog.setTName(entityVo.getName());
+        _selfPlayerLog.setTType(4);//兑换种子
+        _selfPlayerLog.setTAmount(1);
+        _selfPlayerLog.setBeforeAmount(_selfBeforeAmount);
+        ////交易后的数据
+        _selfPlayerLog.setAfterAmount(entityVo.getAmount());
+        _selfPlayerLog.setLMultiple(0);//收获时候,会有一个倍数
+        ComPlayerLogVo _selfPlayerLogVo = CopyUtil.copy(_selfPlayerLog, ComPlayerLogVo.class);
+        comPlayerLogService.save(_selfPlayerLogVo);
+
+        map.put("seed", entityVo.getGoodsIndex());
+        map.put("playerData",player.getData());
+        map.put("isExchange",player.getIsExchange());
+
+        return Result.of(map);
+    }
+
+}

+ 50 - 0
src/main/java/com/td/boss/game/commallseed/pojo/ComMallSeed.java

@@ -0,0 +1,50 @@
+package com.td.boss.game.commallseed.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_mall_seed")
+@Data
+public class ComMallSeed implements Serializable {
+    @Id
+    private Integer id;//
+
+    private String mallType;//商城类型,默认是种子0
+
+    private String picture;
+
+    private String name;//
+
+    private Integer maturity;//
+
+    private Integer planting;//
+
+    private Integer harvestId;
+    private Integer harvestQuantity;//
+
+    private Integer harvestCount;//
+
+    private Integer harvest1;//
+    private Integer harvest2;//
+    private Integer harvest3;//
+
+    private String harvestName;//
+
+    private Integer priceSnb;//
+
+    private Integer priceCnt;//
+
+    private Integer amount;
+
+    private String seedDescribe;//
+
+    private Integer withered;//
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 9 - 0
src/main/java/com/td/boss/game/commallseed/repository/ComMallSeedRepository.java

@@ -0,0 +1,9 @@
+package com.td.boss.game.commallseed.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.commallseed.pojo.ComMallSeed;
+import org.springframework.stereotype.Repository;
+
+@Repository
+public interface ComMallSeedRepository extends CommonRepository<ComMallSeed, Integer> {
+}

+ 14 - 0
src/main/java/com/td/boss/game/commallseed/service/ComMallSeedService.java

@@ -0,0 +1,14 @@
+package com.td.boss.game.commallseed.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallseed.pojo.ComMallSeed;
+import com.td.boss.game.commallseed.vo.ComMallSeedVo;
+
+import java.util.List;
+
+public interface ComMallSeedService extends CommonService<ComMallSeedVo, ComMallSeed, Integer> {
+
+    ComMallSeedVo findById(Integer id);
+
+    List<ComMallSeed>  findMallSeedAll();
+}

+ 41 - 0
src/main/java/com/td/boss/game/commallseed/service/ComMallSeedServiceImpl.java

@@ -0,0 +1,41 @@
+package com.td.boss.game.commallseed.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.commallseed.pojo.ComMallSeed;
+import com.td.boss.game.commallseed.vo.ComMallSeedVo;
+import com.td.boss.game.commallseed.repository.ComMallSeedRepository;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+import java.util.Optional;
+
+@Service
+@Transactional
+public class ComMallSeedServiceImpl extends CommonServiceImpl<ComMallSeedVo, ComMallSeed, Integer> implements ComMallSeedService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComMallSeedRepository comMallSeedRepository;
+
+
+    @Override
+    public ComMallSeedVo findById(Integer id) {
+
+        Optional<ComMallSeed> comMallSeed = comMallSeedRepository.findById(id);
+        if(comMallSeed.orElse(null) == null){
+            return null;
+        }else{
+            return CopyUtil.copy(comMallSeed.get(), ComMallSeedVo.class);
+        }
+    }
+
+    @Override
+    public List<ComMallSeed> findMallSeedAll() {
+        return comMallSeedRepository.findAll();
+    }
+}

+ 45 - 0
src/main/java/com/td/boss/game/commallseed/vo/ComMallSeedVo.java

@@ -0,0 +1,45 @@
+package com.td.boss.game.commallseed.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComMallSeedVo  implements Serializable {
+    private Integer id;//
+
+    private String mallType;//商城类型,默认是种子0
+
+    private String picture;
+
+    private String name;//
+
+    private Integer maturity;//
+
+    private Integer planting;//
+    private Integer harvestId;
+    private Integer harvestQuantity;//
+
+    private Integer harvestCount;//
+
+    private Integer harvest1;//
+    private Integer harvest2;//
+    private Integer harvest3;//
+
+    private String harvestName;//
+
+    private Integer priceSnb;//
+
+    private Integer priceCnt;//
+
+    private Integer amount;//
+
+    private String seedDescribe;//
+
+    private Integer withered;//
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 423 - 0
src/main/java/com/td/boss/game/complayerdog/controller/ComPlayerDogController.java

@@ -0,0 +1,423 @@
+package com.td.boss.game.complayerdog.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.config.enums.ResultEnum;
+import com.td.boss.game.commallfood.service.ComMallFoodService;
+import com.td.boss.game.commallfood.vo.ComMallFoodVo;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import com.td.boss.game.commallother.service.ComMallOtherService;
+import com.td.boss.game.commallother.vo.ComMallOtherSimpleVo;
+import com.td.boss.game.commallother.vo.ComMallOtherVo;
+import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
+import com.td.boss.game.complayerdog.vo.ComPlayerDogSimpleVo;
+import com.td.boss.game.complayerdog.vo.ComPlayerDogVo;
+import com.td.boss.game.complayerdog.service.ComPlayerDogService;
+import com.td.boss.game.comsnbtran.service.ComSnbTranService;
+import com.td.boss.game.comsnbtran.vo.ComSnbTranVo;
+import com.td.boss.game.comusers.pojo.ComUsers;
+import com.td.boss.game.comusers.service.ComUsersService;
+import com.td.boss.game.comusers.vo.ComUsersVo;
+import com.td.boss.util.*;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+@RestController
+@Slf4j
+@RequestMapping("/game/comPlayerDog/")
+public class ComPlayerDogController extends CommonController<ComPlayerDogVo, ComPlayerDog, String> {
+    @Autowired
+    private ComPlayerDogService comPlayerDogService;
+
+    @Autowired
+    private ComMallOtherService comMallOtherService;
+
+    @Autowired
+    private ComUsersService comUsersService;
+
+    @Autowired
+    private ComSnbTranService comSnbTranService;
+
+    @Autowired
+    private ComMallFoodService comMallFoodService;
+
+    @Autowired
+    private RedisLock redisLock;
+
+    /**
+     * 买狗和打狗棒
+     *
+     * @param userId
+     * @param mallOtherId
+     * @return
+     */
+    @PostMapping("addEquipment")
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> addEquipmentFunction(@RequestParam(value = "userId") String userId,
+                                            @RequestParam(value = "mallOtherId") Integer mallOtherId) {
+        Map map = new HashMap();
+        //需要redis 加锁
+        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
+        String _redisKey = "SNB_SAVE_" + userId;
+        try {
+            ComMallOtherVo comMallOtherVo = comMallOtherService.get(mallOtherId).getData();
+            if (comMallOtherVo == null) {
+                return Result.of(null, false, ResultEnum.MALL_OTHER_IS_NULL.getMessage(), ResultEnum.MALL_OTHER_IS_NULL.getCode());
+            }
+            ComPlayerDog comPlayerDog = comPlayerDogService.findByUserIdAndType(userId, comMallOtherVo.getOtherType());
+            if (comPlayerDog != null) {
+                //todo 计算狗和打狗棒是否到期,未到期给提示,到期下面直接给新的计算时间
+                long _runDay = DateUtil.getDays(DateUtil.getNowDate().getTime() - comPlayerDog.getEffectiveStartTime().getTime());
+                int _remainingDay = comPlayerDog.getEffectiveDay() - (int) _runDay;
+                if (_remainingDay > 0) {
+                    return Result.of(null, false, ResultEnum.PLAYER_OTHER_IS_HAVE.getMessage(), ResultEnum.PLAYER_OTHER_IS_HAVE.getCode());
+                }
+            }
+            // 防止重复操作
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                return Result.of(null, false, ResultEnum.REDIS_IS_LOCK.getMessage(), ResultEnum.REDIS_IS_LOCK.getCode());
+            }
+
+            ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+            Double _allSnb = DoubleUtil.add(comUsersVo.getSnb().doubleValue(), comUsersVo.getSnbPart());
+            //如果有snb,扣除对应的snb
+            Integer _allSnbMaxInt = (int) Math.floor(_allSnb);
+            if (_allSnbMaxInt < comMallOtherVo.getPriceSnb()) {
+                return Result.of(null, false, ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getMessage(), ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getCode());
+            }
+            /**
+             * todo 购买装备时候snb变动
+             */
+            ComSnbTranVo _snbTran = new ComSnbTranVo();
+            _snbTran.setBeforeSnb(comUsersVo.getSnb());
+            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
+
+            Integer _tranSnb = _allSnbMaxInt - comMallOtherVo.getPriceSnb();
+            comUsersVo.setSnb(_tranSnb);
+            Double _SnbPart = DoubleUtil.sub(_allSnb, Math.floor(_allSnb));
+            comUsersVo.setSnbPart(_SnbPart);
+            //记录修改的数据
+            comUsersService.save(comUsersVo);
+
+            //记录交易数据
+            _snbTran.setTranId(mallOtherId.toString());
+            _snbTran.setUserId(userId);
+            if (comMallOtherVo.getOtherType().equals(0)) {
+                _snbTran.setTranName("购买狗");
+                _snbTran.setTranType(5);
+                _snbTran.setTranDescribe("购买了一只狗");
+            } else if (comMallOtherVo.getOtherType().equals(1)) {
+                _snbTran.setTranName("购买棒子");
+                _snbTran.setTranType(6);
+                _snbTran.setTranDescribe("购买了一根打狗棒");
+            }
+            _snbTran.setTranAmount(1);
+            _snbTran.setTranAmountPart(0d);
+            _snbTran.setTranPrice(comMallOtherVo.getPriceSnb());
+            _snbTran.setIsAdd(0);//减少
+            _snbTran.setTranSnb(comMallOtherVo.getPriceSnb());
+            _snbTran.setTranSnbPart(0d);
+            _snbTran.setAfterSnb(comUsersVo.getSnb());
+            _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
+            comSnbTranService.save(_snbTran);
+
+            Date _nowDate = DateUtil.getNowDate();
+            if (comPlayerDog == null) {
+                comPlayerDog = new ComPlayerDog();
+                comPlayerDog.setOtherIndex(mallOtherId);
+                comPlayerDog.setOtherType(comMallOtherVo.getOtherType());
+                comPlayerDog.setUserId(userId);
+                comPlayerDog.setAmount(1);
+                comPlayerDog.setAmountPart(0d);
+                comPlayerDog.setEffectiveDay(comMallOtherVo.getEffectiveDay());
+                comPlayerDog.setEffectiveStartTime(_nowDate);
+                comPlayerDog.setConsumption(0);//狗的消耗品是狗粮
+                comPlayerDog.setConsumptionUpdate(_nowDate);
+                //记录一个最大值
+                comPlayerDog.setTotalConsumption(0);
+                comPlayerDog.setIsShow(1);//默认显示
+                comPlayerDog.setStartShowTime(_nowDate);
+            } else {
+                //重置新狗的有效时间
+                comPlayerDog.setEffectiveDay(comMallOtherVo.getEffectiveDay());
+                comPlayerDog.setEffectiveStartTime(_nowDate);
+            }
+            comPlayerDogService.save(CopyUtil.copy(comPlayerDog, ComPlayerDogVo.class));
+
+            //加入是否显示判断
+            long _allTime = _nowDate.getTime() - comPlayerDog.getConsumptionUpdate().getTime();
+            //隐藏计算
+            if (comPlayerDog.getIsShow().equals(0)) {
+                //计算双倍的时间
+                _allTime = _nowDate.getTime() * 2 - comPlayerDog.getConsumptionUpdate().getTime() - comPlayerDog.getStartShowTime().getTime();
+            }
+            long _consumptionHours = DateUtil.getDiffHours(_allTime);
+            int _remainingConsumption = comPlayerDog.getConsumption() - (int) _consumptionHours;
+
+            ComPlayerDogSimpleVo comPlayerDogSimpleVo = CopyUtil.copy(comPlayerDog, ComPlayerDogSimpleVo.class);
+            comPlayerDogSimpleVo.setRemainingConsumption(_remainingConsumption < 0 ? 0 : _remainingConsumption);
+            comPlayerDogSimpleVo.setMallOther(CopyUtil.copy(comMallOtherVo, ComMallOtherSimpleVo.class));
+
+            long _runDay = DateUtil.getDays(_nowDate.getTime() - comPlayerDog.getEffectiveStartTime().getTime());
+            int _remainingDay = comPlayerDog.getEffectiveDay() - (int) _runDay;
+            comPlayerDogSimpleVo.setRemainingDay(_remainingDay < 0 ? 0 : _remainingDay);
+            comPlayerDogSimpleVo.setTotalDay(comPlayerDog.getEffectiveDay());
+
+            redisLock.unlock(_redisKey, String.valueOf(time));
+
+            map.put("equip", comPlayerDogSimpleVo);
+            return Result.of(map);
+        } catch (Exception e) {
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+
+    /**
+     * 添加狗粮
+     *
+     * @param userId
+     * @return
+     */
+    @PostMapping("addFood")
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> addFoodFunction(@RequestParam(value = "userId") String userId,
+                                       @RequestParam(value = "foodId") Integer foodId,
+                                       @RequestParam(value = "buyAmount", defaultValue = "1") Integer buyAmount) {
+
+
+        //查看用户是否有狗
+        ComPlayerDog comPlayerDog = comPlayerDogService.findByUserIdAndType(userId, 0);
+        if (comPlayerDog == null) {
+            return Result.of(null, false, ResultEnum.PLAYER_OTHER_IS_NULL.getMessage(), ResultEnum.PLAYER_OTHER_IS_NULL.getCode());
+        }
+
+        ComMallFoodVo comMallFoodVo = comMallFoodService.get(foodId).getData();
+        Map map = new HashMap();
+        if (comMallFoodVo == null) {
+            map.put("msg", "非法操作:所修改的种子不存在服务器中!");
+            return Result.of(map, false, ResultEnum.FOOD_DATA_ERROR.getMessage(), ResultEnum.FOOD_DATA_ERROR.getCode());
+        }
+        //todo snb加锁
+        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
+        String _redisKey = "SNB_SAVE_" + userId;
+        try {
+            //todo snb冻结加锁
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                return Result.of(null, false, ResultEnum.SEED_SALE_SAVE_LOCK.getMessage(), ResultEnum.SEED_SALE_SAVE_LOCK.getCode());
+            }
+            //todo 操作snb数量
+            ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+            /**
+             * todo 购买狗粮时候snb变动
+             */
+            ComSnbTranVo _snbTran = new ComSnbTranVo();
+            _snbTran.setBeforeSnb(comUsersVo.getSnb());
+            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
+            //计算
+            Double _allSnb = DoubleUtil.add(comUsersVo.getSnb().doubleValue(), comUsersVo.getSnbPart());
+            //如果有snb,扣除对应的snb
+            Integer _allSnbMaxInt = (int) Math.floor(_allSnb);
+            Integer _needPrice = buyAmount * comMallFoodVo.getPriceSnb();
+            if (_allSnbMaxInt < _needPrice) {
+                return Result.of(null, false, ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getMessage(), ResultEnum.WALLET_SNB_INSUFFICIENT_QUANTITY.getCode());
+            }
+            Integer _tranSnb = _allSnbMaxInt - _needPrice;
+            comUsersVo.setSnb(_tranSnb);
+            Double _SnbPart = DoubleUtil.sub(_allSnb, Math.floor(_allSnb));
+            comUsersVo.setSnbPart(_SnbPart);
+            //记录修改的数据
+            comUsersService.save(comUsersVo);
+
+            //修改狗的数据
+            Date _nowDate = DateUtil.getNowDate();
+            //加入是否显示判断
+            long _allTime = _nowDate.getTime() - comPlayerDog.getConsumptionUpdate().getTime();
+            //隐藏计算
+            if (comPlayerDog.getIsShow().equals(0)) {
+                //计算双倍的时间
+                _allTime = _nowDate.getTime() * 2 - comPlayerDog.getConsumptionUpdate().getTime() - comPlayerDog.getStartShowTime().getTime();
+                //计算了一次双时间,记录
+                comPlayerDog.setStartShowTime(_nowDate);
+            }
+            long _consumptionHours = DateUtil.getDiffHours(_allTime);
+            int _remainingConsumption = comPlayerDog.getConsumption() - (int) _consumptionHours;
+
+            //判断狗粮是否消耗完,未消耗完叠加,消耗完重新设置
+            Integer _addWeight = buyAmount * comMallFoodVo.getWeight();
+            if (_remainingConsumption < 0) {
+                comPlayerDog.setConsumption(_addWeight);
+                //记录一个最大值
+                comPlayerDog.setTotalConsumption(_addWeight);
+            } else {
+                comPlayerDog.setConsumption(_remainingConsumption + _addWeight);
+                //记录一个最大值,已当前更新为主
+                comPlayerDog.setTotalConsumption(_remainingConsumption + _addWeight);
+            }
+
+            comPlayerDog.setConsumptionUpdate(_nowDate);
+            comPlayerDogService.save(CopyUtil.copy(comPlayerDog, ComPlayerDogVo.class));
+
+            //记录交易数据
+            _snbTran.setTranId(foodId.toString());
+            _snbTran.setUserId(userId);
+            _snbTran.setTranName("购买狗粮");
+            _snbTran.setTranType(7);
+            _snbTran.setTranDescribe("购买狗粮类型:" + comMallFoodVo.getMallType() + ",Weight:" + comMallFoodVo.getWeight());
+            _snbTran.setTranAmount(comMallFoodVo.getWeight());
+            _snbTran.setTranAmountPart(0d);
+            _snbTran.setTranPrice(comMallFoodVo.getPriceSnb());
+            _snbTran.setIsAdd(0);//减少
+            _snbTran.setTranSnb(_needPrice);
+            _snbTran.setTranSnbPart(0d);
+            _snbTran.setAfterSnb(comUsersVo.getSnb());
+            _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
+            comSnbTranService.save(_snbTran);
+
+            redisLock.unlock(_redisKey, String.valueOf(time));
+
+            ComPlayerDogSimpleVo comPlayerDogSimpleVo = CopyUtil.copy(comPlayerDog, ComPlayerDogSimpleVo.class);
+            comPlayerDogSimpleVo.setRemainingConsumption(comPlayerDog.getConsumption());
+            ComMallOtherVo comMallOtherVo = comMallOtherService.get(comPlayerDog.getOtherIndex()).getData();
+            comPlayerDogSimpleVo.setMallOther(CopyUtil.copy(comMallOtherVo, ComMallOtherSimpleVo.class));
+            long _runDay = DateUtil.getDays(_nowDate.getTime() - comPlayerDog.getEffectiveStartTime().getTime());
+            int _remainingDay = comPlayerDog.getEffectiveDay() - (int) _runDay;
+            comPlayerDogSimpleVo.setRemainingDay(_remainingDay < 0 ? 0 : _remainingDay);
+            comPlayerDogSimpleVo.setTotalDay(comPlayerDog.getEffectiveDay());
+
+            map.put("dog", comPlayerDogSimpleVo);
+            map.put("snb", comUsersVo.getSnb().doubleValue());
+            map.put("snbPart", comUsersVo.getSnbPart());
+
+            return Result.of(map);
+        } catch (Exception e) {
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+    }
+
+    @GetMapping("getEquipmentByType")
+    public Result<ComPlayerDogSimpleVo> getEquipmentByTypeFunction(@RequestParam(value = "userId") String userId,
+                                                                   @RequestParam(value = "type") Integer type) {
+
+        ComPlayerDog comPlayerDog = comPlayerDogService.findByUserIdAndType(userId, type);
+        if (comPlayerDog == null) {
+            return Result.of(null, false, ResultEnum.PLAYER_OTHER_IS_NULL.getMessage(), ResultEnum.PLAYER_OTHER_IS_NULL.getCode());
+        }
+        ComMallOtherVo comMallOtherVo = comMallOtherService.get(comPlayerDog.getOtherIndex()).getData();
+        if (comMallOtherVo == null) {
+            return Result.of(null, false, ResultEnum.MALL_OTHER_IS_NULL.getMessage(), ResultEnum.MALL_OTHER_IS_NULL.getCode());
+        }
+        Date _nowDate = DateUtil.getNowDate();
+        //加入是否显示判断
+        long _allTime = _nowDate.getTime() - comPlayerDog.getConsumptionUpdate().getTime();
+        //隐藏计算
+        if (comPlayerDog.getIsShow().equals(0)) {
+            //计算双倍的时间
+            _allTime = _nowDate.getTime() * 2 - comPlayerDog.getConsumptionUpdate().getTime() - comPlayerDog.getStartShowTime().getTime();
+        }
+        long _consumptionHours = DateUtil.getDiffHours(_allTime);
+        int _remainingConsumption = comPlayerDog.getConsumption() - (int) _consumptionHours;
+        ComPlayerDogSimpleVo comPlayerDogSimpleVo = CopyUtil.copy(comPlayerDog, ComPlayerDogSimpleVo.class);
+        comPlayerDogSimpleVo.setMallOther(CopyUtil.copy(comMallOtherVo, ComMallOtherSimpleVo.class));
+        comPlayerDogSimpleVo.setRemainingConsumption(_remainingConsumption < 0 ? 0 : _remainingConsumption);
+
+        return Result.of(comPlayerDogSimpleVo);
+    }
+
+    @GetMapping("getListEquipment")
+    public Result<List<ComPlayerDogSimpleVo>> getListEquipmentFunction(@RequestParam(value = "userId") String userId,
+                                                                       @RequestParam(value = "otherUserId", required = false) String otherUserId) {
+
+        String currentUserId = userId;
+        if (StringUtils.hasText(otherUserId)) {
+            currentUserId = otherUserId;
+        }
+
+
+        List<ComPlayerDog> comPlayerDogList = comPlayerDogService.findAllByUserId(currentUserId);
+        List<ComPlayerDogSimpleVo> comPlayerDogSimpleVos = new ArrayList<>();
+
+        Date _nowDate = DateUtil.getNowDate();
+        comPlayerDogList.stream().map(e -> {
+            ComMallOtherVo comMallOtherVo = comMallOtherService.get(e.getOtherIndex()).getData();
+            if (comMallOtherVo == null) {
+                return null;
+            }
+            ComPlayerDogSimpleVo comPlayerDogSimpleVo = CopyUtil.copy(e, ComPlayerDogSimpleVo.class);
+            comPlayerDogSimpleVo.setMallOther(CopyUtil.copy(comMallOtherVo, ComMallOtherSimpleVo.class));
+            long _runDay = DateUtil.getDays(_nowDate.getTime() - e.getEffectiveStartTime().getTime());
+            int _remainingDay = e.getEffectiveDay() - (int) _runDay;
+            comPlayerDogSimpleVo.setRemainingDay(_remainingDay < 0 ? 0 : _remainingDay);
+            comPlayerDogSimpleVo.setTotalDay(e.getEffectiveDay());
+            //加入是否显示判断
+            long _allTime = _nowDate.getTime() - e.getConsumptionUpdate().getTime();
+            //隐藏计算
+            if (e.getIsShow().equals(0)) {
+                //计算双倍的时间
+                _allTime = _nowDate.getTime() * 2 - e.getConsumptionUpdate().getTime() - e.getStartShowTime().getTime();
+            }
+            long _consumptionHours = DateUtil.getDiffHours(_allTime);
+            int _remainingConsumption = e.getConsumption() - (int) _consumptionHours;
+            comPlayerDogSimpleVo.setRemainingConsumption(_remainingConsumption < 0 ? 0 : _remainingConsumption);
+            return comPlayerDogSimpleVos.add(comPlayerDogSimpleVo);
+
+        }).collect(Collectors.toList());
+
+        return Result.of(comPlayerDogSimpleVos);
+    }
+
+
+    /**
+     * 设置狗的显示状态
+     *
+     * @param userId
+     * @param isShow
+     * @return
+     */
+    @PostMapping("setDogShowState")
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> setDogShowStateFunction(@RequestParam(value = "userId") String userId,
+                                               @RequestParam(value = "isShow") Integer isShow) {
+
+        //查看用户是否有狗
+        ComPlayerDog comPlayerDog = comPlayerDogService.findByUserIdAndType(userId, 0);
+        if (comPlayerDog == null) {
+            return Result.of(null, false, ResultEnum.PLAYER_OTHER_IS_NULL.getMessage(), ResultEnum.PLAYER_OTHER_IS_NULL.getCode());
+        }
+        Map map = new HashMap();
+        //todo 判断是否是隐藏属性,更新一次狗的狗粮数据。隐藏需要双倍处理扣除
+        //修改狗的数据
+        Date _nowDate = DateUtil.getNowDate();
+        //加入是否显示判断
+        long _allTime = _nowDate.getTime() - comPlayerDog.getConsumptionUpdate().getTime();
+        //隐藏计算
+        if (comPlayerDog.getIsShow().equals(0)) {
+            //计算双倍的时间
+            _allTime = _nowDate.getTime() * 2 - comPlayerDog.getConsumptionUpdate().getTime() - comPlayerDog.getStartShowTime().getTime();
+        }
+        long _consumptionHours = DateUtil.getDiffHours(_allTime);
+        int _remainingConsumption = comPlayerDog.getConsumption() - (int) _consumptionHours;
+        //计算对应的狗粮
+        comPlayerDog.setConsumption(_remainingConsumption < 0 ? 0 : _remainingConsumption);
+        comPlayerDog.setConsumptionUpdate(_nowDate);
+        //更新状态
+        comPlayerDog.setIsShow(isShow);
+        comPlayerDog.setStartShowTime(_nowDate);
+        comPlayerDogService.save(CopyUtil.copy(comPlayerDog, ComPlayerDogVo.class));
+        ComPlayerDogSimpleVo comPlayerDogSimpleVo = CopyUtil.copy(comPlayerDog, ComPlayerDogSimpleVo.class);
+        comPlayerDogSimpleVo.setRemainingConsumption(_remainingConsumption < 0 ? 0 : _remainingConsumption);
+        ComMallOtherVo comMallOtherVo = comMallOtherService.get(comPlayerDog.getOtherIndex()).getData();
+        comPlayerDogSimpleVo.setMallOther(CopyUtil.copy(comMallOtherVo, ComMallOtherSimpleVo.class));
+        map.put("dog", comPlayerDogSimpleVo);
+        return Result.of(map);
+    }
+
+}

+ 43 - 0
src/main/java/com/td/boss/game/complayerdog/pojo/ComPlayerDog.java

@@ -0,0 +1,43 @@
+package com.td.boss.game.complayerdog.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_player_dog")
+@Data
+public class ComPlayerDog implements Serializable {
+    @Id
+    private String id;//物品id,字符串
+
+    private Integer otherIndex;//仓库物品下标, 根据类型区分。比如狗type=0,index=狗id,
+
+    private Integer otherType;//对应类型(0:看护狗,1:打狗棒)
+
+    private String userId;//
+
+    private Integer amount;//数量
+
+    private Double amountPart;//amount增加部分,目前用于收取其他用户部分处理数据
+
+    private Integer effectiveDay;//物品的有效时间
+
+    private Date effectiveStartTime;//有效的开始时间,比如买狗了,生效时间
+
+    private Integer consumption;//当前物品对应的消耗品数量
+
+    private Date consumptionUpdate;//消耗品的更新时间
+
+    private Integer totalConsumption;//使用过的狗粮总量
+
+    private Integer isShow;//是否显示狗,用户其他农场显示 默认显示: 1 ,隐藏:0
+
+    private Date startShowTime;//切换状态时候,更新一下这个显示的时间,用来计算双倍狗粮
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 17 - 0
src/main/java/com/td/boss/game/complayerdog/repository/ComPlayerDogRepository.java

@@ -0,0 +1,17 @@
+package com.td.boss.game.complayerdog.repository;
+
+import com.td.boss.common.repository.*;
+import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
+import org.springframework.stereotype.Repository;
+import org.springframework.util.StringUtils;
+
+import java.util.List;
+import java.util.Optional;
+
+@Repository
+public interface ComPlayerDogRepository extends CommonRepository<ComPlayerDog, String> {
+
+    Optional<ComPlayerDog> findByUserIdAndOtherType(String userId,Integer OtherType);
+
+    List<ComPlayerDog> findAllByUserId(String userId);
+}

+ 14 - 0
src/main/java/com/td/boss/game/complayerdog/service/ComPlayerDogService.java

@@ -0,0 +1,14 @@
+package com.td.boss.game.complayerdog.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
+import com.td.boss.game.complayerdog.vo.ComPlayerDogVo;
+
+import java.util.List;
+
+public interface ComPlayerDogService extends CommonService<ComPlayerDogVo, ComPlayerDog, String> {
+
+    ComPlayerDog findByUserIdAndType(String userId,Integer otherType);
+
+    List<ComPlayerDog> findAllByUserId(String userId);
+}

+ 35 - 0
src/main/java/com/td/boss/game/complayerdog/service/ComPlayerDogServiceImpl.java

@@ -0,0 +1,35 @@
+package com.td.boss.game.complayerdog.service;
+
+import com.td.boss.common.service.*;
+import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
+import com.td.boss.game.complayerdog.vo.ComPlayerDogVo;
+import com.td.boss.game.complayerdog.repository.ComPlayerDogRepository;
+import com.td.boss.util.CopyUtil;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import javax.persistence.EntityManager;
+import javax.persistence.PersistenceContext;
+import java.util.List;
+
+@Service
+@Transactional
+public class ComPlayerDogServiceImpl extends CommonServiceImpl<ComPlayerDogVo, ComPlayerDog, String> implements ComPlayerDogService{
+
+    @PersistenceContext
+    private EntityManager em;
+    @Autowired
+    private ComPlayerDogRepository comPlayerDogRepository;
+
+
+    @Override
+    public ComPlayerDog findByUserIdAndType(String userId, Integer otherType) {
+        ComPlayerDog comPlayerDog = comPlayerDogRepository.findByUserIdAndOtherType(userId, otherType).orElse(null);
+        return comPlayerDog;
+    }
+
+    @Override
+    public List<ComPlayerDog> findAllByUserId(String userId) {
+        return comPlayerDogRepository.findAllByUserId(userId);
+    }
+}

+ 45 - 0
src/main/java/com/td/boss/game/complayerdog/vo/ComPlayerDogSimpleVo.java

@@ -0,0 +1,45 @@
+package com.td.boss.game.complayerdog.vo;
+
+import com.td.boss.common.pojo.PageCondition;
+import com.td.boss.game.commallother.vo.ComMallOtherSimpleVo;
+import lombok.Data;
+
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComPlayerDogSimpleVo implements Serializable {
+
+    private ComMallOtherSimpleVo mallOther; //返回一个对象
+
+    private Integer otherType;//商城类型,默认是小狗0,1打狗棒
+
+    private Integer amount;//数量
+
+    private Double amountPart;//amount增加部分,目前用于收取其他用户部分处理数据
+
+    //private Integer effectiveDay;//物品的有效时间
+    //
+    //private Date effectiveStartTime;//有效的开始时间,比如买狗了,生效时间
+
+    private Integer remainingDay; //剩余天数
+
+    private Integer totalDay;//全部天数
+
+    //private Integer consumption;//当前物品对应的消耗品数量
+    //
+    //private Date consumptionUpdate;//消耗品的更新时间
+
+    private Integer remainingConsumption; //剩余消耗品。狗粮
+
+    private Integer totalConsumption;//当前全部的狗粮
+
+    private Integer isShow;//是否显示狗,用户其他农场显示 默认显示: 1 ,隐藏:0
+
+    //private Date startShowTime;//切换状态时候,更新一下这个显示的时间,用来计算双倍狗粮
+
+    //private Date createTime;//
+
+    //private Date updateTime;//
+
+}

+ 39 - 0
src/main/java/com/td/boss/game/complayerdog/vo/ComPlayerDogVo.java

@@ -0,0 +1,39 @@
+package com.td.boss.game.complayerdog.vo;
+
+import com.td.boss. common.pojo.PageCondition;import lombok.Data;
+import java.io.Serializable;
+import java.util.Date;
+
+@Data
+public class ComPlayerDogVo extends PageCondition implements Serializable {
+    private String id;//物品id,字符串
+
+    private Integer otherIndex;//仓库物品下标, 根据类型区分。比如狗type=0,index=狗id,
+
+    private Integer otherType;//对应类型(0:看护狗,1:打狗棒)
+
+    private String userId;//
+
+    private Integer amount;//数量
+
+    private Double amountPart;//amount增加部分,目前用于收取其他用户部分处理数据
+
+    private Integer effectiveDay;//物品的有效时间
+
+    private Date effectiveStartTime;//有效的开始时间,比如买狗了,生效时间
+
+    private Integer consumption;//当前物品对应的消耗品数量
+
+    private Date consumptionUpdate;//消耗品的更新时间
+
+    private Integer totalConsumption;//当前全部的狗粮
+
+    private Integer isShow;//是否显示狗,用户其他农场显示 默认显示: 1 ,隐藏:0
+
+    private Date startShowTime;//切换状态时候,更新一下这个显示的时间,用来计算双倍狗粮
+
+    private Date createTime;//
+
+    private Date updateTime;//
+
+}

+ 1316 - 0
src/main/java/com/td/boss/game/complayergoods/controller/ComPlayerGoodsController.java

@@ -0,0 +1,1316 @@
+package com.td.boss.game.complayergoods.controller;
+
+import com.td.boss.common.controller.*;
+import com.td.boss.common.pojo.Result;
+import com.td.boss.config.enums.ResultEnum;
+import com.td.boss.game.comexplainland.service.ComExplainLandService;
+import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
+import com.td.boss.game.comfruit.pojo.ComFruit;
+import com.td.boss.game.comfruit.service.ComFruitService;
+import com.td.boss.game.comfruit.vo.ComFruitAmountVo;
+import com.td.boss.game.comfruit.vo.ComFruitVo;
+import com.td.boss.game.commallother.pojo.ComMallOther;
+import com.td.boss.game.commallother.service.ComMallOtherService;
+import com.td.boss.game.commallother.vo.ComMallOtherVo;
+import com.td.boss.game.commallseed.service.ComMallSeedService;
+import com.td.boss.game.commallseed.vo.ComMallSeedVo;
+import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
+import com.td.boss.game.complayerdog.service.ComPlayerDogService;
+import com.td.boss.game.complayerdog.vo.ComPlayerDogVo;
+import com.td.boss.game.complayergoods.pojo.ComPlayerGoods;
+import com.td.boss.game.complayergoods.vo.ComPlayerGoodsSimpleVo;
+import com.td.boss.game.complayergoods.vo.ComPlayerGoodsVo;
+import com.td.boss.game.complayergoods.service.ComPlayerGoodsService;
+import com.td.boss.game.complayerland.pojo.ComPlayerLand;
+import com.td.boss.game.complayerland.pojo.ComPlayerLandAndCanSteal;
+import com.td.boss.game.complayerland.service.ComPlayerLandService;
+import com.td.boss.game.complayerland.vo.ComPlayerLandVo;
+import com.td.boss.game.complayerlog.pojo.ComPlayerLog;
+import com.td.boss.game.complayerlog.service.ComPlayerLogService;
+import com.td.boss.game.complayerlog.vo.ComPlayerLogVo;
+import com.td.boss.game.complayerprofit.pojo.ComPlayerProfit;
+import com.td.boss.game.complayerprofit.service.ComPlayerProfitService;
+import com.td.boss.game.complayerprofit.vo.ComPlayerProfitVo;
+import com.td.boss.game.complayers.vo.ComPlayersVo;
+import com.td.boss.game.complayersattri.service.ComPlayersAttriService;
+import com.td.boss.game.complayersattri.vo.ComPlayersAttriSimpleVo;
+import com.td.boss.game.complayersattri.vo.ComPlayersAttriVo;
+import com.td.boss.game.comsetting.pojo.ComSetting;
+import com.td.boss.game.comsetting.service.ComSettingService;
+import com.td.boss.game.comsetting.vo.ComSettingVo;
+import com.td.boss.game.comsnbtran.pojo.ComSnbTran;
+import com.td.boss.game.comsnbtran.service.ComSnbTranService;
+import com.td.boss.game.comsnbtran.vo.ComSnbTranVo;
+import com.td.boss.game.comusers.service.ComUsersService;
+import com.td.boss.game.comusers.vo.ComUsersVo;
+import com.td.boss.util.*;
+import net.bytebuddy.asm.Advice;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.StringUtils;
+import org.springframework.web.bind.annotation.*;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.stream.Collectors;
+
+@RestController
+@RequestMapping("/game/comPlayerGoods/")
+public class ComPlayerGoodsController extends CommonController<ComPlayerGoodsVo, ComPlayerGoods, String> {
+    @Autowired
+    private ComPlayerGoodsService comPlayerGoodsService;
+    @Autowired
+    private ComMallSeedService comMallSeedService;
+    @Autowired
+    private ComFruitService comFruitService;
+
+    @Autowired
+    private ComPlayerLandService comPlayerLandService;
+
+    @Autowired
+    private ComPlayerProfitService comPlayerProfitService;
+
+    @Autowired
+    private ComUsersService comUsersService;
+
+    @Autowired
+    private ComPlayersAttriService comPlayersAttriService;
+
+    //交易表service
+    @Autowired
+    private ComSnbTranService comSnbTranService;
+
+    //背包操作日志
+    @Autowired
+    private ComPlayerLogService comPlayerLogService;
+
+    @Autowired
+    private ComExplainLandService comExplainLandService;
+
+    //配置信息
+    @Autowired
+    private ComSettingService comSettingService;
+
+    @Autowired
+    private ComPlayerDogService comPlayerDogService;
+
+    @Autowired
+    private ComMallOtherService comMallOtherService;
+
+
+    @Autowired
+    private RedisLock redisLock;
+
+    /**
+     * 获取仓库 全部数据,目前使用goods_type区分种子和果实
+     *
+     * @param userId
+     * @return
+     */
+    @GetMapping("getSeedAndFruit")
+    public Result<Map> getSeedAndFruitFunction(@RequestParam(value = "userId") String userId) {
+        List<ComPlayerGoods> comPlayerGoodsList = comPlayerGoodsService.findAllByUserId(userId);
+        List<ComMallSeedVo> seedList = new ArrayList<>();
+        List<ComFruitAmountVo> fruitList = new ArrayList<>();
+        comPlayerGoodsList.stream().map(e -> {
+            if (e.getGoodsType().equals(0)) {
+                //种子类型
+                ComMallSeedVo comMallSeedVo = comMallSeedService.findById(e.getGoodsIndex());
+                //数量要用背包的数量
+                comMallSeedVo.setAmount(e.getAmount());
+                seedList.add(comMallSeedVo);
+
+            } else if (e.getGoodsType().equals(1)) {
+                //果实类型
+                //数量要用背包的数量
+                ComFruitVo comFruitVo = comFruitService.findById(e.getGoodsIndex());
+                ComFruitAmountVo comFruitAmountVo = CopyUtil.copy(comFruitVo, ComFruitAmountVo.class);
+                comFruitAmountVo.setAmount(e.getAmount());
+                comFruitAmountVo.setAmountPart(e.getAmountPart());
+                fruitList.add(comFruitAmountVo);
+            }
+            return e;
+        }).collect(Collectors.toList());
+        Map map = new HashMap();
+        map.put("seed", seedList);
+        map.put("fruit", fruitList);
+        return Result.of(map);
+    }
+
+
+    /**
+     * 获取购买的种子状态,如果webSocket未通,
+     * 使用轮询方法查询更新状态
+     *
+     * @param userId
+     * @return
+     */
+    @GetMapping("getSeedState")
+    public Result<Map> getSeedStateFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "seedId") Integer seedId,
+            @RequestParam(value = "currentAmount") Integer currentAmount
+    ) {
+        //查询背包种子存在的数量
+        ComPlayerGoods comPlayerGood = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, seedId, 0);
+        if (comPlayerGood == null) {
+            //如果背包种子是空的,说明还未添加上去
+            return Result.of(null, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
+        }
+        if (comPlayerGood.getAmount() <= currentAmount) {
+            //如果背包数量等于或者小于传进来的,提示轮询未更新
+            return Result.of(null, false, ResultEnum.SEED_AMOUNT_ERROR.getMessage(), ResultEnum.SEED_AMOUNT_ERROR.getCode());
+        } else {
+            Map map = new HashMap();
+            map.put("msg", "背包种子更新成功!");
+            map.put("goodsAmount", comPlayerGood.getAmount());
+            map.put("currentAmount", currentAmount);
+            return Result.of(map);
+        }
+
+
+    }
+
+
+    /**
+     * 收获时候,传入土地id和种子id
+     * <p>
+     * <p>
+     * 收获时候,操作用户snb时候,需要加锁,防止用户收取时候遗漏计算snb
+     *
+     * @param userId
+     * @param landId
+     * @return
+     */
+    @PostMapping("addFruit")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> addFruitFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "landId") Integer landId) {
+        //todo 确认下是否多倍收成
+        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, landId);
+        //土地数据不存在
+        if (comPlayerLand == null) {
+            return Result.of(null, false, ResultEnum.LAND_DATA_ERROR.getMessage(), ResultEnum.LAND_DATA_ERROR.getCode());
+        }
+        //土地未租赁
+        if (!comPlayerLand.getIsLease().equals(1)) {
+            return Result.of(null, false, ResultEnum.LAND_NOT_LEASE.getMessage(), ResultEnum.LAND_NOT_LEASE.getCode());
+        }
+        //土地未种植
+        if (comPlayerLand.getIsPlant().equals(0)) {
+            return Result.of(null, false, ResultEnum.LAND_NOT_PLANT.getMessage(), ResultEnum.LAND_NOT_PLANT.getCode());
+        }
+        //todo 判断土地租赁是否到期,是到期不给种植
+        //租赁的剩余天数 。
+        Long leaseDaysMill = comPlayerLand.getLeaseTime().getTime() - DateUtil.getNowDate().getTime();
+        if (leaseDaysMill <= 0) {
+            // 清除记录的数据
+            // 1.土地租赁到期,删除土地租赁数据
+            comPlayerLandService.delete(comPlayerLand.getId());
+            // 2.把删除的这个土地的数据记录到com_explain_land中去
+            ComExplainLandVo comExplainLandVo = new ComExplainLandVo();
+            comExplainLandVo = CopyUtil.copy(comPlayerLand, ComExplainLandVo.class);
+            comExplainLandVo.setLandId(comPlayerLand.getConfigLandId());
+            comExplainLandVo.setLandCreate(comPlayerLand.getCreateTime());
+            comExplainLandVo.setExplainType(0);
+            comExplainLandVo.setExplainDescribe("收获果实时:土地租赁过期回收");
+            comExplainLandVo.setCreateTime(new Date());
+            comExplainLandVo.setUpdateTime(new Date());
+            comExplainLandService.save(comExplainLandVo);
+            return Result.of(null, false, ResultEnum.LAND_LEASE_EXPIRED.getMessage(), ResultEnum.LAND_LEASE_EXPIRED.getCode());
+        }
+
+        //获取种植的种子
+        ComMallSeedVo comMallSeedVo = comMallSeedService.findById(comPlayerLand.getPlantId());
+        if (comMallSeedVo == null) {
+            return Result.of(null, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
+        }
+
+        // 收获,同时重置土地状态,先判断是否成熟
+        //当前时间减去 成熟期,判断是否成熟
+        Date _harvestTime = DateUtil.getNowDateMinusDay(comMallSeedVo.getMaturity());
+        // 两个时间差, 逻辑是_harvestTime 时间慢慢接近种植时间
+        long diff = comPlayerLand.getPlantStart().getTime() - _harvestTime.getTime();
+        //if (DateUtil.getNowDateMinusDay(comMallSeedVo.getMaturity()).getTime() < comPlayerLand.getPlantStart().getTime())
+        if (diff > 0) {
+            //如果当前时间减去种子的成熟期,还是小于种植日期,则判断未成熟
+            return Result.of(null, false, ResultEnum.SEED_DATE_ERROR.getMessage(), ResultEnum.SEED_DATE_ERROR.getCode());
+        }
+        //获取数据库相关配置
+        ComSettingVo comSettingVo = comSettingService.get("1").getData();
+        if (comSettingVo.equals(null)) {
+            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
+        }
+
+        //需要redis 加锁
+        long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();
+        //获取用户的plantFlag,因为之前的用户没有设置这个flag,所以设为第一次,用 userId 和 landId 组合成key
+        String _redisKey = comPlayerLand.getPlantFlag();
+        if (!StringUtils.hasText(_redisKey)) {
+            //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
+            _redisKey = comPlayerLand.getUserId() + "or" + comPlayerLand.getConfigLandId();
+        }
+        Map map = new HashMap();
+        //todo 总共偷去的数量
+        Double _stealProfits = 0d;
+        try {
+            // 如果存在plantFlag 说明是新种植的
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                return Result.of(null, false, ResultEnum.LAND_STEAL_LOCK.getMessage(), ResultEnum.LAND_STEAL_LOCK.getCode());
+            }
+
+            //设置一个未种植状态即可
+            comPlayerLand.setIsPlant(0);
+            ComPlayerLandVo comPlayerLandVo = CopyUtil.copy(comPlayerLand, ComPlayerLandVo.class);
+            comPlayerLandService.save(comPlayerLandVo);
+
+            //计算一个收获量, 租赁倍数* 租赁日期下的产量
+            Integer _amount = 0;
+            Double _amountPart = 0d;
+            if (comPlayerLand.getLeaseDate().equals(1)) {
+                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
+            } else if (comPlayerLand.getLeaseDate().equals(2)) {
+                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
+            } else if (comPlayerLand.getLeaseDate().equals(3)) {
+                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
+            }
+            //todo 收获果实时候,需要减去一个被偷取的利润,然后判断一个是否大于设置的利润,超出了报异常,并且收取的数量不能为小于0
+            //     拿出计算利润,收获量减去种子的成本(snb)后的百分之20 ,后面需要后台可调整
+            Integer _profit = _amount - comPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
+            // 配置的参数。这里先直接定义
+            Double profitConfig = comSettingVo.getProfit(), stealRatioMaxConfig = comSettingVo.getStealMaxRatio(), stealRatioMinConfig = comSettingVo.getStealMinRatio(), finallyGetRatioConfig = comSettingVo.getFinalRatio();
+
+            Double _profitDouble = _profit.doubleValue();
+            Double _residualProfit = DoubleUtil.mul(_profitDouble, DoubleUtil.sub(1d, profitConfig));//如果 profitConfig 0.2,剩余利润就是0.8;
+            Double _stealAmount = DoubleUtil.sub(_profitDouble, _residualProfit); //可偷取的利润
+
+            //todo 计算总共损失的数量
+            List<ComPlayerProfit> comPlayerProfits = comPlayerProfitService.findByUserIdAndPlantFlagAndLandId(userId, _redisKey, comPlayerLand.getConfigLandId());
+            for (int i = 0; i < comPlayerProfits.size(); i++) {
+                ComPlayerProfit temp = comPlayerProfits.get(i);
+                _stealProfits = DoubleUtil.add(_stealProfits, temp.getStolen());
+            }
+            Double _userHarvestPart = DoubleUtil.sub(_stealAmount, _stealProfits);
+            if (_userHarvestPart < 0.0) {
+                //差值不能为负数
+                _userHarvestPart = 0d;
+                //相当于被偷取全部利润,这里返回一个被偷去的全部利润值
+                map.put("lossAmount", _stealAmount);
+            } else {
+                //todo 返回一个被偷的数量
+                map.put("lossAmount", _stealProfits);
+            }
+
+            //todo 利润
+            _amount -= _profit;
+            //用户收取的对应数量,固定收入+被偷取后剩余的部分
+            _amountPart = DoubleUtil.add(_residualProfit, _userHarvestPart);
+            //果实 Type =1
+            ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
+            Integer _beforeAmount = 0;
+            Double _beforeProfitPart = 0d, _afterProfitPart = 0d;
+            if (comPlayerGoodsSimpleVo == null) {
+                //背包增加果实
+                comPlayerGoodsSimpleVo = new ComPlayerGoods();
+                comPlayerGoodsSimpleVo.setUserId(userId);
+                comPlayerGoodsSimpleVo.setGoodsIndex(comMallSeedVo.getHarvestId());
+                comPlayerGoodsSimpleVo.setGoodsType(1);
+                comPlayerGoodsSimpleVo.setName(comMallSeedVo.getHarvestName());
+                //用种子的picture
+                comPlayerGoodsSimpleVo.setPictureName(comMallSeedVo.getPicture());
+                comPlayerGoodsSimpleVo.setAmount(_amount);
+                comPlayerGoodsSimpleVo.setAmountPart(_amountPart);
+
+                _beforeAmount = 0;
+                _beforeProfitPart = 0d;
+                _afterProfitPart = comPlayerGoodsSimpleVo.getAmountPart();
+            } else {
+                _beforeAmount = comPlayerGoodsSimpleVo.getAmount();
+                _beforeProfitPart = comPlayerGoodsSimpleVo.getAmountPart();
+
+                comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() + _amount);
+                //用户利润部分
+                comPlayerGoodsSimpleVo.setAmountPart(DoubleUtil.add(comPlayerGoodsSimpleVo.getAmountPart(), _amountPart));
+
+                _afterProfitPart = comPlayerGoodsSimpleVo.getAmountPart();
+            }
+
+            ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+            comPlayerGoodsService.save(comPlayerGoodsVo);
+
+            //todo 记录一个收获的操作日志
+            ComPlayerLog _playerLog = new ComPlayerLog();
+            _playerLog.setUserId(userId);
+            _playerLog.setTId(comMallSeedVo.getHarvestId());
+            _playerLog.setTName(comMallSeedVo.getHarvestName());
+            _playerLog.setTType(0);//设置一个默认水果id
+            _playerLog.setTAmount(_amount);
+            _playerLog.setBeforeAmount(_beforeAmount);
+            ////交易后的数据
+            _playerLog.setAfterAmount(comPlayerGoodsSimpleVo.getAmount());
+            //收取时候信息记录
+            _playerLog.setTPart(_amountPart);
+            _playerLog.setTLoss(_stealProfits); //损失的部分
+            _playerLog.setBeforePart(_beforeProfitPart);
+            _playerLog.setAfterPart(_afterProfitPart);
+            _playerLog.setLMultiple(comPlayerLand.getLeaseMultiple());
+            ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
+            comPlayerLogService.save(_playerLogVo);
+
+            //对应的,存在plantFlag则解锁
+            redisLock.unlock(_redisKey, String.valueOf(time));
+        } catch (Exception e) {
+            //对应的,存在plantFlag则解锁
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+        map.put("msg", "成功收取果实!");
+        return Result.of(map);
+    }
+
+    /**
+     * 按土地收取水果果实时候,传入对方用户id 和土地id
+     * <p>
+     * <p>
+     * 收获时候,操作用户snb时候,需要加锁,防止用户收取时候遗漏计算snb
+     *
+     * @param otherUserId
+     * @param otherLandId
+     * @return
+     */
+    @PostMapping("stealFruit")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> stealFruitFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "otherUserId") String otherUserId,
+            @RequestParam(value = "otherLandId") Integer otherLandId) {
+        Map map = new HashMap();
+
+        //todo 计入狗和打狗棒
+        Boolean _dogWork = false;//狗是否工作,默认不工作
+        Boolean _wasTheDogBitten = false; //默认没有被狗咬
+        Boolean _holdTheStick = false;//默认没持有棒子
+        Boolean _hasReduceSnb = false;//是否可以扣除偷菜的snb
+        //对方的狗
+        ComPlayerDog _otherDogVo = comPlayerDogService.findByUserIdAndType(otherUserId, 0);
+        if (_otherDogVo != null) {
+            //有狗或者有狗粮时候,处理狗的事件
+            //todo 计算狗是否到期
+            long _runDay = DateUtil.getDays(DateUtil.getNowDate().getTime() - _otherDogVo.getEffectiveStartTime().getTime());
+            int _remainingDay = _otherDogVo.getEffectiveDay() - (int) _runDay;
+            // 存在时间
+            if (_remainingDay > 0) {
+                //todo 这里判断狗粮是否足够
+                long _diffConsumptionMill = _otherDogVo.getConsumptionUpdate().getTime() - DateUtil.getNowDate().getTime();
+                long _workHours = DateUtil.getHours(_diffConsumptionMill);
+                //工作多少个小时,消耗多少狗粮
+                long _needConsumption = _otherDogVo.getConsumption() - _workHours;
+                if (_needConsumption > 0) {
+                    //口粮足够,工作
+                    _dogWork = true;
+                    //狗存在并工作,获取狗的数据
+                    ComMallOtherVo _mallDogData = comMallOtherService.get(_otherDogVo.getOtherIndex()).getData();
+                    Double _triggerPro = _mallDogData.getTriggerPro();//触发概率
+                    if (new Random().nextDouble() <= _triggerPro) {
+                        //被狗咬了
+                        _wasTheDogBitten = true;
+                    }
+                }
+            }
+
+        }
+
+        //
+        //己方的打狗棒
+        ComPlayerDog _beatDogStick = comPlayerDogService.findByUserIdAndType(userId, 1);
+
+        if (_beatDogStick != null) {
+            //todo 判断打狗棒是否到期
+            long _stickRunDay = DateUtil.getDays(DateUtil.getNowDate().getTime() - _beatDogStick.getEffectiveStartTime().getTime());
+            int _stickRemainingDay = _beatDogStick.getEffectiveDay() - (int) _stickRunDay;
+            if(_stickRemainingDay>0){
+                //持有打狗棒
+                _holdTheStick = true;
+            }
+        }
+
+        //获取数据库相关配置
+        ComSettingVo comSettingVo = comSettingService.get("1").getData();
+        if (comSettingVo.equals(null)) {
+            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
+        }
+        //todo 后台可配置,获取一个链上数据
+        Integer _configStrength = comSettingVo.getMaxStrength(), _chainStrength = DappUtil.getChildrenCount(userId);
+        //前端的显示,输出一个不小于0的体力值
+        Integer _maxStrength = _configStrength + _chainStrength;
+        //1.先判断用户是否有足够的体力,根据snb判断。2. 收取到果实,扣除用户一个体力值
+        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+        ComPlayersAttriVo playersAttribute = comPlayersAttriService.findByUserId(userId);
+        Integer _out = _maxStrength - Math.abs(playersAttribute.getStrength());
+        Double _userAllSnb = DoubleUtil.add(comUsersVo.getSnb().doubleValue(), comUsersVo.getSnbPart());
+        //判断是否有足够的snb
+        if (DoubleUtil.compare(_userAllSnb, comSettingVo.getDeductSnb()) == -1) {
+            _hasReduceSnb = false;
+            //没有足够的snb
+            //如果没有被狗咬,,正常扣除1点体力
+            //如果被狗咬了,除了正常扣除1点体力外,额外扣除1点体力
+            if (_wasTheDogBitten) {
+                //判断是否有双倍体力
+                if (_out < comSettingVo.getSnbUnitStrength()) {
+                    map.put("msg", "需要双倍体力:" + comSettingVo.getSnbUnitStrength());
+                    return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
+                }
+            }
+        } else {
+            _hasReduceSnb = true;
+            //正常判断体力
+            if (_out < comSettingVo.getUnitStrength()) {
+                map.put("msg", "需要单倍体力:" + comSettingVo.getUnitStrength());
+                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
+            }
+        }
+
+        //List<Map> _stealLandInfos = new ArrayList<>();
+        Double _finalStealAmountSum = 0d;
+        //todo 偷取用户目标的信息,即otherUserId和otherLandId
+        ComPlayerLand otherPlayerLand = comPlayerLandService.findByUserIdAndLandId(otherUserId, otherLandId);
+        if (otherPlayerLand == null) {
+            return Result.of(null, false, ResultEnum.LAND_CAN_STEAL_IS_NULL.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_NULL.getCode());
+        }
+        //snb 的key
+        String _redisSNBKey = "SNB_SAVE_" + userId;
+        //获取用户的plantFlag,因为之前的用户没有设置这个flag,所以设为第一次,用 userId 和 landId 组合成key
+        String _redisKey = otherPlayerLand.getPlantFlag();
+        if (!StringUtils.hasText(_redisKey)) {
+            //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
+            _redisKey = otherPlayerLand.getUserId() + "or" + otherPlayerLand.getConfigLandId();
+        }
+        //todo 先判断是否偷取过
+        ComPlayerProfit comPlayerProfit = comPlayerProfitService.findByUserIdAndOtherUserIdAndPlantFlag(userId, otherUserId, _redisKey);
+        if (comPlayerProfit != null) {
+            return Result.of(null, false, ResultEnum.LAND_HARVEST_STOLEN.getMessage(), ResultEnum.LAND_HARVEST_STOLEN.getCode());
+        }
+        //获取当前种植的种子
+        ComMallSeedVo comMallSeedVo = comMallSeedService.findById(otherPlayerLand.getPlantId());
+        //需要redis 加锁
+        long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();
+
+        try {
+            if (!redisLock.lock(_redisSNBKey, String.valueOf(time))) {
+                //如果有snb冲突锁
+                return Result.of(null, false, ResultEnum.USER_LOGIN_LOCK.getMessage(), ResultEnum.USER_LOGIN_LOCK.getCode());
+
+            }
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                //如果有冲突锁
+                return Result.of(null, false, ResultEnum.LAND_STEAL_LOCK.getMessage(), ResultEnum.LAND_STEAL_LOCK.getCode());
+            }
+            //todo 如果当前人员偷窃水果。记录一个信息,存储当前玩家已偷过的水果的信息
+            //计算一个偷取的收获量, 租赁倍数* 租赁日期下的产量
+            Integer _otherAmount = 0;
+            if (otherPlayerLand.getLeaseDate().equals(1)) {
+                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
+            } else if (otherPlayerLand.getLeaseDate().equals(2)) {
+                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
+            } else if (otherPlayerLand.getLeaseDate().equals(3)) {
+                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
+            }
+            //todo 拿出计算利润,收获量减去种子的成本(snb)后的百分之30 ,后面需要后台可调整
+            Integer _profit = _otherAmount - otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
+            // 配置的参数。这里先直接定义
+            // 需要根据 狗是否生效,小偷是否装备打狗棒来确定参数
+            Double profitConfig = _dogWork ? comSettingVo.getProfitDog() : comSettingVo.getProfit(),
+                    stealRatioMaxConfig = comSettingVo.getStealMaxRatio(),
+                    stealRatioMinConfig = comSettingVo.getStealMinRatio(),
+                    finallyGetRatioConfig = comSettingVo.getFinalRatio();
+            Double _stealRatio = new Random().nextDouble() * DoubleUtil.sub(stealRatioMaxConfig, stealRatioMinConfig) + stealRatioMinConfig;// 1%-3%
+            //todo 被偷取的人扣除 一个损失量,初利润算 _profit
+            //  如果狗生效,根据装备了打狗棒,设置对应的偷窃比例
+            if (_dogWork) {
+                _stealRatio = _holdTheStick ? comSettingVo.getStealRatioHasStick() : comSettingVo.getStealRatioNoStick();
+            }
+            Double _stolenAmount = DoubleUtil.mul(_profit.doubleValue(), _stealRatio);
+            //这里根据plantFlag判断目标用户当前种植被偷取完,记录更新用户 comPlayerLand plantSteal 字段;保存一个偷取状态
+            Double _sumStolen = comPlayerProfitService.getStolenSumByOtherUserIdAndPlantFlag(otherUserId, _redisKey);
+            //todo 这里的可偷利润应该是最大值,用户不能超过这个,需要限制判断
+            Double _maxAmount = DoubleUtil.mul(_profit.doubleValue(), profitConfig); //可偷取的利润
+            //1 单偷操作 就是 _sumStolen>_maxAmount 或者 _sumStolen == _maxAmount ,被偷完了
+            if (!DoubleUtil.compare(_sumStolen, _maxAmount).equals(-1)) {
+                // 需要redis 解锁
+                redisLock.unlock(_redisKey, String.valueOf(time));
+                redisLock.unlock(_redisSNBKey, String.valueOf(time));
+                //记录一个不可偷取的状态
+                otherPlayerLand.setPlantSteal(0);
+                comPlayerLandService.save(CopyUtil.copy(otherPlayerLand, ComPlayerLandVo.class));
+                map.put("plant_steal", otherPlayerLand.getPlantSteal());
+                return Result.of(map, false, ResultEnum.LAND_CAN_STEAL_IS_MAX.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_MAX.getCode());
+            }
+            //计算剩下可偷利润的差,防止过多偷取用户利润 _stolenAmount
+            Double _diff = Math.abs(DoubleUtil.sub(_maxAmount, _sumStolen));
+            if (DoubleUtil.compare(_stolenAmount, _diff).equals(1)) {
+                //_stolenAmount>_diff
+                _stolenAmount = _diff;
+            }
+            //最终偷窃人获取的是偷取的是目标用户损失量的10% finallyGetRatioConfig
+            Double _finalStealAmount = DoubleUtil.mul(_stolenAmount, finallyGetRatioConfig);
+            //偷窃后,系统回收部分
+            Double _lostStealAmount = DoubleUtil.sub(1d, finallyGetRatioConfig) * _stolenAmount;
+            //果实 Type =1
+            //todo 增加一个字段处理背包果实,拿出当前用户背包果实
+            ComPlayerGoods comPlayerGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
+            Double _beforeStealAmount = 0.0;
+            if (comPlayerGoods == null) {
+                //背包增加收取到别的用户对应的类型数量。比如当前是偷取到的果实
+                comPlayerGoods = new ComPlayerGoods();
+                comPlayerGoods.setUserId(userId);
+                comPlayerGoods.setGoodsIndex(comMallSeedVo.getHarvestId());
+                comPlayerGoods.setGoodsType(1);
+                comPlayerGoods.setName(comMallSeedVo.getHarvestName());
+                //用种子的picture
+                comPlayerGoods.setPictureName(comMallSeedVo.getPicture());
+                //amount 设置0
+                comPlayerGoods.setAmount(0);
+                comPlayerGoods.setAmountPart(_finalStealAmount);
+                _beforeStealAmount = 0.0;
+            } else {
+                _beforeStealAmount = comPlayerGoods.getAmountPart();
+                //修改偷取字段
+                comPlayerGoods.setAmountPart(DoubleUtil.add(comPlayerGoods.getAmountPart(), _finalStealAmount));
+            }
+            ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoods, ComPlayerGoodsVo.class);
+            comPlayerGoodsService.save(comPlayerGoodsVo);
+
+            //todo 偷窃损失,处理目标用户收取果实时候,减扣一部分被偷取的数量
+            comPlayerProfit = new ComPlayerProfit();
+            comPlayerProfit.setUserId(userId);
+            comPlayerProfit.setTargetId(otherUserId);
+            comPlayerProfit.setPlantFlag(_redisKey);
+            comPlayerProfit.setLandId(otherPlayerLand.getConfigLandId());
+            comPlayerProfit.setLeaseMultiple(otherPlayerLand.getLeaseMultiple());
+            comPlayerProfit.setLeaseDate(otherPlayerLand.getLeaseDate());
+            comPlayerProfit.setHarvest(_otherAmount);
+            comPlayerProfit.setProfit(_profit.doubleValue());//目标用户可偷的初始利润值
+            comPlayerProfit.setStolen(_stolenAmount);//目标用户被偷的数量
+            comPlayerProfit.setFinalSteal(_finalStealAmount);//用户最终偷取的量,是被偷的数量 10%左右
+            comPlayerProfit.setProfitAfter(DoubleUtil.sub(_profit.doubleValue(), DoubleUtil.add(_stolenAmount, _sumStolen))); //可偷的减去被偷的
+            //记录相关比例
+            comPlayerProfit.setProfitRatio(profitConfig);
+            comPlayerProfit.setStealRatio(_stealRatio);
+            comPlayerProfit.setFinalRatio(finallyGetRatioConfig);
+            ComPlayerProfitVo comPlayerProfitVo = CopyUtil.copy(comPlayerProfit, ComPlayerProfitVo.class);
+            comPlayerProfitService.save(comPlayerProfitVo);
+
+            //todo 记录一个收获的操作日志,这里的数量都与偷窃知道相关。amountPart,注意不是果实实际总数 amount
+            ComPlayerLog _playerLog = new ComPlayerLog();
+            _playerLog.setUserId(userId);
+            _playerLog.setTId(comMallSeedVo.getHarvestId());
+            _playerLog.setTName(comMallSeedVo.getHarvestName());
+            _playerLog.setTType(6);//偷取果实时候,type设置6
+            //偷水果时候这部分数据应该是不变的
+            _playerLog.setTAmount(0);
+            _playerLog.setBeforeAmount(comPlayerGoods.getAmount());
+            _playerLog.setAfterAmount(comPlayerGoods.getAmount());
+            //偷取用户果实时候,收取时候信息记录
+            _playerLog.setBeforePart(_beforeStealAmount);
+            _playerLog.setTPart(_finalStealAmount);
+            _playerLog.setTLoss(_lostStealAmount); //损失的部分
+            _playerLog.setAfterPart(comPlayerGoods.getAmountPart());
+            _playerLog.setLMultiple(otherPlayerLand.getLeaseMultiple());
+            ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
+            comPlayerLogService.save(_playerLogVo);
+
+            //todo 成功收取记录一个土地id
+            Map _landMap = new HashMap();
+            //记录一个id
+            _landMap.put("landId", otherPlayerLand.getConfigLandId());
+            //
+            _landMap.put("stealAmount", _finalStealAmount);
+
+            //固定成本
+            _landMap.put("cost", otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb());
+            //当前利润
+            _landMap.put("profit", _profit);
+            //打印一个比例
+            _landMap.put("profitRatio", profitConfig);
+            _landMap.put("intervalStealRatio", _stealRatio);
+            _landMap.put("finallyGetRatioConfig", finallyGetRatioConfig);
+
+            _finalStealAmountSum = DoubleUtil.add(_finalStealAmount, _finalStealAmountSum);
+            // 需要redis 解锁
+            redisLock.unlock(_redisKey, String.valueOf(time));
+
+            map.put("msg", "偷取果实.");
+            map.put("stealInfo", _landMap);
+            map.put("stealSum", _finalStealAmountSum);
+
+            //todo 体力值可以后台配置
+            Integer _addStrength = comSettingVo.getUnitStrength();
+            if (_hasReduceSnb) {
+                /**
+                 * todo 偷取果实时候snb变动
+                 */
+                ComSnbTranVo _snbTran = new ComSnbTranVo();
+                _snbTran.setBeforeSnb(comUsersVo.getSnb());
+                _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
+
+                //如果有snb,扣除对应的snb
+                //先判断 SnbPart 部分是否有足够来扣取
+                if (DoubleUtil.compare(comUsersVo.getSnbPart(), comSettingVo.getDeductSnb()) == -1) {
+                    //如果不足,取1扣取
+                    Integer _deductSnbMaxInt = (int) Math.ceil(comSettingVo.getDeductSnb());
+                    comUsersVo.setSnb(comUsersVo.getSnb() - _deductSnbMaxInt);
+                    Double _deductSnbPart = DoubleUtil.sub(1d, comSettingVo.getDeductSnb());
+                    comUsersVo.setSnbPart(DoubleUtil.add(comUsersVo.getSnbPart(), _deductSnbPart));
+
+                    _snbTran.setTranSnb(0);
+                    _snbTran.setTranSnbPart(comSettingVo.getDeductSnb());
+                } else {
+                    //直接用snbPart 减去小数点部分即可
+                    Double _deductSnbPart = DoubleUtil.sub(comUsersVo.getSnbPart(), comSettingVo.getDeductSnb());
+                    comUsersVo.setSnbPart(_deductSnbPart);
+                    _snbTran.setTranSnb(0);//整数部分没有进行交易
+                    _snbTran.setTranSnbPart(comSettingVo.getDeductSnb());
+                }
+                //记录修改的数据
+                comUsersService.save(comUsersVo);
+
+                //记录果实id,记录交易数据
+                _snbTran.setTranId(comMallSeedVo.getHarvestId().toString());
+                _snbTran.setUserId(userId);
+                _snbTran.setTranName("偷取果实");
+                _snbTran.setTranType(4);
+                _snbTran.setTranAmount(0); //此数量会操作背包记录
+                _snbTran.setTranAmountPart(_finalStealAmount);
+                _snbTran.setTranPrice(0);
+                _snbTran.setTranDescribe("偷取用户果实消耗的snb");
+                _snbTran.setIsAdd(0);//减少
+                _snbTran.setAfterSnb(comUsersVo.getSnb());
+                _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
+                comSnbTranService.save(_snbTran);
+
+            } else {
+                //被狗咬掉双倍体力
+                if (_wasTheDogBitten) {
+                    _addStrength = comSettingVo.getSnbUnitStrength();
+                }
+            }
+
+            playersAttribute.setStrength(playersAttribute.getStrength() + _addStrength);
+            comPlayersAttriService.save(playersAttribute);
+            ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttribute, ComPlayersAttriSimpleVo.class);
+            //前端的显示,输出一个不小于0的体力值
+            Integer _outEnd = _maxStrength - Math.abs(playersAttribute.getStrength());
+            comPlayersAttriSimpleVo.setCurrentStrength(_outEnd < 0 ? 0 : _outEnd);
+            //最大值是配置的值和链上数据的值相加
+            comPlayersAttriSimpleVo.setMaxStrength(_maxStrength);
+
+            map.put("playerAttribute", comPlayersAttriSimpleVo);
+
+            //输出对应的判定值
+            map.put("dogWork", _dogWork);
+            map.put("wasTheDogBitten", _wasTheDogBitten);
+            map.put("holdTheStick", _holdTheStick);
+            map.put("hasReduceSnb", _hasReduceSnb);
+            //如果被狗咬后扣除的snb,和对应扣除的体力
+            map.put("deductSnb", comSettingVo.getDeductSnb());
+            map.put("deductStrength", _addStrength);
+
+            //用户snb
+            map.put("snb", comUsersVo.getSnb());
+            map.put("snbPart", comUsersVo.getSnbPart());
+
+            //解除snb锁
+            redisLock.unlock(_redisSNBKey, String.valueOf(time));
+            return Result.of(map);
+        } catch (Exception e) {
+            // 需要redis 解锁
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            redisLock.unlock(_redisSNBKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+
+    }
+
+
+    /**
+     * 处理一键收取水果果实时候,传入对方用户id 即可
+     * <p>
+     * <p>
+     * 收获时候,操作用户snb时候,需要加锁,防止用户收取时候遗漏计算snb
+     *
+     * @param otherUserId
+     * @return
+     */
+    @PostMapping("stealAllFruit")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> stealAllFruitFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "otherUserId") String otherUserId) {
+
+        Map map = new HashMap();
+
+        //获取数据库相关配置
+        ComSettingVo comSettingVo = comSettingService.get("1").getData();
+        if (comSettingVo.equals(null)) {
+            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
+        }
+        //todo 后台可配置,获取一个链上数据
+        Integer _configStrength = comSettingVo.getMaxStrength(), _chainStrength = DappUtil.getChildrenCount(userId);
+        //前端的显示,输出一个不小于0的体力值
+        Integer _maxStrength = _configStrength + _chainStrength;
+        //1.先判断用户是否有足够的体力,根据snb判断。2. 收取到果实,扣除用户一个体力值
+        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+        ComPlayersAttriVo playersAttribute = comPlayersAttriService.findByUserId(userId);
+        Integer _out = _maxStrength - Math.abs(playersAttribute.getStrength());
+        if (comUsersVo.getSnb() < comSettingVo.getDeductSnb()) {
+            //判断是否有双倍体力
+            if (_out < comSettingVo.getSnbUnitStrength()) {
+                map.put("msg", "需要双倍体力:" + comSettingVo.getSnbUnitStrength());
+                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
+            }
+        } else {
+            //正常判断体力
+            if (_out < comSettingVo.getUnitStrength()) {
+                map.put("msg", "需要单倍体力:" + comSettingVo.getUnitStrength());
+                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
+            }
+        }
+
+        List<Map> _stealLandInfos = new ArrayList<>();
+        Double _finalStealAmountSum = 0d;
+        //todo 偷取用户的对象信息等id
+        List<ComPlayerLandAndCanSteal> otherPlayerLandAndCanSteal = comPlayerLandService.findCanStealByUserIdAndOtherUserId(userId, otherUserId);
+        //List<ComPlayerLand> otherPlayerLands = comPlayerLandService.findAllByCanStealOtherLands(userId, otherUserId);
+        List<ComPlayerLand> otherPlayerLands = new ArrayList<>();
+        for (int i = 0; i < otherPlayerLandAndCanSteal.size(); i++) {
+            if (otherPlayerLandAndCanSteal.get(i).getCanSteal().equals(1)) {
+                otherPlayerLands.add(CopyUtil.copy(otherPlayerLandAndCanSteal.get(i), ComPlayerLand.class));
+            }
+        }
+        if (otherPlayerLands.size() == 0) {
+            return Result.of(null, false, ResultEnum.LAND_CAN_STEAL_IS_ZERO.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_ZERO.getCode());
+        }
+
+        for (int i = 0; i < otherPlayerLands.size(); i++) {
+            ComPlayerLand otherPlayerLand = otherPlayerLands.get(i);
+            //获取当前种植的种子
+            ComMallSeedVo comMallSeedVo = comMallSeedService.findById(otherPlayerLand.getPlantId());
+            //需要redis 加锁
+            long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();
+            //获取用户的plantFlag,因为之前的用户没有设置这个flag,所以设为第一次,用 userId 和 landId 组合成key
+            String _redisKey = otherPlayerLand.getPlantFlag();
+            if (!StringUtils.hasText(_redisKey)) {
+                //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
+                _redisKey = otherPlayerLand.getUserId() + "or" + otherPlayerLand.getConfigLandId();
+            }
+            try {
+                if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                    //如果有冲突锁,跳过
+                    continue;
+                }
+                //todo 如果当前人员偷窃水果。记录一个信息,存储当前玩家已偷过的水果的信息
+                //计算一个偷取的收获量, 租赁倍数* 租赁日期下的产量
+                Integer _otherAmount = 0;
+                if (otherPlayerLand.getLeaseDate().equals(1)) {
+                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
+                } else if (otherPlayerLand.getLeaseDate().equals(2)) {
+                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
+                } else if (otherPlayerLand.getLeaseDate().equals(3)) {
+                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
+                }
+                //todo 拿出计算利润,收获量减去种子的成本(snb)后的百分之30 ,后面需要后台可调整
+                Integer _profit = _otherAmount - otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
+                // 配置的参数。这里先直接定义
+                Double profitConfig = comSettingVo.getProfit(), stealRatioMaxConfig = comSettingVo.getStealMaxRatio(), stealRatioMinConfig = comSettingVo.getStealMinRatio(), finallyGetRatioConfig = comSettingVo.getFinalRatio();
+                Double _stealRatio = new Random().nextDouble() * DoubleUtil.sub(stealRatioMaxConfig, stealRatioMinConfig) + stealRatioMinConfig;// 1%-3%
+                //todo 被偷取的人扣除 一个损失量,初利润算 _profit
+                Double _stolenAmount = DoubleUtil.mul(_profit.doubleValue(), _stealRatio);
+                //这里根据plantFlag判断目标用户当前种植被偷取完,看下是否需要处理更新前端信息,暂不处理
+                Double _sumStolen = comPlayerProfitService.getStolenSumByOtherUserIdAndPlantFlag(otherUserId, _redisKey);
+                //todo 这里的可偷利润应该是最大值,用户不能超过这个,需要限制判断
+                Double _maxAmount = DoubleUtil.mul(_profit.doubleValue(), profitConfig); //可偷取的利润
+                //1 一键 就是 _sumStolen>_maxAmount 或者 _sumStolen == _maxAmount ,被偷完了
+                if (!DoubleUtil.compare(_sumStolen, _maxAmount).equals(-1)) {
+                    // 需要redis 解锁
+                    redisLock.unlock(_redisKey, String.valueOf(time));
+                    //记录一个不可偷取的状态
+                    otherPlayerLand.setPlantSteal(0);
+                    comPlayerLandService.save(CopyUtil.copy(otherPlayerLand, ComPlayerLandVo.class));
+                    continue;
+                }
+                //计算剩下可偷利润的差,防止过多偷取用户利润 _stolenAmount
+                Double _diff = Math.abs(DoubleUtil.sub(_maxAmount, _sumStolen));
+                if (DoubleUtil.compare(_stolenAmount, _diff).equals(1)) {
+                    //_stolenAmount>_diff
+                    _stolenAmount = _diff;
+                }
+                //最终偷窃人获取的是偷取的是目标用户损失量的10% finallyGetRatioConfig
+                Double _finalStealAmount = DoubleUtil.mul(_stolenAmount, finallyGetRatioConfig);
+                //偷窃后,系统回收部分
+                Double _lostStealAmount = DoubleUtil.sub(1d, finallyGetRatioConfig) * _stolenAmount;
+                //果实 Type =1
+                //todo 增加一个字段处理背包果实,拿出当前用户背包果实
+                ComPlayerGoods comPlayerGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
+                Double _beforeStealAmount = 0.0;
+                if (comPlayerGoods == null) {
+                    //背包增加收取到别的用户对应的类型数量。比如当前是偷取到的果实
+                    comPlayerGoods = new ComPlayerGoods();
+                    comPlayerGoods.setUserId(userId);
+                    comPlayerGoods.setGoodsIndex(comMallSeedVo.getHarvestId());
+                    comPlayerGoods.setGoodsType(1);
+                    comPlayerGoods.setName(comMallSeedVo.getHarvestName());
+                    //用种子的picture
+                    comPlayerGoods.setPictureName(comMallSeedVo.getPicture());
+                    //amount 设置0
+                    comPlayerGoods.setAmount(0);
+                    comPlayerGoods.setAmountPart(_finalStealAmount);
+                    _beforeStealAmount = 0.0;
+                } else {
+                    _beforeStealAmount = comPlayerGoods.getAmountPart();
+                    //修改偷取字段
+                    comPlayerGoods.setAmountPart(DoubleUtil.add(comPlayerGoods.getAmountPart(), _finalStealAmount));
+                }
+                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoods, ComPlayerGoodsVo.class);
+                comPlayerGoodsService.save(comPlayerGoodsVo);
+
+                //todo 偷窃损失,处理目标用户收取果实时候,减扣一部分被偷取的数量
+                ComPlayerProfit comPlayerProfit = new ComPlayerProfit();
+                comPlayerProfit.setUserId(userId);
+                comPlayerProfit.setTargetId(otherUserId);
+                comPlayerProfit.setPlantFlag(_redisKey);
+                comPlayerProfit.setLandId(otherPlayerLand.getConfigLandId());
+                comPlayerProfit.setLeaseMultiple(otherPlayerLand.getLeaseMultiple());
+                comPlayerProfit.setLeaseDate(otherPlayerLand.getLeaseDate());
+                comPlayerProfit.setHarvest(_otherAmount);
+                comPlayerProfit.setProfit(_profit.doubleValue());//目标用户可偷的初始利润值
+                comPlayerProfit.setStolen(_stolenAmount);//目标用户被偷的数量
+                comPlayerProfit.setFinalSteal(_finalStealAmount);//用户最终偷取的量,是被偷的数量 10%左右
+                comPlayerProfit.setProfitAfter(DoubleUtil.sub(_profit.doubleValue(), DoubleUtil.add(_stolenAmount, _sumStolen))); //可偷的减去被偷的
+                //记录相关比例
+                comPlayerProfit.setProfitRatio(profitConfig);
+                comPlayerProfit.setStealRatio(_stealRatio);
+                comPlayerProfit.setFinalRatio(finallyGetRatioConfig);
+                ComPlayerProfitVo comPlayerProfitVo = CopyUtil.copy(comPlayerProfit, ComPlayerProfitVo.class);
+                comPlayerProfitService.save(comPlayerProfitVo);
+
+                //todo 记录一个收获的操作日志,这里由于偷取果实是有小数点的,小数点处理成整数,单位 暂定5位 100000
+                //      这里的数量都与偷窃知道相关。amountPart,注意不是果实实际总数 amount
+                ComPlayerLog _playerLog = new ComPlayerLog();
+                _playerLog.setUserId(userId);
+                _playerLog.setTId(comMallSeedVo.getHarvestId());
+                _playerLog.setTName(comMallSeedVo.getHarvestName());
+                _playerLog.setTType(6);//偷取果实时候,type设置6
+                //偷水果时候这部分数据应该是不变的
+                _playerLog.setTAmount(0);
+                _playerLog.setBeforeAmount(comPlayerGoods.getAmount());
+                _playerLog.setAfterAmount(comPlayerGoods.getAmount());
+                //偷取用户果实时候,收取时候信息记录
+                _playerLog.setBeforePart(_beforeStealAmount);
+                _playerLog.setTPart(_finalStealAmount);
+                _playerLog.setTLoss(_lostStealAmount); //损失的部分
+                _playerLog.setAfterPart(comPlayerGoods.getAmountPart());
+                _playerLog.setLMultiple(otherPlayerLand.getLeaseMultiple());
+                ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
+                comPlayerLogService.save(_playerLogVo);
+
+                //todo 成功收取记录一个土地id
+                Map _landMap = new HashMap();
+                //记录一个id
+                _landMap.put("landId", otherPlayerLand.getConfigLandId());
+                //
+                _landMap.put("stealAmount", _finalStealAmount);
+
+                //固定成本
+                _landMap.put("cost", otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb());
+                //当前利润
+                _landMap.put("profit", _profit);
+                //打印一个比例
+                _landMap.put("profitRatio", profitConfig);
+                _landMap.put("intervalStealRatio", _stealRatio);
+                _landMap.put("finallyGetRatioConfig", finallyGetRatioConfig);
+
+                _finalStealAmountSum = DoubleUtil.add(_finalStealAmount, _finalStealAmountSum);
+                _stealLandInfos.add(_landMap);
+                // 需要redis 解锁
+                redisLock.unlock(_redisKey, String.valueOf(time));
+            } catch (Exception e) {
+                // 需要redis 解锁
+                redisLock.unlock(_redisKey, String.valueOf(time));
+                throw new RuntimeException(e.getMessage());
+            }
+        }
+
+        map.put("msg", "偷取果实.");
+        map.put("steals", _stealLandInfos);
+        map.put("stealSum", _finalStealAmountSum);
+
+        if (_stealLandInfos.size() > 0) {
+            //todo 体力值可以后台配置
+            Integer _addStrength = comUsersVo.getSnb() < comSettingVo.getDeductSnb() ? comSettingVo.getSnbUnitStrength() : comSettingVo.getUnitStrength();
+            playersAttribute.setStrength(playersAttribute.getStrength() + _addStrength);
+            comPlayersAttriService.save(playersAttribute);
+            ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttribute, ComPlayersAttriSimpleVo.class);
+            //前端的显示,输出一个不小于0的体力值
+            Integer _outEnd = _maxStrength - Math.abs(playersAttribute.getStrength());
+            comPlayersAttriSimpleVo.setCurrentStrength(_outEnd < 0 ? 0 : _outEnd);
+            //最大值是配置的值和链上数据的值相加
+            comPlayersAttriSimpleVo.setMaxStrength(_maxStrength);
+
+            map.put("playerAttribute", comPlayersAttriSimpleVo);
+        }
+
+        return Result.of(map);
+    }
+
+
+    /**
+     * 出售果实
+     * <p>
+     * todo 需要记录出售果实的snb操作记录
+     *
+     * @param userId
+     * @param amount
+     * @return
+     */
+    @PostMapping("saleFruit")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> saleFruitFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "fruitId") Integer fruitId,
+            @RequestParam(value = "amount") Integer amount) {
+
+        //
+        ComFruitVo comFruitVo = comFruitService.findById(fruitId);
+
+        if (comFruitVo == null) {
+            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
+        }
+        //果实
+        ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, fruitId, 1);
+
+        //销售果实数量
+        if (comPlayerGoodsSimpleVo == null) {
+            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
+        }
+        //添加了 amountPart,需要加入判断整数部分
+        Integer _userFloorAmount = comPlayerGoodsSimpleVo.getAmount() + (int) Math.floor(comPlayerGoodsSimpleVo.getAmountPart());
+        //旧判断 amount > comPlayerGoodsSimpleVo.getAmount()
+        if (amount > _userFloorAmount) {
+            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_ERROR.getMessage(), ResultEnum.FRUIT_AMOUNT_ERROR.getCode());
+        }
+        //todo 销售果实时候,收取5%手续费,最少出售20的倍数
+        //   1.判定销售数量, 如果果实输入是0,或者不是20的倍数,返回提示
+        // || amount % 20 != 0
+        if (amount.equals(0)) {
+            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_IS_ZERO_AND_NOT_MULTIPLE.getMessage(), ResultEnum.FRUIT_AMOUNT_IS_ZERO_AND_NOT_MULTIPLE.getCode());
+        }
+        //todo snb冻结加锁
+        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
+        String _redisKey = "SNB_SAVE_" + userId;
+
+        try {
+            //todo snb冻结加锁
+            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
+                return Result.of(null, false, ResultEnum.PLAYER_GOODS_SAVE_LOCK.getMessage(), ResultEnum.PLAYER_GOODS_SAVE_LOCK.getCode());
+            }
+
+            Integer _selfBeforeFruitAmount = comPlayerGoodsSimpleVo.getAmount();
+            Integer _selfAfterFruitAmount = 0;
+            Double _selfBeforeFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+            Double _selfAfterFruitAmountPart = 0d;
+            Integer _tAmount = 0;
+            Double _tAmountPart = 0d;
+
+            //if (comPlayerGoodsSimpleVo.getAmount().equals(amount)) {
+            //    //如果库存量和出售的果实数量一样,删除
+            //    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
+            //    _selfAfterFruitAmount = 0;
+            //} else {
+            //    comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() - amount);
+            //    _selfAfterFruitAmount = comPlayerGoodsSimpleVo.getAmount();
+            //    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+            //    comPlayerGoodsService.save(comPlayerGoodsVo);
+            //}
+            Integer _amountDif = comPlayerGoodsSimpleVo.getAmount() - amount;
+            //优先处理背包的amount
+            if (_amountDif > 0) {
+                //当前交易值
+                _tAmount = amount;
+                //如果amount 数量足够,直接记录 他们的差值
+                comPlayerGoodsSimpleVo.setAmount(_amountDif);
+                _selfAfterFruitAmount = comPlayerGoodsSimpleVo.getAmount();
+                _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+                comPlayerGoodsService.save(comPlayerGoodsVo);
+            } else {
+                _selfAfterFruitAmount = 0;
+
+                //保证精度
+                BigDecimal bigDecimal1 = new BigDecimal(Double.toString(_amountDif));
+                BigDecimal bigDecimal2 = new BigDecimal(Double.toString(comPlayerGoodsSimpleVo.getAmountPart()));
+                BigDecimal _amountPart = bigDecimal1.add(bigDecimal2);
+
+                //当前交易值,_amountDif 小于0 说明 amount 是全全部扣除,到小数部分了。
+                _tAmount = comPlayerGoodsSimpleVo.getAmount();
+                _tAmountPart = Math.abs(bigDecimal1.doubleValue());
+
+                System.out.print("_amountPart{}," + _amountPart.doubleValue());
+                //前面判断了。所以 _amountPart 不会比0.0小
+                if (_amountPart.compareTo(new BigDecimal(0.0)) <= 0) {
+                    //如果库存量 amount 和 amountPart 都为零,删除
+                    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
+                    //这里记录一下,看是否会是一个负数
+                    _selfAfterFruitAmountPart = _amountPart.doubleValue();
+                } else {
+                    comPlayerGoodsSimpleVo.setAmount(0);
+                    comPlayerGoodsSimpleVo.setAmountPart(_amountPart.doubleValue());
+                    _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+                    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+                    comPlayerGoodsService.save(comPlayerGoodsVo);
+                }
+            }
+            //todo 收取手续费之后
+            int _endAmount = (int) (amount * 1);
+            //添加SNB
+            ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
+            ComSnbTranVo _snbTran = new ComSnbTranVo();
+            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
+            //交易前的snb数据
+            Integer _beforeSnb = comUsersVo.getSnb();
+            //数量乘以售价
+            int _saleSnb = comFruitVo.getPriceSnb() * _endAmount; //此时的售价数量已经扣除了手续费,相当于减少了几个果实
+            //当前操作的snb
+            Integer _currentSnb = comUsersVo.getSnb() + _saleSnb;
+            comUsersVo.setSnb(_currentSnb);
+            comUsersService.save(comUsersVo);
+
+            /**
+             * todo 果实出售snb数据记录
+             */
+
+            //记录果实id
+            _snbTran.setTranId(comFruitVo.getId().toString());
+            _snbTran.setUserId(userId);
+            _snbTran.setTranName(comFruitVo.getName());
+            _snbTran.setTranType(0);
+            _snbTran.setTranAmount(amount); //此数量会操作背包记录
+            _snbTran.setTranAmountPart(0d);
+            _snbTran.setTranPrice(comFruitVo.getPriceSnb());
+            //"出售果实价格:" + comFruitVo.getPriceSnb() + ",数量:" + amount
+            _snbTran.setTranDescribe("手续费系数:1,最终果实数量为:" + _endAmount + ",销售价格:" + _saleSnb);
+            _snbTran.setIsAdd(1);//增加收入
+            _snbTran.setBeforeSnb(_beforeSnb);
+            _snbTran.setTranSnb(_saleSnb);
+            _snbTran.setAfterSnb(_currentSnb);
+
+            _snbTran.setTranSnbPart(0d);
+            _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
+
+            comSnbTranService.save(_snbTran);
+
+            /**
+             * todo 出售果实时候,记录操作 PlayerLog
+             */
+            //当前用户
+            ComPlayerLog _saleFruitPlayerLog = new ComPlayerLog();
+            _saleFruitPlayerLog.setUserId(userId);
+            _saleFruitPlayerLog.setTId(comFruitVo.getId());
+            _saleFruitPlayerLog.setTName(comFruitVo.getName());
+            _saleFruitPlayerLog.setTType(5);//出售水果时候,type设置5
+            _saleFruitPlayerLog.setTAmount(_tAmount);
+            _saleFruitPlayerLog.setBeforeAmount(_selfBeforeFruitAmount);
+            ////交易后的数据
+            _saleFruitPlayerLog.setAfterAmount(_selfAfterFruitAmount);
+            //增加一个小数部分记录
+            _saleFruitPlayerLog.setTPart(_tAmountPart);
+            _saleFruitPlayerLog.setTLoss(0d);
+            _saleFruitPlayerLog.setBeforePart(_selfBeforeFruitAmountPart);
+            _saleFruitPlayerLog.setAfterPart(_selfAfterFruitAmountPart);
+            _saleFruitPlayerLog.setLMultiple(0);//收获时候,会有一个倍数
+            ComPlayerLogVo _saleFruitPlayerLogVo = CopyUtil.copy(_saleFruitPlayerLog, ComPlayerLogVo.class);
+            comPlayerLogService.save(_saleFruitPlayerLogVo);
+
+
+            redisLock.unlock(_redisKey, String.valueOf(time));
+        } catch (Exception e) {
+            redisLock.unlock(_redisKey, String.valueOf(time));
+            throw new RuntimeException(e.getMessage());
+        }
+
+
+        Map map = new HashMap();
+        map.put("msg", "成功出售果实!");
+        return Result.of(map);
+    }
+
+
+    /**
+     * 根据用户地址送果实
+     *
+     * @param userId
+     * @param address 目标用户地址
+     * @return
+     */
+    @PostMapping("grantFruit")
+    @ResponseBody
+    @Transactional(rollbackFor = Exception.class)
+    public Result<Map> grantFruitFunction(
+            @RequestParam(value = "userId") String userId,
+            @RequestParam(value = "fruitId") Integer fruitId,
+            @RequestParam(value = "amount") Integer amount,
+            @RequestParam(value = "address") String address) {
+
+        //目标用户的背包
+        ComUsersVo _targetUserVo = comUsersService.findByAddress(address);
+        if (_targetUserVo == null) {
+            return Result.of(null, false, ResultEnum.USER_NOT_ADDRESS.getMessage(), ResultEnum.USER_NOT_ADDRESS.getCode());
+        }
+
+        if (_targetUserVo.getUserId().equals(userId)) {
+            return Result.of(null, false, ResultEnum.FRUIT_SEND_SELF.getMessage(), ResultEnum.FRUIT_SEND_SELF.getCode());
+        }
+
+        ComFruitVo comFruitVo = comFruitService.findById(fruitId);
+
+        if (comFruitVo == null) {
+            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
+        }
+        //果实type =1
+        ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, fruitId, 1);
+
+        //销售果实数量
+        if (comPlayerGoodsSimpleVo == null) {
+            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
+        }
+        //添加了 amountPart,需要加入判断整数部分
+        Integer _userFloorAmount = comPlayerGoodsSimpleVo.getAmount() + (int) Math.floor(comPlayerGoodsSimpleVo.getAmountPart());
+        if (amount > _userFloorAmount) {
+            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_ERROR.getMessage(), ResultEnum.FRUIT_AMOUNT_ERROR.getCode());
+        }
+        try {
+            Integer _selfBeforeAmount = comPlayerGoodsSimpleVo.getAmount();
+            Integer _selfAfterAmount = 0;
+            Double _selfBeforeFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+            Double _selfAfterFruitAmountPart = 0d;
+            Integer _tAmount = 0;
+            Double _tAmountPart = 0d;
+
+            //if (comPlayerGoodsSimpleVo.getAmount().equals(amount)) {
+            //    //如果库存量和出售的果实数量一样,删除
+            //    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
+            //    _selfAfterAmount = 0;
+            //} else {
+            //    comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() - amount);
+            //    _selfAfterAmount = comPlayerGoodsSimpleVo.getAmount();
+            //    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+            //    comPlayerGoodsService.save(comPlayerGoodsVo);
+            //}
+            Integer _amountDif = comPlayerGoodsSimpleVo.getAmount() - amount;
+            //优先处理背包的amount
+            if (_amountDif > 0) {
+                //当前交易值
+                _tAmount = amount;
+                //如果amount 数量足够,直接记录 他们的差值
+                comPlayerGoodsSimpleVo.setAmount(_amountDif);
+                _selfAfterAmount = comPlayerGoodsSimpleVo.getAmount();
+                _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+                comPlayerGoodsService.save(comPlayerGoodsVo);
+            } else {
+                _selfAfterAmount = 0;
+                //保证精度
+                BigDecimal bigDecimal1 = new BigDecimal(Double.toString(_amountDif));
+                BigDecimal bigDecimal2 = new BigDecimal(Double.toString(comPlayerGoodsSimpleVo.getAmountPart()));
+                BigDecimal _amountPart = bigDecimal1.add(bigDecimal2);
+                //当前交易值,_amountDif 小于0 说明 amount 是全全部扣除,到小数部分了。
+                _tAmount = comPlayerGoodsSimpleVo.getAmount();
+                _tAmountPart = Math.abs(bigDecimal1.doubleValue());
+                System.out.print("_amountPart{}," + _amountPart.doubleValue());
+                //前面判断了。所以 _amountPart 不会比0.0小
+                if (_amountPart.compareTo(new BigDecimal(0.0)) <= 0) {
+                    //如果库存量 amount 和 amountPart 都为零,删除
+                    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
+                    //这里记录一下,看是否会是一个负数
+                    _selfAfterFruitAmountPart = _amountPart.doubleValue();
+                } else {
+                    comPlayerGoodsSimpleVo.setAmount(0);
+                    comPlayerGoodsSimpleVo.setAmountPart(_amountPart.doubleValue());
+                    _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
+                    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
+                    comPlayerGoodsService.save(comPlayerGoodsVo);
+                }
+            }
+
+            //todo 发送果实,记录当前用户的playerLog
+            //当前用户
+            ComPlayerLog _selfPlayerLog = new ComPlayerLog();
+            _selfPlayerLog.setUserId(userId);
+            _selfPlayerLog.setTId(comFruitVo.getId());
+            _selfPlayerLog.setTName(comFruitVo.getName());
+            _selfPlayerLog.setTType(2);//发送水果的类型:2
+            _selfPlayerLog.setTAmount(amount);
+            _selfPlayerLog.setBeforeAmount(_selfBeforeAmount);
+            ////交易后的数据
+            _selfPlayerLog.setAfterAmount(_selfAfterAmount);
+            _selfPlayerLog.setLMultiple(0);//收获时候,会有一个倍数
+            ComPlayerLogVo _selfPlayerLogVo = CopyUtil.copy(_selfPlayerLog, ComPlayerLogVo.class);
+            comPlayerLogService.save(_selfPlayerLogVo);
+
+            //接受人的背包数据
+            ComPlayerGoods _targetGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(_targetUserVo.getUserId(), comFruitVo.getId(), 1);
+            Integer _targetBeforeAmount = 0;
+            Integer _targetAfterAmount = 0;
+            if (_targetGoods == null) {
+                //背包增加一个果实
+                _targetGoods = new ComPlayerGoods();
+                //目标id
+                _targetGoods.setUserId(_targetUserVo.getUserId());
+                _targetGoods.setGoodsIndex(comFruitVo.getId());
+                _targetGoods.setGoodsType(1);
+                _targetGoods.setName(comFruitVo.getName());
+                //用种子的picture
+                _targetGoods.setPictureName(comFruitVo.getPicture());
+                _targetGoods.setAmount(amount);
+                _targetGoods.setAmountPart(0d);
+                _targetAfterAmount = _targetGoods.getAmount();
+            } else {
+                _targetBeforeAmount = _targetGoods.getAmount();
+                _targetGoods.setAmount(_targetGoods.getAmount() + amount);
+                _targetAfterAmount = _targetGoods.getAmount();
+            }
+            ComPlayerGoodsVo _targetGoodsVo = CopyUtil.copy(_targetGoods, ComPlayerGoodsVo.class);
+            comPlayerGoodsService.save(_targetGoodsVo);
+            //todo 接收果实,记录目标用户的playerLog
+            ComPlayerLog _targetPlayerLog = new ComPlayerLog();
+            _targetPlayerLog.setUserId(_targetUserVo.getUserId());
+            _targetPlayerLog.setTId(comFruitVo.getId());
+            _targetPlayerLog.setTName(comFruitVo.getName());
+            _targetPlayerLog.setTType(3);//接收用户赠送水果,type设置 3
+            _targetPlayerLog.setTAmount(_tAmount);
+            _targetPlayerLog.setBeforeAmount(_targetBeforeAmount);
+            ////交易后的数据
+            _targetPlayerLog.setAfterAmount(_targetAfterAmount);
+            //增加一个小数部分记录
+            _targetPlayerLog.setTPart(_tAmountPart);
+            _targetPlayerLog.setTLoss(0d);
+            _targetPlayerLog.setBeforePart(_selfBeforeFruitAmountPart);
+            _targetPlayerLog.setAfterPart(_selfAfterFruitAmountPart);
+
+            _targetPlayerLog.setLMultiple(0);
+            ComPlayerLogVo _targetPlayerLogVo = CopyUtil.copy(_targetPlayerLog, ComPlayerLogVo.class);
+            comPlayerLogService.save(_targetPlayerLogVo);
+        } catch (Exception e) {
+            throw new RuntimeException(e.getMessage());
+        }
+
+
+        Map map = new HashMap();
+        map.put("msg", "成功发送果实!");
+        return Result.of(map);
+    }
+}

+ 31 - 0
src/main/java/com/td/boss/game/complayergoods/pojo/ComPlayerGoods.java

@@ -0,0 +1,31 @@
+package com.td.boss.game.complayergoods.pojo;
+
+import lombok.Data;
+import javax.persistence.*;
+import java.io.Serializable;
+import java.util.Date;
+
+@Entity
+@Table(name = "com_player_goods")
+@Data
+public class ComPlayerGoods implements Serializable {
+    @Id
+    private String goodsId;//仓库物品id,字符串
+
+    private Integer goodsIndex;//仓库物品下标, 根据类型区分。比如种子type=0,index=种子id,
+
+    private Integer goodsType;//对应类型(0:种子,1:果实)
+
+    private String userId;//用户id
+
+    private String name;//名字
+
+    private String pictureName;//图片名字,走前端读取吧
+
+    private Integer amount;//数量
+
+    private Double amountPart;//部分数量,处理收取其他用户数据为主
+
+    private Date createTime;
+
+}

Some files were not shown because too many files changed in this diff