瀏覽代碼

“初始化提交

zsy 1 周之前
當前提交
fe06e32c74
共有 72 個文件被更改,包括 2467 次插入0 次删除
  1. 20 0
      .gitignore
  2. 3 0
      README.md
  3. 109 0
      build.gradle
  4. 234 0
      gradlew
  5. 57 0
      src/main/java/com/rf/help/ReciprocalHelpApplication.java
  6. 46 0
      src/main/java/com/rf/help/base/model/BaseEntity.java
  7. 18 0
      src/main/java/com/rf/help/base/repository/BaseRepository.java
  8. 76 0
      src/main/java/com/rf/help/base/rest/BaseController.java
  9. 30 0
      src/main/java/com/rf/help/config/CorsConfig.java
  10. 22 0
      src/main/java/com/rf/help/config/Properties.java
  11. 87 0
      src/main/java/com/rf/help/config/RedisConfig.java
  12. 51 0
      src/main/java/com/rf/help/config/Swagger2Config.java
  13. 18 0
      src/main/java/com/rf/help/dao/dto/JpaDto.java
  14. 21 0
      src/main/java/com/rf/help/dao/dto/ProgressDto.java
  15. 2 0
      src/main/java/com/rf/help/dao/lombok.config
  16. 46 0
      src/main/java/com/rf/help/file/FileController.java
  17. 92 0
      src/main/java/com/rf/help/filter/AuthenticationInterceptor.java
  18. 62 0
      src/main/java/com/rf/help/filter/JWTInterceptor.java
  19. 49 0
      src/main/java/com/rf/help/filter/JWTInterceptorConfig.java
  20. 12 0
      src/main/java/com/rf/help/filter/JwtIgnore.java
  21. 67 0
      src/main/java/com/rf/help/security/SSLConfig.java
  22. 18 0
      src/main/java/com/rf/help/security/SafetyProcess.java
  23. 36 0
      src/main/java/com/rf/help/task/dao/model/ReplyEntity.java
  24. 60 0
      src/main/java/com/rf/help/task/dao/model/TaskEntity.java
  25. 24 0
      src/main/java/com/rf/help/task/dao/repository/ReplyRepository.java
  26. 30 0
      src/main/java/com/rf/help/task/dao/repository/TaskRepository.java
  27. 64 0
      src/main/java/com/rf/help/task/rest/ReplyController.java
  28. 125 0
      src/main/java/com/rf/help/task/rest/TaskController.java
  29. 20 0
      src/main/java/com/rf/help/task/service/ReplyService.java
  30. 21 0
      src/main/java/com/rf/help/task/service/TaskService.java
  31. 37 0
      src/main/java/com/rf/help/task/service/impl/ReplyServiceImpl.java
  32. 46 0
      src/main/java/com/rf/help/task/service/impl/TaskServiceImpl.java
  33. 42 0
      src/main/java/com/rf/help/user/dao/model/UserEntity.java
  34. 14 0
      src/main/java/com/rf/help/user/dao/repository/UserRepository.java
  35. 185 0
      src/main/java/com/rf/help/user/rest/UserController.java
  36. 17 0
      src/main/java/com/rf/help/user/service/UserService.java
  37. 36 0
      src/main/java/com/rf/help/user/service/impl/UserServiceImpl.java
  38. 42 0
      src/main/java/com/rf/help/utils/Constant.java
  39. 19 0
      src/main/java/com/rf/help/utils/HttpStatus.java
  40. 71 0
      src/main/java/com/rf/help/utils/JWTUtil.java
  41. 35 0
      src/main/java/com/rf/help/utils/LocalAssert.java
  42. 62 0
      src/main/java/com/rf/help/utils/Result.java
  43. 44 0
      src/main/java/com/rf/help/utils/WebContextUtil.java
  44. 48 0
      src/main/resources/config/application-prod.yml
  45. 51 0
      src/main/resources/config/application-test.yml
  46. 26 0
      src/main/resources/config/application.yml
  47. 144 0
      src/main/resources/logback.xml
  48. 28 0
      src/main/resources/proguard.conf
  49. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743412955790-=-微信图片_20220210103150.jpg
  50. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743421867661-=-6LcknnZVJzhB9ca8ed84d5f1b445cdfd101807eea0d8.jpeg
  51. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422117043-=-zEoubMTvIVXP08d542ce58dca14ae0d88b3b02dade17.jpeg
  52. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422183594-=-PaJ3VeR5gaZl27ccb01c4c68b7f32adda0fb1b95caef.jpeg
  53. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422219713-=-uy4qqrMZkhfO43126e9f4ed7b6826ee6d478ff775823.jpeg
  54. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422240576-=-ZJAb409FAqPOb956267313c221db44a89b26c949cdc0.jpeg
  55. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422543977-=-wH3Vbk3vs6A01eabddd21c8b1b704fb527cc1bc38bbb.jpeg
  56. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422706287-=-WZmEYtsH6powb36da84fd8a2c1f8553c2c6cbbd68a18.jpeg
  57. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422725996-=-GDpyX12ALVQA2ade7b0258d2b4738920acd651764be5.jpeg
  58. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422738990-=-0VlVuYIAcmQc89bb9249c80f2d0d10557d45872bfd36.jpeg
  59. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422770610-=-XZ6dwgZ5tkXH8d60685d44425b2767b8775802baecbe.jpeg
  60. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422866008-=-045HfweuPSx4a647247c511ca29f202cc6e318e76087.jpeg
  61. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422895245-=-WeNSvYoudc2x14625654a849538b11212ab8e694f590.jpeg
  62. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422948319-=-mvoJGIqDL8Mo062200f98c3ca9e6651e24b97afcd2f8.jpeg
  63. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743423043741-=-O4MJk0nqccND05d418bf2aa807b8d4efc3473abedb0f.jpeg
  64. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743423096410-=-MJfUYtf5V0Qie3dd4b80600dbdd62079b225dff1fa51.jpeg
  65. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468061570-=-aJBqL4zRib5U3522054ae7d5323589ec93328687a8dc.jpeg
  66. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468154690-=-ApzJxSIKUcHV659f794698003651c0a8c21efc17b7f5.jpeg
  67. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468415270-=-fIbIt2Wld6TD3df55119c5745ecc503ac7c4cd793388.jpeg
  68. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468996537-=-1T954WgvkiUC9dc35d387b0981c9e3acdfc7dab0dc8d.jpeg
  69. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743469133335-=-AXs823UWfv5def9129cb13cea74f74880ddddb85f926.jpeg
  70. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743469228670-=-jPgcZv6Unzk80bf6b12a17088e287c7f6cac58ed5f30.jpeg
  71. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743470633741-=-0103114133.png
  72. 二進制
      userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743470811355-=-0103114133.png

+ 20 - 0
.gitignore

@@ -0,0 +1,20 @@
+# ---> Java
+*.class
+
+# Mobile Tools for Java (J2ME)
+.mtj.tmp/
+
+# Package Files #
+*.jar
+*.war
+*.ear
+
+# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
+hs_err_pid*
+
+/.gradle/
+/build/
+/src/main/resources/static/
+/logs/
+/gradle/
+/.idea/

+ 3 - 0
README.md

@@ -0,0 +1,3 @@
+# reciprocal_help
+
+review code

+ 109 - 0
build.gradle

@@ -0,0 +1,109 @@
+buildscript {
+	ext {
+		queryDslVersion = '4.2.1'
+		lombokVersion = '1.18.12'
+	}
+}
+plugins {
+	id 'org.springframework.boot' version '2.4.2'
+	id 'io.spring.dependency-management' version '1.0.11.RELEASE'
+	id 'java'
+}
+
+group = 'com.rf'
+version = 'v1.1-'+getNewVersion()
+sourceCompatibility = '8'
+
+configurations {
+	compileOnly {
+		extendsFrom annotationProcessor
+	}
+}
+
+repositories {
+	maven {
+		url 'https://maven.aliyun.com/repository/public'
+	}
+	maven {
+		credentials {
+			username '0p07qf'
+			password 'z1XwdFEf4M'
+		}
+		url 'https://repo.rdc.aliyun.com/repository/107260-release-GqdtcU/'
+	}
+	maven {
+		credentials {
+			username '0p07qf'
+			password 'z1XwdFEf4M'
+		}
+		url 'https://repo.rdc.aliyun.com/repository/107260-snapshot-C6ziam/'
+	}
+	maven {
+		url "https://plugins.gradle.org/m2/"
+	}
+	mavenLocal()
+}
+
+dependencies {
+	implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
+	implementation 'org.springframework.boot:spring-boot-starter-web'
+	compileOnly 'org.projectlombok:lombok'
+	annotationProcessor 'org.projectlombok:lombok'
+	implementation 'org.springframework.boot:spring-boot-starter-validation:2.6.3'
+
+	implementation 'mysql:mysql-connector-java:8.0.21'
+
+	implementation("com.querydsl:querydsl-core:${queryDslVersion}")
+	implementation("com.querydsl:querydsl-jpa:${queryDslVersion}")
+	annotationProcessor("com.querydsl:querydsl-apt:${queryDslVersion}:jpa",
+			"org.hibernate.javax.persistence:hibernate-jpa-2.1-api:1.0.2.Final",
+			"javax.annotation:javax.annotation-api:1.3.2",
+			"org.projectlombok:lombok")
+
+	compileOnly "org.projectlombok:lombok:${lombokVersion}"
+	annotationProcessor "org.projectlombok:lombok:${lombokVersion}"
+	implementation("org.projectlombok:lombok:${lombokVersion}")
+	testImplementation 'org.springframework.boot:spring-boot-starter-test:2.6.3'
+
+	implementation 'com.alibaba:fastjson:1.2.83'
+	implementation 'org.apache.poi:poi-ooxml:3.17'
+
+	implementation 'commons-lang:commons-lang:2.6'
+
+	implementation group: 'io.springfox', name: 'springfox-swagger2', version: '2.9.2'
+	implementation group: 'io.springfox', name: 'springfox-swagger-ui', version: '2.9.2'
+	implementation group: 'io.swagger', name: 'swagger-annotations', version: '1.5.22'
+	implementation group: 'io.swagger', name: 'swagger-models', version: '1.5.22'
+	implementation group: 'commons-fileupload', name: 'commons-fileupload', version: '1.3.1'
+	implementation group: 'com.github.qcloudsms', name: 'qcloudsms', version: '1.0.6'
+	implementation group: 'cn.keking.project', name: 'kk-anti-reptile', version: '1.0.0-RELEASE'
+	implementation group: 'com.belerweb', name: 'pinyin4j', version: '2.5.1'
+	implementation 'org.springframework.boot:spring-boot-starter-websocket:2.5.5'
+	implementation 'org.java-websocket:Java-WebSocket:1.5.1'
+	implementation 'com.auth0:java-jwt:3.18.2'
+	implementation group: 'com.alibaba', name: 'druid-spring-boot-starter', version: '1.2.9'
+	testImplementation group: 'junit', name: 'junit', version: '4.13.2'
+	implementation 'com.github.wechatpay-apiv3:wechatpay-apache-httpclient:0.4.7'
+
+	implementation group: 'org.springframework.boot', name: 'spring-boot-configuration-processor', version: '2.6.8'
+
+	implementation group: 'com.google.code.gson', name: 'gson', version: '2.8.6'
+
+	implementation group: 'cn.hutool', name: 'hutool-all', version: '5.7.18'
+
+	implementation group: 'org.springframework.boot', name: 'spring-boot-starter-data-redis', version: '2.6.3'
+
+	implementation group: 'org.dom4j', name: 'dom4j', version: '2.1.1'
+
+
+
+
+}
+
+test {
+	useJUnitPlatform()
+}
+
+def getNewVersion(){
+	return new Date().format("yyyyMMddHHmmss",TimeZone.getTimeZone("GMT+08:00"))
+}

+ 234 - 0
gradlew

@@ -0,0 +1,234 @@
+#!/bin/sh
+
+#
+# Copyright © 2015-2021 the original authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      https://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+##############################################################################
+#
+#   Gradle start up script for POSIX generated by Gradle.
+#
+#   Important for running:
+#
+#   (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is
+#       noncompliant, but you have some other compliant shell such as ksh or
+#       bash, then to run this script, type that shell name before the whole
+#       command line, like:
+#
+#           ksh Gradle
+#
+#       Busybox and similar reduced shells will NOT work, because this script
+#       requires all of these POSIX shell features:
+#         * functions;
+#         * expansions «$var», «${var}», «${var:-default}», «${var+SET}»,
+#           «${var#prefix}», «${var%suffix}», and «$( cmd )»;
+#         * compound commands having a testable exit status, especially «case»;
+#         * various built-in commands including «command», «set», and «ulimit».
+#
+#   Important for patching:
+#
+#   (2) This script targets any POSIX shell, so it avoids extensions provided
+#       by Bash, Ksh, etc; in particular arrays are avoided.
+#
+#       The "traditional" practice of packing multiple parameters into a
+#       space-separated string is a well documented source of bugs and security
+#       problems, so this is (mostly) avoided, by progressively accumulating
+#       options in "$@", and eventually passing that to Java.
+#
+#       Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS,
+#       and GRADLE_OPTS) rely on word-splitting, this is performed explicitly;
+#       see the in-line comments for details.
+#
+#       There are tweaks for specific operating systems such as AIX, CygWin,
+#       Darwin, MinGW, and NonStop.
+#
+#   (3) This script is generated from the Groovy template
+#       https://github.com/gradle/gradle/blob/master/subprojects/plugins/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt
+#       within the Gradle project.
+#
+#       You can find Gradle at https://github.com/gradle/gradle/.
+#
+##############################################################################
+
+# Attempt to set APP_HOME
+
+# Resolve links: $0 may be a link
+app_path=$0
+
+# Need this for daisy-chained symlinks.
+while
+    APP_HOME=${app_path%"${app_path##*/}"}  # leaves a trailing /; empty if no leading path
+    [ -h "$app_path" ]
+do
+    ls=$( ls -ld "$app_path" )
+    link=${ls#*' -> '}
+    case $link in             #(
+      /*)   app_path=$link ;; #(
+      *)    app_path=$APP_HOME$link ;;
+    esac
+done
+
+APP_HOME=$( cd "${APP_HOME:-./}" && pwd -P ) || exit
+
+APP_NAME="Gradle"
+APP_BASE_NAME=${0##*/}
+
+# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
+DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
+
+# Use the maximum available, or set MAX_FD != -1 to use that value.
+MAX_FD=maximum
+
+warn () {
+    echo "$*"
+} >&2
+
+die () {
+    echo
+    echo "$*"
+    echo
+    exit 1
+} >&2
+
+# OS specific support (must be 'true' or 'false').
+cygwin=false
+msys=false
+darwin=false
+nonstop=false
+case "$( uname )" in                #(
+  CYGWIN* )         cygwin=true  ;; #(
+  Darwin* )         darwin=true  ;; #(
+  MSYS* | MINGW* )  msys=true    ;; #(
+  NONSTOP* )        nonstop=true ;;
+esac
+
+CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar
+
+
+# Determine the Java command to use to start the JVM.
+if [ -n "$JAVA_HOME" ] ; then
+    if [ -x "$JAVA_HOME/jre/sh/java" ] ; then
+        # IBM's JDK on AIX uses strange locations for the executables
+        JAVACMD=$JAVA_HOME/jre/sh/java
+    else
+        JAVACMD=$JAVA_HOME/bin/java
+    fi
+    if [ ! -x "$JAVACMD" ] ; then
+        die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+    fi
+else
+    JAVACMD=java
+    which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH.
+
+Please set the JAVA_HOME variable in your environment to match the
+location of your Java installation."
+fi
+
+# Increase the maximum file descriptors if we can.
+if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then
+    case $MAX_FD in #(
+      max*)
+        MAX_FD=$( ulimit -H -n ) ||
+            warn "Could not query maximum file descriptor limit"
+    esac
+    case $MAX_FD in  #(
+      '' | soft) :;; #(
+      *)
+        ulimit -n "$MAX_FD" ||
+            warn "Could not set maximum file descriptor limit to $MAX_FD"
+    esac
+fi
+
+# Collect all arguments for the java command, stacking in reverse order:
+#   * args from the command line
+#   * the main class name
+#   * -classpath
+#   * -D...appname settings
+#   * --module-path (only if needed)
+#   * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables.
+
+# For Cygwin or MSYS, switch paths to Windows format before running java
+if "$cygwin" || "$msys" ; then
+    APP_HOME=$( cygpath --path --mixed "$APP_HOME" )
+    CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" )
+
+    JAVACMD=$( cygpath --unix "$JAVACMD" )
+
+    # Now convert the arguments - kludge to limit ourselves to /bin/sh
+    for arg do
+        if
+            case $arg in                                #(
+              -*)   false ;;                            # don't mess with options #(
+              /?*)  t=${arg#/} t=/${t%%/*}              # looks like a POSIX filepath
+                    [ -e "$t" ] ;;                      #(
+              *)    false ;;
+            esac
+        then
+            arg=$( cygpath --path --ignore --mixed "$arg" )
+        fi
+        # Roll the args list around exactly as many times as the number of
+        # args, so each arg winds up back in the position where it started, but
+        # possibly modified.
+        #
+        # NB: a `for` loop captures its iteration list before it begins, so
+        # changing the positional parameters here affects neither the number of
+        # iterations, nor the values presented in `arg`.
+        shift                   # remove old arg
+        set -- "$@" "$arg"      # push replacement arg
+    done
+fi
+
+# Collect all arguments for the java command;
+#   * $DEFAULT_JVM_OPTS, $JAVA_OPTS, and $GRADLE_OPTS can contain fragments of
+#     shell script including quotes and variable substitutions, so put them in
+#     double quotes to make sure that they get re-expanded; and
+#   * put everything else in single quotes, so that it's not re-expanded.
+
+set -- \
+        "-Dorg.gradle.appname=$APP_BASE_NAME" \
+        -classpath "$CLASSPATH" \
+        org.gradle.wrapper.GradleWrapperMain \
+        "$@"
+
+# Use "xargs" to parse quoted args.
+#
+# With -n1 it outputs one arg per line, with the quotes and backslashes removed.
+#
+# In Bash we could simply go:
+#
+#   readarray ARGS < <( xargs -n1 <<<"$var" ) &&
+#   set -- "${ARGS[@]}" "$@"
+#
+# but POSIX shell has neither arrays nor command substitution, so instead we
+# post-process each arg (as a line of input to sed) to backslash-escape any
+# character that might be a shell metacharacter, then use eval to reverse
+# that process (while maintaining the separation between arguments), and wrap
+# the whole thing up as a single "set" statement.
+#
+# This will of course break if any of these variables contains a newline or
+# an unmatched quote.
+#
+
+eval "set -- $(
+        printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" |
+        xargs -n1 |
+        sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' |
+        tr '\n' ' '
+    )" '"$@"'
+
+exec "$JAVACMD" "$@"

+ 57 - 0
src/main/java/com/rf/help/ReciprocalHelpApplication.java

@@ -0,0 +1,57 @@
+package com.rf.help;
+
+import com.querydsl.jpa.impl.JPAQueryFactory;
+import lombok.extern.slf4j.Slf4j;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.Bean;
+import org.springframework.core.env.Environment;
+import org.springframework.data.jpa.repository.config.EnableJpaAuditing;
+import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
+import org.springframework.scheduling.annotation.EnableScheduling;
+import org.springframework.transaction.annotation.EnableTransactionManagement;
+
+import javax.persistence.EntityManager;
+
+/**
+ * @author zzf
+ */
+@EnableJpaRepositories(basePackages = {"com.rf.help"})
+@SpringBootApplication(scanBasePackages = {"com.rf.help"})
+@EnableJpaAuditing
+@EnableTransactionManagement
+@EnableScheduling
+@Slf4j
+public class ReciprocalHelpApplication {
+
+    static Logger logger = LoggerFactory.getLogger(ReciprocalHelpApplication.class);
+    @Autowired
+    static Environment environment;
+    @Value("${spring.profiles.active}")
+    static String profile;
+
+
+    public static void main(String[] args) {
+
+        SpringApplication.run(ReciprocalHelpApplication.class, args);
+
+    }
+
+
+    /**
+     * 让Spring管理JPAQueryFactory
+     *
+     * @param entityManager
+     * @return
+     */
+    @Bean
+    public JPAQueryFactory jpaQueryFactory(EntityManager entityManager) {
+        return new JPAQueryFactory(entityManager);
+    }
+
+
+}

+ 46 - 0
src/main/java/com/rf/help/base/model/BaseEntity.java

@@ -0,0 +1,46 @@
+package com.rf.help.base.model;
+
+
+import lombok.AllArgsConstructor;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
+import org.hibernate.annotations.GenericGenerator;
+import org.springframework.data.jpa.domain.support.AuditingEntityListener;
+
+import javax.persistence.*;
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/1/18 19:13
+ */
+@Getter
+@Setter
+@NoArgsConstructor
+@AllArgsConstructor
+@EntityListeners({AuditingEntityListener.class})
+@MappedSuperclass
+public class BaseEntity implements Serializable {
+    @Getter
+    @Id
+    @GenericGenerator(name = "jpa-uuid", strategy = "uuid")
+    @GeneratedValue(generator = "jpa-uuid")
+    @Column(name = "id", columnDefinition = "varchar(36) COMMENT '数据ID'")
+    @NotNull(groups = Update.class)
+    private String id;
+
+    @Column(name = "create_time", columnDefinition = "varchar(36) COMMENT '创建时间'")
+    private String createTime;
+
+
+    @Column(name = "update_time", columnDefinition = "varchar(36) COMMENT '更新时间'")
+    private String updateTime;
+
+
+
+    public @interface Update {
+    }
+}

+ 18 - 0
src/main/java/com/rf/help/base/repository/BaseRepository.java

@@ -0,0 +1,18 @@
+package com.rf.help.base.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
+import org.springframework.data.querydsl.QuerydslPredicateExecutor;
+import org.springframework.data.repository.NoRepositoryBean;
+
+import java.io.Serializable;
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/1/18 19:17
+ */
+@NoRepositoryBean
+public interface BaseRepository<T, PK extends Serializable> extends JpaRepository<T, PK>, JpaSpecificationExecutor, QuerydslPredicateExecutor<T> {
+
+}

+ 76 - 0
src/main/java/com/rf/help/base/rest/BaseController.java

@@ -0,0 +1,76 @@
+package com.rf.help.base.rest;
+
+import com.rf.help.utils.HttpStatus;
+import com.rf.help.utils.Result;
+
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/1/18 19:20
+ */
+public class BaseController {
+    protected static final String PAGE_NUM = "pageNum";
+
+    protected static final String PAGE_SIZE = "pageSize";
+
+    protected Result success() {
+        return success(null, "成功");
+    }
+
+    protected static <T> Result<T> success(T data) {
+        return success(data, "成功");
+    }
+
+    protected static <T> Result<T> success(T data, String message) {
+        if (null == data) {
+            return new Result<>(HttpStatus.SUCCESS, message);
+        }
+        return new Result<>(HttpStatus.SUCCESS, message, data);
+    }
+
+    protected static <T> Result<T> success(String code,T data, String message) {
+        if (null == data) {
+            return new Result<>(code, message);
+        }
+        return new Result<>(code, message, data);
+    }
+
+    protected Result fail() {
+        return fail(null, "失败");
+    }
+
+    protected static <T> Result<T> fail(T data) {
+        return fail(data, "失败");
+    }
+
+    protected static <T> Result<T> fail(String message) {
+        return new Result<>(HttpStatus.RUNTIME_EXCEPTION, message);
+    }
+
+    protected static <T> Result<T> fail(String code ,T data ,String message){
+        if(data == null)
+            return new Result<>(code,message);
+        else
+            return new Result<>(code,message,data);
+    }
+
+    protected static <T> Result<T> fail(T data, String message) {
+        if (null == data) {
+            return new Result<>(HttpStatus.RUNTIME_EXCEPTION, message);
+        }
+        return new Result<>(HttpStatus.RUNTIME_EXCEPTION, message, data);
+    }
+
+    protected Result failBadRequest() {
+        return failBadRequest(null, "参数异常");
+    }
+
+    protected static <T> Result<T> failBadRequest(T data) {
+        return failBadRequest(null, "参数异常");
+    }
+
+    protected static <T> Result<T> failBadRequest(T data, String message) {
+        return new Result<>(org.springframework.http.HttpStatus.BAD_REQUEST.value() + "", message, data);
+    }
+}

+ 30 - 0
src/main/java/com/rf/help/config/CorsConfig.java

@@ -0,0 +1,30 @@
+package com.rf.help.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
+import org.springframework.web.filter.CorsFilter;
+
+@Configuration
+public class CorsConfig {
+
+    private CorsConfiguration buildConfig() {
+
+        CorsConfiguration corsConfiguration = new CorsConfiguration();
+        corsConfiguration.setAllowCredentials(true);
+        corsConfiguration.addAllowedOriginPattern("*");
+        corsConfiguration.addAllowedHeader("*");
+        corsConfiguration.addAllowedMethod("*");
+        return corsConfiguration;
+
+    }
+
+    @Bean
+    public CorsFilter corsFilter() {
+        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
+        source.registerCorsConfiguration("/**", buildConfig());
+        return new CorsFilter(source);
+    }
+
+}

+ 22 - 0
src/main/java/com/rf/help/config/Properties.java

@@ -0,0 +1,22 @@
+package com.rf.help.config;
+
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @Author:zzf
+ * @Date:2022/7/5:18:15
+ * @Description:
+ */
+
+public class Properties {
+    private final Map<String, Duration> initCaches = new HashMap<>();
+    public Map<String ,Duration> getInitCache(){
+
+        return initCaches;
+    }
+}

+ 87 - 0
src/main/java/com/rf/help/config/RedisConfig.java

@@ -0,0 +1,87 @@
+package com.rf.help.config;
+
+import com.fasterxml.jackson.annotation.JsonAutoDetect;
+import com.fasterxml.jackson.annotation.PropertyAccessor;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.CachingConfigurerSupport;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.data.redis.cache.RedisCacheConfiguration;
+import org.springframework.data.redis.cache.RedisCacheManager;
+import org.springframework.data.redis.cache.RedisCacheWriter;
+import org.springframework.data.redis.connection.RedisConnectionFactory;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
+import org.springframework.data.redis.serializer.RedisSerializationContext;
+import org.springframework.data.redis.serializer.StringRedisSerializer;
+
+import java.time.Duration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.ConcurrentHashMap;
+
+/**
+ * @Description:Redis缓存时间、序列化、反序列化等配置
+ * @Author: zsf
+ * @Date: 2022/7/1
+ */
+@Slf4j
+public class RedisConfig extends CachingConfigurerSupport {
+    @Autowired
+    private Properties properties;
+
+    @Bean
+    public CacheManager cacheManager(RedisConnectionFactory factory) {
+        RedisCacheConfiguration cacheConfiguration = RedisCacheConfiguration
+                .defaultCacheConfig()
+                .entryTtl(Duration.ofDays(30))
+                .disableCachingNullValues()
+                .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer()));
+        RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(factory);
+        Set<String> cacheNames = new HashSet<>();
+        ConcurrentHashMap<String, RedisCacheConfiguration> cacheConfig = new ConcurrentHashMap<>();
+
+        properties.getInitCache().forEach((key, value) -> {
+            cacheNames.add(key);
+            cacheConfig.put(key,cacheConfiguration.entryTtl(value));
+        });
+        return RedisCacheManager.builder(redisCacheWriter)
+                .cacheDefaults(cacheConfiguration)
+                .initialCacheNames(cacheNames)
+                .withInitialCacheConfigurations(cacheConfig)
+                .build();
+    }
+
+
+    @Bean
+    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
+        RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();
+        redisTemplate.setConnectionFactory(redisConnectionFactory);
+        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer());
+
+        StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
+        redisTemplate.setKeySerializer(stringRedisSerializer);
+
+        redisTemplate.setHashKeySerializer(stringRedisSerializer);
+        redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer());
+        redisTemplate.afterPropertiesSet();
+        return redisTemplate;
+    }
+
+    public Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer() {
+        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
+        ObjectMapper objectMapper = new ObjectMapper();
+
+        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
+
+        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);
+
+        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);
+        return jackson2JsonRedisSerializer;
+    }
+}

+ 51 - 0
src/main/java/com/rf/help/config/Swagger2Config.java

@@ -0,0 +1,51 @@
+package com.rf.help.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import springfox.documentation.builders.ApiInfoBuilder;
+import springfox.documentation.builders.ParameterBuilder;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.schema.ModelRef;
+import springfox.documentation.service.ApiInfo;
+import springfox.documentation.service.Parameter;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+import springfox.documentation.swagger2.annotations.EnableSwagger2;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author lpf
+ * @description:swagger 配置
+ * @date 2022/3/14 19:25
+ */
+@Configuration
+@EnableSwagger2
+@Profile({"test","lan"})
+public class Swagger2Config {
+
+    @Bean
+    public Docket createRestApi() {
+        ParameterBuilder tokenPar = new ParameterBuilder();
+        List<Parameter> pars = new ArrayList<>();
+        tokenPar.name("Authorization").description("token令牌,格式为Bearer Token(可从/test/token接口处获取临时测试token)").modelRef(new ModelRef("string")).parameterType("header").required(false).build();
+        pars.add(tokenPar.build());
+        return new Docket(DocumentationType.SWAGGER_2)
+                .apiInfo(apiInfo())
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.rf.help"))
+                .paths(PathSelectors.any())
+                .build()
+                .globalOperationParameters(pars);
+    }
+
+    private ApiInfo apiInfo() {
+        return new ApiInfoBuilder()
+                .title("互帮互助小程序")
+                .version("v1.0")
+                .build();
+    }
+}

+ 18 - 0
src/main/java/com/rf/help/dao/dto/JpaDto.java

@@ -0,0 +1,18 @@
+package com.rf.help.dao.dto;
+
+import org.springframework.stereotype.Component;
+
+import java.lang.annotation.*;
+
+/**
+ * @author lpf
+ * @description: 自定义注解类,加载dto上表示这是个JpaDto类,解决jpa原生不能返回dto的问题
+ * @date 2022/4/6 21:08
+ */
+
+@Documented
+@Component
+@Target(value = {ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+public @interface JpaDto {
+}

+ 21 - 0
src/main/java/com/rf/help/dao/dto/ProgressDto.java

@@ -0,0 +1,21 @@
+package com.rf.help.dao.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+/**
+ * @author lpf
+ * @description:
+ * @date 2022/5/5 18:02
+ */
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class ProgressDto {
+
+    private long maxNum;
+
+    private long havingDoNum;
+
+}

+ 2 - 0
src/main/java/com/rf/help/dao/lombok.config

@@ -0,0 +1,2 @@
+config.stopbubbling=true
+lombok.equalsandhashcode.callsuper=call

+ 46 - 0
src/main/java/com/rf/help/file/FileController.java

@@ -0,0 +1,46 @@
+package com.rf.help.file;
+
+import com.rf.help.base.rest.BaseController;
+import io.swagger.annotations.ApiOperation;
+import org.apache.commons.io.FileUtils;
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+import org.springframework.web.multipart.MultipartFile;
+import javax.servlet.http.HttpServletRequest;
+import java.io.File;
+import java.io.FileInputStream;
+import java.util.UUID;
+
+/**
+ * @Author:zzf
+ * @Date:2022/8/9:17:25
+ * @Description:
+ */
+
+@Controller
+@RequestMapping("/v1/file")
+public class FileController extends BaseController {
+
+    @GetMapping(value = "/show",produces = MediaType.IMAGE_JPEG_VALUE )
+    @ResponseBody
+    @ApiOperation(value = "图片展示",notes = "filePath:相对路径")
+    public byte[] showPhoto(String filePath) throws Exception {
+        try{
+            File file = new File(filePath);
+            if(file.exists()){
+                FileInputStream inputStream = new FileInputStream(file);
+                byte [] bytes = new byte[inputStream.available()];
+                inputStream.read(bytes,0,inputStream.available());
+                return bytes;
+            }
+            return null;
+        }catch(Exception e){
+            throw new Exception("未找到图片");
+        }
+    }
+
+}

+ 92 - 0
src/main/java/com/rf/help/filter/AuthenticationInterceptor.java

@@ -0,0 +1,92 @@
+package com.rf.help.filter;
+
+import com.alibaba.fastjson.JSON;
+import com.auth0.jwt.exceptions.AlgorithmMismatchException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.rf.help.utils.JWTUtil;
+import com.rf.help.utils.LocalAssert;
+import com.rf.help.utils.WebContextUtil;
+import io.swagger.models.HttpMethod;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.stereotype.Component;
+import org.springframework.web.method.HandlerMethod;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.lang.reflect.Method;
+import java.util.HashMap;
+
+/**
+ * @Description:使用AuthenticationInterceptor拦截器对接口参数进行验证
+ * @Author: mimang
+ * @Date: 2024/9/6
+ */
+@Slf4j
+@Component
+public class AuthenticationInterceptor implements HandlerInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        HashMap<String, Object> resultJson = new HashMap<>();
+        final String url = request.getRequestURI();
+        log.info("url-------------------"+url);
+        // 从http请求头中取出token
+        final String token = request.getHeader(JWTUtil.AUTH_HEADER_KEY);
+        //如果不是映射到方法,直接通过
+        if(!(handler instanceof HandlerMethod)){
+            return true;
+        }
+        //如果是方法探测,直接通过
+        if (HttpMethod.OPTIONS.equals(request.getMethod())) {
+            response.setStatus(HttpServletResponse.SC_OK);
+            return true;
+        }
+        //如果方法有JwtIgnore注解,直接通过
+        HandlerMethod handlerMethod = (HandlerMethod) handler;
+        Method method=handlerMethod.getMethod();
+        if (method.isAnnotationPresent(JwtIgnore.class)) {
+            JwtIgnore jwtIgnore = method.getAnnotation(JwtIgnore.class);
+            if(jwtIgnore.value()){
+                return true;
+            }
+        }
+
+        try {
+            LocalAssert.isStringEmpty(token, "token为空,鉴权失败!");
+            //验证,并获取token内部信息
+            String userToken = JWTUtil.verifyToken(token);
+            //将token放入本地缓存
+            WebContextUtil.setUserToken(userToken);
+            return true;
+        }catch (SignatureVerificationException e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "无效签名信息");
+        } catch (AlgorithmMismatchException e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "token算法不一致");
+        }catch (TokenExpiredException e){
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "token已过期,请重新获取");
+        } catch (Exception e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "登录状态失效,请重新登录");
+        }
+        String s = JSON.toJSONString(resultJson);
+        response.setContentType("application/json;charset=UTF-8");
+        response.getWriter().println(s);
+        return false;
+    }
+
+    @Override
+    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+        //方法结束后,移除缓存的token
+        WebContextUtil.removeUserToken();
+    }
+}
+

+ 62 - 0
src/main/java/com/rf/help/filter/JWTInterceptor.java

@@ -0,0 +1,62 @@
+package com.rf.help.filter;
+
+import com.alibaba.fastjson.JSON;
+import com.auth0.jwt.exceptions.AlgorithmMismatchException;
+import com.auth0.jwt.exceptions.SignatureVerificationException;
+import com.rf.help.utils.JWTUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.lang.StringUtils;
+import org.springframework.stereotype.Component;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.HashMap;
+
+/**
+ * @author lpf
+ * @description:
+ * @date 2021/12/2821:48
+ */
+@Slf4j
+public class JWTInterceptor implements HandlerInterceptor {
+
+    @Override
+    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+        HashMap<String, Object> resultJson = new HashMap<>();
+        String uri = request.getRequestURI();
+        try {
+            String token = request.getHeader("Authorization");
+            if(StringUtils.isEmpty(token)) {
+                resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+                resultJson.put("msg", "无效签名信息");
+            }
+            String[] len = token.split(" ");
+            if (len.length >=1){
+                token = len[1];
+                JWTUtil.verify(token);
+                return true;
+            }else {
+                resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+                resultJson.put("msg", "无效签名信息");
+            }
+
+        } catch (SignatureVerificationException e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "无效签名信息");
+        } catch (AlgorithmMismatchException e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "token算法不一致");
+        } catch (Exception e) {
+            e.printStackTrace();
+            resultJson.put("code", HttpServletResponse.SC_UNAUTHORIZED);
+            resultJson.put("msg", "登录状态失效,请重新登录");
+        }
+        String s = JSON.toJSONString(resultJson);
+        response.setContentType("application/json;charset=UTF-8");
+        response.getWriter().println(s);
+        return false;
+    }
+}

+ 49 - 0
src/main/java/com/rf/help/filter/JWTInterceptorConfig.java

@@ -0,0 +1,49 @@
+package com.rf.help.filter;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+/**
+ * @author lpf
+ * @description:
+ * @date 2021/12/2822:06
+ */
+@Configuration
+public class JWTInterceptorConfig implements WebMvcConfigurer {
+
+    @Override
+    public void addInterceptors(InterceptorRegistry registry) {
+        String[] swaggerExcludes = new String[]{"/swagger-ui.html", "/swagger-resources/**", "/csrf", "/webjars/**"};
+        String[] webLogin = new String[]{"/user/authCode", "/user/saveUser", "/user/webLogin", "/user/webUpdatePassword", "/user/*/checkUser"};
+        String[] systemApi = new String[]{"/user/code2openid/**","/user/show"};
+        registry.addInterceptor(new AuthenticationInterceptor())
+                .addPathPatterns("/**")
+                .excludePathPatterns("/", "/index**", "/error")
+                .excludePathPatterns(systemApi)
+                //.excludePathPatterns(webLogin)
+                .excludePathPatterns(swaggerExcludes)
+                .excludePathPatterns("/favicon.ico")
+                .excludePathPatterns("/druid/**")
+                .excludePathPatterns("/static/**");
+        registry.addInterceptor(new AuthenticationInterceptor())
+                .addPathPatterns("/**")
+                .excludePathPatterns("/", "/index.html", "/error")
+                .excludePathPatterns(systemApi)
+                //.excludePathPatterns(webLogin)
+                .excludePathPatterns(swaggerExcludes)
+                .excludePathPatterns("/favicon.ico")
+                .excludePathPatterns("/druid/**")
+                .excludePathPatterns("/static/**");
+
+    }
+
+    @Override
+    public void addResourceHandlers(ResourceHandlerRegistry registry) {
+        registry.addResourceHandler("/static/**")
+                .addResourceLocations("classpath:/static/");
+    }
+}

+ 12 - 0
src/main/java/com/rf/help/filter/JwtIgnore.java

@@ -0,0 +1,12 @@
+package com.rf.help.filter;
+
+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 JwtIgnore {
+    boolean value() default true;
+}

+ 67 - 0
src/main/java/com/rf/help/security/SSLConfig.java

@@ -0,0 +1,67 @@
+package com.rf.help.security;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.connector.Connector;
+import org.apache.tomcat.util.descriptor.web.SecurityCollection;
+import org.apache.tomcat.util.descriptor.web.SecurityConstraint;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.Profile;
+
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/9/15 14:32
+ */
+@Configuration
+@Profile({"prod", "public"})
+public class SSLConfig {
+
+    @Value("${spring.profiles.active}")
+    private String profile;
+
+    @Bean
+    public TomcatServletWebServerFactory servletContainer() {
+
+        TomcatServletWebServerFactory tomcat = new TomcatServletWebServerFactory() {
+
+            @Override
+            protected void postProcessContext(Context context) {
+
+                SecurityConstraint securityConstraint = new SecurityConstraint();
+                securityConstraint.setUserConstraint("CONFIDENTIAL");
+                SecurityCollection collection = new SecurityCollection();
+                collection.addPattern("/*");
+                securityConstraint.addCollection(collection);
+                context.addConstraint(securityConstraint);
+            }
+        };
+        tomcat.addAdditionalTomcatConnectors(initiateHttpConnector());
+        return tomcat;
+    }
+
+
+    private Connector initiateHttpConnector() {
+        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
+        connector.setScheme("http");
+        connector.setSecure(false);
+        if ("prod".equals(profile)) {
+            connector.setPort(8082);
+            connector.setRedirectPort(8848);
+        } else if ("public".equals(profile)) {
+            connector.setPort(8081);
+            connector.setRedirectPort(8443);
+        } else {
+            //TODO 待定
+            connector.setPort(9528);
+            connector.setRedirectPort(8849);
+        }
+
+
+        return connector;
+    }
+
+}

+ 18 - 0
src/main/java/com/rf/help/security/SafetyProcess.java

@@ -0,0 +1,18 @@
+package com.rf.help.security;
+
+import java.lang.annotation.*;
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/7/29 11:44
+ */
+@Target({ElementType.METHOD,ElementType.TYPE})
+@Retention(RetentionPolicy.RUNTIME)
+@Documented
+@Inherited
+public @interface SafetyProcess {
+    boolean decode() default false;
+
+    boolean encode() default true;
+}

+ 36 - 0
src/main/java/com/rf/help/task/dao/model/ReplyEntity.java

@@ -0,0 +1,36 @@
+package com.rf.help.task.dao.model;
+
+import com.rf.help.base.model.BaseEntity;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.NoArgsConstructor;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author zsy
+ * @description:回答信息表
+ * @date 2021/6/17 15:55
+ */
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "t_reply_info")
+@EqualsAndHashCode(callSuper=true)
+@org.hibernate.annotations.Table(appliesTo = "t_reply_info", comment = "回答信息表")
+public class ReplyEntity extends BaseEntity {
+
+    @Column(name = "task_id", columnDefinition = "varchar(36) not null comment '所属任务ID'")
+    private String taskId;
+
+    @Column(name = "reply_content", columnDefinition = "text not null comment '回答内容'")
+    private String replyContent;
+
+    @Column(name = "reply_status", columnDefinition = "varchar(2) not null comment '状态(1:无状态;2:有用;3:无用)'")
+    private String replyStatus;
+
+}

+ 60 - 0
src/main/java/com/rf/help/task/dao/model/TaskEntity.java

@@ -0,0 +1,60 @@
+package com.rf.help.task.dao.model;
+
+import com.rf.help.base.model.BaseEntity;
+import lombok.*;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author zsy
+ * @description:任务信息表
+ * @date 2021/6/17 15:55
+ */
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "t_task_info")
+@EqualsAndHashCode(callSuper=true)
+@org.hibernate.annotations.Table(appliesTo = "t_task_info", comment = "任务信息表")
+public class TaskEntity extends BaseEntity {
+
+    @Column(name = "take_delivery_position", columnDefinition = "varchar(255) comment '取货位置'")
+    private String takeDeliveryPosition;
+
+    @Column(name = "delivery_position", columnDefinition = "varchar(255) comment '送达位置'")
+    private String deliveryPosition;
+
+    @Column(name = "item_size", columnDefinition = "varchar(2) comment '物品大小(1:大件:2:小件)'")
+    private String itemSize;
+
+    @Column(name = "title", columnDefinition = "varchar(100) comment '标题信息'")
+    private String title;
+
+    @Column(name = "details", columnDefinition = "varchar(255) comment '详细信息'")
+    private String details;
+
+    @Column(name = "remark", columnDefinition = "varchar(255) comment '备注信息'")
+    private String remark;
+
+    @Column(name = "bonus", columnDefinition = "varchar(6) comment '悬赏金额'")
+    private String bonus;
+
+    @Column(name = "phone", columnDefinition = "varchar(15) comment '联系电话'")
+    private String phone;
+
+    @Column(name = "task_type", columnDefinition = "varchar(2) not null comment '任务类型(1:取快递,2:带饭,3:跑腿,4:问答)'")
+    private String taskType;
+
+    @Column(name = "publish_user_id", columnDefinition = "varchar(36) not null comment '发布人ID'")
+    private String publishUserId;
+
+    @Column(name = "accept_user_id", columnDefinition = "varchar(36) comment '接单人ID'")
+    private String acceptUserId;
+
+    @Column(name = "task_status", columnDefinition = "varchar(2) not null comment '任务状态(1:已发布,2:已接单,3:已完成)'")
+    private String taskStatus;
+
+}

+ 24 - 0
src/main/java/com/rf/help/task/dao/repository/ReplyRepository.java

@@ -0,0 +1,24 @@
+package com.rf.help.task.dao.repository;
+
+import com.rf.help.base.repository.BaseRepository;
+import com.rf.help.task.dao.model.ReplyEntity;
+import com.rf.help.task.dao.model.TaskEntity;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.Query;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface ReplyRepository extends BaseRepository<ReplyEntity, String> {
+
+    @Query(value = "select * from t_reply_info where task_id = ?1 " +
+            "and if(?2 is not null and ?2 !='',(reply_content like CONCAT('%',?2,'%')),1=1) " +
+            "order by create_time desc ",
+            countQuery = "select * from t_reply_info where task_id = ?1 " +
+                    "and if(?2 is not null and ?2 !='',(reply_content like CONCAT('%',?2,'%')),1=1) ",
+            nativeQuery = true)
+    Page<ReplyEntity> findReplyByTaskId(String taskId, String searchKey, PageRequest of);
+}

+ 30 - 0
src/main/java/com/rf/help/task/dao/repository/TaskRepository.java

@@ -0,0 +1,30 @@
+package com.rf.help.task.dao.repository;
+
+import com.rf.help.base.repository.BaseRepository;
+import com.rf.help.task.dao.model.TaskEntity;
+import com.rf.help.user.dao.model.UserEntity;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.jpa.repository.Query;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface TaskRepository extends BaseRepository<TaskEntity, String> {
+
+    @Query(value = "select * from t_task_info where task_type = ?1 " +
+            "and if(?2 is not null and ?2 !='',(details like CONCAT('%',?2,'%') or phone like CONCAT('%',?2,'%')),1=1) " +
+            "and if(?3 is not null and ?3 !='',(publish_user_id = ?3),1=1) " +
+            "and if(?4 is not null and ?4 !='',(accept_user_id = ?4),1=1) " +
+            "and if(?5 is not null and ?5 !='',(task_status = ?5),1=1) " +
+            "order by create_time desc ",
+            countQuery = "select * from t_task_info where task_type = ?1 " +
+                    "and if(?2 is not null and ?2 !='',(details like CONCAT('%',?2,'%') or phone like CONCAT('%',?2,'%')),1=1) " +
+                    "and if(?3 is not null and ?3 !='',(publish_user_id = ?3),1=1) " +
+                    "and if(?4 is not null and ?4 !='',(accept_user_id = ?4),1=1) " +
+                    "and if(?5 is not null and ?5 !='',(task_status = ?5),1=1) ",
+            nativeQuery = true)
+    Page<TaskEntity> findTaskList(String taskType, String searchKey, String publishUserId, String acceptUserId, String taskStatus, PageRequest of);
+}

+ 64 - 0
src/main/java/com/rf/help/task/rest/ReplyController.java

@@ -0,0 +1,64 @@
+package com.rf.help.task.rest;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.rf.help.base.rest.BaseController;
+import com.rf.help.task.dao.model.ReplyEntity;
+import com.rf.help.task.dao.model.TaskEntity;
+import com.rf.help.task.service.ReplyService;
+import com.rf.help.task.service.TaskService;
+import com.rf.help.user.dao.model.UserEntity;
+import com.rf.help.user.service.UserService;
+import com.rf.help.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.web.bind.annotation.*;
+
+/**
+ * @Description: 回答等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@RestController
+@RequestMapping("/reply")
+@Api(tags = "回答等相关接口")
+@Slf4j
+public class ReplyController extends BaseController {
+
+    @Autowired
+    private ReplyService replyService;
+
+    @PostMapping("/publishReply")
+    @ApiOperation(value = "发布回答和修改状态", notes = "ReplyEntity对象")
+    public Result publishReply(@RequestBody String json) {
+        ReplyEntity replyEntity = JSONObject.parseObject(json, ReplyEntity.class);
+        if (replyEntity.getId() == null || replyEntity.getId().equals("")) {
+            replyEntity.setCreateTime(DateUtil.now());
+            replyEntity.setUpdateTime(DateUtil.now());
+        }else {
+            replyEntity.setUpdateTime(DateUtil.now());
+        }
+        replyEntity = this.replyService.save(replyEntity);
+        return success(replyEntity);
+    }
+
+    @GetMapping("/deleteReply")
+    @ApiOperation(value = "删除回答", notes = "参数包括:id:回答id")
+    public Result deleteReply(@RequestParam String id) {
+        this.replyService.deleteByid(id);
+        return success();
+    }
+
+    @GetMapping("/findReplyList")
+    @ApiOperation(value = "回答内容列表查询", notes = "参数包括:beginNum:页码,pageSize:每页条数,taskId:任务Id,searchKey:搜索信息")
+    public Result findReplyList(String taskId, String searchKey,int beginNum, int pageSize) {
+        Page<ReplyEntity> replyEntities = this.replyService.findReplyByTaskId(taskId, searchKey, beginNum, pageSize);
+        return success(replyEntities);
+    }
+
+
+
+}

+ 125 - 0
src/main/java/com/rf/help/task/rest/TaskController.java

@@ -0,0 +1,125 @@
+package com.rf.help.task.rest;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
+import com.rf.help.base.rest.BaseController;
+import com.rf.help.security.SafetyProcess;
+import com.rf.help.task.dao.model.TaskEntity;
+import com.rf.help.task.service.TaskService;
+import com.rf.help.user.dao.model.UserEntity;
+import com.rf.help.user.service.UserService;
+import com.rf.help.utils.Constant;
+import com.rf.help.utils.JWTUtil;
+import com.rf.help.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.Date;
+import java.util.HashMap;
+
+/**
+ * @Description: 任务等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@RestController
+@RequestMapping("/task")
+@Api(tags = "任务等相关接口")
+@Slf4j
+public class TaskController extends BaseController {
+
+    @Autowired
+    private TaskService taskService;
+
+    @Autowired
+    private UserService userService;
+
+    @PostMapping("/publishTask")
+    @ApiOperation(value = "发布任务和修改完成状态", notes = "TaskEntity对象")
+    public Result publishTask(@RequestBody String json) {
+        TaskEntity taskEntity = JSONObject.parseObject(json, TaskEntity.class);
+        if (taskEntity.getId() == null || taskEntity.getId().equals("")) {
+            taskEntity.setCreateTime(DateUtil.now());
+            taskEntity.setUpdateTime(DateUtil.now());
+        }else {
+            taskEntity.setUpdateTime(DateUtil.now());
+        }
+        taskEntity = this.taskService.save(taskEntity);
+        return success(taskEntity);
+    }
+
+    @GetMapping("/acceptTask")
+    @ApiOperation(value = "接受任务", notes = "参数包括:taskId:任务id,acceptUserId:接单人id")
+    public Result acceptTask(@RequestParam String acceptUserId, @RequestParam String taskId) throws InterruptedException {
+        UserEntity userEntity = this.userService.findById(acceptUserId);
+        if (userEntity == null) {
+            return fail(null, "用户不存在");
+        }
+        TaskEntity taskEntity = this.taskService.findById(taskId);
+        if (taskEntity == null) {
+            return fail(null, "任务不存在");
+        }
+        Thread.sleep(2000);
+        if (taskEntity.getTaskStatus().equals("1")){
+            if (synchronizedStaticMethod(taskId,acceptUserId)){
+                return success("接单成功");
+            }else {
+                return fail(null, "任务已被接受");
+            }
+        }else {
+            return fail(null, "任务已被接受");
+        }
+    }
+
+    public synchronized boolean synchronizedStaticMethod(String taskId, String acceptUserId) {
+        TaskEntity taskEntity = this.taskService.findById(taskId);
+        if (taskEntity.getTaskStatus().equals("1")){
+            taskEntity.setTaskStatus("2");
+            taskEntity.setAcceptUserId(acceptUserId);
+            taskEntity.setUpdateTime(DateUtil.now());
+            this.taskService.save(taskEntity);
+            return true;
+        }else {
+            return false;
+        }
+    }
+    @GetMapping("/findTask")
+    @ApiOperation(value = "查看任务详情", notes = "参数包括:id:任务id")
+    public Result findTask(@RequestParam String id) {
+        return success(this.taskService.findById(id));
+    }
+
+    @GetMapping("/deleteTask")
+    @ApiOperation(value = "删除任务", notes = "参数包括:id:任务id")
+    public Result deleteTask(@RequestParam String id) {
+        this.taskService.deleteByid(id);
+        return success();
+    }
+
+    @GetMapping("/findTaskList")
+    @ApiOperation(value = "任务订单列表查询", notes = "参数包括:beginNum:页码,pageSize:每页条数,taskType:任务类型(1:取快递,2:带饭,3:跑腿,4:问答)," +
+            "searchKey:搜索信息,publishUserId:发布人ID,acceptUserId:接单人ID,taskStatus:任务状态(1:已发布,2:已接单,3:已完成)")
+    public Result findTaskList(String taskType, String searchKey, String publishUserId, String acceptUserId, String taskStatus, int beginNum, int pageSize) {
+        Page<TaskEntity> taskEntities = this.taskService.findTaskList(taskType, searchKey, publishUserId, acceptUserId, taskStatus, beginNum, pageSize);
+        return success(taskEntities);
+    }
+
+
+
+}

+ 20 - 0
src/main/java/com/rf/help/task/service/ReplyService.java

@@ -0,0 +1,20 @@
+package com.rf.help.task.service;
+
+
+import com.rf.help.task.dao.model.ReplyEntity;
+import com.rf.help.task.dao.model.TaskEntity;
+import org.springframework.data.domain.Page;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface ReplyService {
+
+    ReplyEntity save(ReplyEntity replyEntity);
+
+    Page<ReplyEntity> findReplyByTaskId(String taskId, String searchKey, int beginNum, int pageSize);
+
+    void deleteByid(String id);
+}

+ 21 - 0
src/main/java/com/rf/help/task/service/TaskService.java

@@ -0,0 +1,21 @@
+package com.rf.help.task.service;
+
+
+import com.rf.help.task.dao.model.TaskEntity;
+import org.springframework.data.domain.Page;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface TaskService {
+
+    TaskEntity save(TaskEntity taskEntity);
+
+    void deleteByid(String id);
+
+    TaskEntity findById(String taskId);
+
+    Page<TaskEntity> findTaskList(String taskType, String searchKey, String publishUserId, String acceptUserId, String taskStatus, int beginNum, int pageSize);
+}

+ 37 - 0
src/main/java/com/rf/help/task/service/impl/ReplyServiceImpl.java

@@ -0,0 +1,37 @@
+package com.rf.help.task.service.impl;
+
+import com.rf.help.task.dao.model.ReplyEntity;
+import com.rf.help.task.dao.repository.ReplyRepository;
+import com.rf.help.task.service.ReplyService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@Service
+public class ReplyServiceImpl implements ReplyService {
+
+    @Autowired
+    private ReplyRepository replyRepository;
+
+    @Override
+    public ReplyEntity save(ReplyEntity replyEntity) {
+        return this.replyRepository.save(replyEntity);
+    }
+
+    @Override
+    public Page<ReplyEntity> findReplyByTaskId(String taskId, String searchKey, int beginNum, int pageSize) {
+        return this.replyRepository.findReplyByTaskId(taskId, searchKey, PageRequest.of(beginNum - 1, pageSize));
+    }
+
+    @Override
+    public void deleteByid(String id) {
+        this.replyRepository.deleteById(id);
+    }
+}

+ 46 - 0
src/main/java/com/rf/help/task/service/impl/TaskServiceImpl.java

@@ -0,0 +1,46 @@
+package com.rf.help.task.service.impl;
+
+import com.rf.help.task.dao.model.TaskEntity;
+import com.rf.help.task.dao.repository.TaskRepository;
+import com.rf.help.task.service.TaskService;
+import com.rf.help.user.dao.model.UserEntity;
+import com.rf.help.user.dao.repository.UserRepository;
+import com.rf.help.user.service.UserService;
+import com.rf.help.utils.Constant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@Service
+public class TaskServiceImpl implements TaskService {
+
+    @Autowired
+    private TaskRepository taskRepository;
+
+    @Override
+    public TaskEntity save(TaskEntity taskEntity) {
+        return this.taskRepository.save(taskEntity);
+    }
+
+    @Override
+    public void deleteByid(String id) {
+        this.taskRepository.deleteById(id);
+    }
+
+    @Override
+    public TaskEntity findById(String taskId) {
+        return this.taskRepository.findById(taskId).orElse(null);
+    }
+
+    @Override
+    public Page<TaskEntity> findTaskList(String taskType, String searchKey, String publishUserId, String acceptUserId, String taskStatus, int beginNum, int pageSize) {
+        return this.taskRepository.findTaskList(taskType, searchKey, publishUserId, acceptUserId, taskStatus, PageRequest.of(beginNum - 1, pageSize));
+    }
+}

+ 42 - 0
src/main/java/com/rf/help/user/dao/model/UserEntity.java

@@ -0,0 +1,42 @@
+package com.rf.help.user.dao.model;
+
+import com.rf.help.base.model.BaseEntity;
+import lombok.*;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.Table;
+
+/**
+ * @author zsy
+ * @description:用户信息表
+ * @date 2021/6/17 15:55
+ */
+@Entity
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(name = "t_user_info")
+@EqualsAndHashCode(callSuper=true)
+@org.hibernate.annotations.Table(appliesTo = "t_user_info", comment = "用户信息表")
+public class UserEntity extends BaseEntity {
+
+    @Column(name = "user_name", columnDefinition = "varchar(50) comment '昵称'")
+    private String userName;
+
+    @Column(name = "open_id", columnDefinition = "varchar(36) not null comment 'openId'")
+    private String openId;
+
+    @Column(name = "phone", columnDefinition = "varchar(36) comment '手机号'")
+    private String phone;
+
+    @Column(name = "gender", columnDefinition = "varchar(2) comment '性别(0-男;1-女)'")
+    private String gender;
+
+    @Column(name = "password", columnDefinition = "varchar(32) comment '密码'")
+    private String password;
+
+    @Column(name = "avatar", columnDefinition = "varchar(255) comment '头像URL'")
+    private String avatar;
+
+}

+ 14 - 0
src/main/java/com/rf/help/user/dao/repository/UserRepository.java

@@ -0,0 +1,14 @@
+package com.rf.help.user.dao.repository;
+
+import com.rf.help.base.repository.BaseRepository;
+import com.rf.help.user.dao.model.UserEntity;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface UserRepository extends BaseRepository<UserEntity, String> {
+
+    UserEntity findByOpenId(String openId);
+}

+ 185 - 0
src/main/java/com/rf/help/user/rest/UserController.java

@@ -0,0 +1,185 @@
+package com.rf.help.user.rest;
+
+import cn.hutool.core.date.DateUtil;
+import com.alibaba.fastjson.JSONObject;
+import com.google.gson.Gson;
+import com.rf.help.base.rest.BaseController;
+import com.rf.help.security.SafetyProcess;
+import com.rf.help.user.dao.model.UserEntity;
+import com.rf.help.user.service.UserService;
+import com.rf.help.utils.Constant;
+import com.rf.help.utils.JWTUtil;
+import com.rf.help.utils.Result;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.io.FileUtils;
+import org.apache.http.HttpResponse;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.impl.client.HttpClients;
+import org.apache.http.util.EntityUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.http.MediaType;
+import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.util.HashMap;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@RestController
+@RequestMapping("/user")
+@Api(tags = "用户注册登录等相关接口")
+@Slf4j
+public class UserController extends BaseController {
+
+    @Autowired
+    private UserService userService;
+
+    @GetMapping("/code2openid/{code}")
+    @ApiOperation(value = "code 换 opernid",notes = "code:临时code,获取openid")
+    @SafetyProcess
+    public Result<JSONObject> code2openid(@PathVariable String code) throws Exception {
+        String openId;
+        HttpResponse response = getToken(code);
+        int statusCode = response.getStatusLine().getStatusCode();//响应状态码
+        String body = EntityUtils.toString(response.getEntity());
+        Gson gson = new Gson();
+        HashMap resultBody = gson.fromJson(body, HashMap.class);
+        if(statusCode == 200 || statusCode == 204){
+            openId = (String) resultBody.get("openid");
+            if (openId == null){
+                log.error("错误码:{}",resultBody.get("errcode"));
+                log.error("错误消息:{}",resultBody.get("errmsg"));
+                return fail(String.valueOf(resultBody.get("errmsg")));
+            }
+        }else{
+            log.error("获取openid失败:错误码 "+resultBody.get("errcode")+"  "+resultBody.get("errmsg"));
+            log.error("错误码:{}",resultBody.get("errcode"));
+            log.error("错误消息:{}",resultBody.get("errmsg"));
+            return fail();
+        }
+        JSONObject resultJson = new JSONObject();
+        //静默注册登录
+        UserEntity userEntity = this.userService.findByOpenId(openId);
+        if(userEntity != null){
+            resultJson.put("improve",false);
+        }else{
+            //注册
+            userEntity = new UserEntity();
+            userEntity.setCreateTime(DateUtil.now());
+            userEntity.setUpdateTime(DateUtil.now());
+            userEntity.setUserName("微信用户");
+            userEntity.setOpenId(openId);
+            userEntity.setPhone("-");
+            userEntity.setGender("0");
+            userEntity.setPassword("-");
+            userEntity.setAvatar("-");
+
+            userEntity = this.userService.save(userEntity);
+            resultJson.put("improve",true);
+        }
+
+        resultJson.put("token", JWTUtil.getTokenByUserInfo(userEntity));
+        resultJson.put("user",userEntity);
+        resultJson.put("openId",openId);
+        log.info("响应消息:"+resultJson.toJSONString());
+        return success(resultJson);
+    }
+
+    public HttpResponse getToken(String code) throws IOException {
+        String url = Constant.ASPI_URL +
+                "?appid=" + Constant.ASPI_APPID +
+                "&secret=" + Constant.ASPI_SECRET +
+                "&js_code=" + code +
+                "&grant_type=authorization_code";
+        log.info("code2openid URL :"+url);
+        HttpClient httpClient = HttpClients.createDefault();
+        HttpGet httpGet = new HttpGet(url);
+        httpGet.setHeader("Accept", "application/json");
+        return httpClient.execute(httpGet);
+    }
+
+    /**
+     * 修改用户信息
+     * @param
+     * @return
+     */
+    @ApiOperation(value = "修改用户信息接口",notes = "id:用户id,userName:昵称,phone:手机号,gender:性别(0-男;1-女),avatar:头像图片路径")
+    @PostMapping("/updateUser")
+    public Result updateAvatar(@RequestBody String json){
+            UserEntity userEntity = JSONObject.parseObject(json, UserEntity.class);
+            userEntity.setUpdateTime(DateUtil.now());
+            this.userService.save(userEntity);
+            return success(userEntity);
+    }
+
+    /**
+     * 上传图片
+     * @param
+     * @return
+     */
+    @ApiOperation(value = "上传图片",notes = "avatar:用户头像文件,userId:用户id")
+    @PostMapping("/uploadPictures")
+    public Result uploadPictures(String userId, MultipartFile avatar){
+        if (avatar == null) {
+            return fail("头像为空");
+        } else {
+            UserEntity userEntity = userService.findById(userId);
+            System.out.println("11:"+userEntity);
+            if (userEntity == null) {
+                return fail(null, "用户不存在");
+            }
+            String fileName = "";
+            String FILEDIR = "./userAvatar/";
+            String avatarUrl = "";
+            if (!avatar.isEmpty()) {
+                fileName = userEntity.getOpenId() + "-=-" + System.currentTimeMillis() + "-=-" + avatar.getOriginalFilename();
+                try {
+                    File temp = new File(FILEDIR);
+                    if (!temp.exists()) {
+                        temp.mkdirs();
+                    }
+                    File fileLocal = new File(FILEDIR, fileName);
+                    FileUtils.copyInputStreamToFile(avatar.getInputStream(), fileLocal);
+                    avatarUrl = FILEDIR + fileName;
+                    userEntity.setAvatar(avatarUrl);
+                    userEntity.setUpdateTime(DateUtil.now());
+                    this.userService.save(userEntity);
+                    return success(avatarUrl);
+                } catch (Exception e) {
+                    e.printStackTrace();
+                    return fail("文件上传失败");
+                }
+            } else {
+                return fail("图片为空");
+            }
+        }
+    }
+
+    @GetMapping(value = "/show",produces = MediaType.IMAGE_JPEG_VALUE )
+    @ResponseBody
+    @ApiOperation(value = "图片展示",notes = "filePath:相对路径")
+    public byte[] showPhoto(String filePath) throws Exception {
+        try{
+            File file = new File(filePath);
+            if(file.exists()){
+                FileInputStream inputStream = new FileInputStream(file);
+                byte [] bytes = new byte[inputStream.available()];
+                inputStream.read(bytes,0,inputStream.available());
+                return bytes;
+            }
+            return null;
+        }catch(Exception e){
+            throw new Exception("未找到图片");
+        }
+    }
+
+}

+ 17 - 0
src/main/java/com/rf/help/user/service/UserService.java

@@ -0,0 +1,17 @@
+package com.rf.help.user.service;
+
+import com.rf.help.user.dao.model.UserEntity;
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+public interface UserService {
+
+    UserEntity save(UserEntity userEntity);
+
+    UserEntity findById(String id);
+
+    UserEntity findByOpenId(String openId);
+}

+ 36 - 0
src/main/java/com/rf/help/user/service/impl/UserServiceImpl.java

@@ -0,0 +1,36 @@
+package com.rf.help.user.service.impl;
+
+import com.rf.help.user.dao.model.UserEntity;
+import com.rf.help.user.dao.repository.UserRepository;
+import com.rf.help.user.service.UserService;
+import com.rf.help.utils.Constant;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+
+
+/**
+ * @Description: 用户注册登录等相关接口
+ * @Author: zsy
+ * @Date: 2024/12/4
+ */
+@Service
+public class UserServiceImpl implements UserService {
+
+    @Autowired
+    private UserRepository userRepository;
+
+    @Override
+    public UserEntity save(UserEntity userEntity) {
+        return this.userRepository.save(userEntity);
+    }
+
+    @Override
+    public UserEntity findById(String id) {
+        return this.userRepository.findById(id).orElse(null);
+    }
+
+    @Override
+    public UserEntity findByOpenId(String openId) {
+        return this.userRepository.findByOpenId(openId);
+    }
+}

+ 42 - 0
src/main/java/com/rf/help/utils/Constant.java

@@ -0,0 +1,42 @@
+package com.rf.help.utils;
+
+/**
+ * @author zzf
+ * @description:静态类
+ * @date 2021/1/19 16:45
+ */
+public class Constant {
+
+
+    public static final String SYSTEM_VERSION ="1.0";
+
+
+
+    public static final String DEFAULT_VALUE_ZERO ="0";
+
+    public static String MAGIC_NUM_0 = "0";
+    public static String MAGIC_NUM_1 = "1";
+    public static String MAGIC_NUM_2 = "2";
+
+    public static final String DATE_TRANS_FLAG_START = "start";
+    public static final String DATE_TRANS_FLAG_END = "end";
+
+
+    public static final String DEFAULT_PASSWORD = "123456";
+
+    public static final String ASPI_URL = "https://api.weixin.qq.com/sns/jscode2session";
+    //小程序ID
+    public static final String ASPI_APPID = "wxfccd1cfccd4ac866";
+    //小程序秘钥
+    public static final String ASPI_SECRET = "a72f2f3f2034f0f5d824ef21cf6af81b";
+
+
+
+
+
+
+
+
+
+
+}

+ 19 - 0
src/main/java/com/rf/help/utils/HttpStatus.java

@@ -0,0 +1,19 @@
+package com.rf.help.utils;
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/1/18 19:37
+ */
+public interface HttpStatus {
+    String SUCCESS = "200";
+    String SUCCESS_PUB = "201";
+
+    String RUNTIME_EXCEPTION = "2001";
+
+    String SERVER_EXCEPTION = "500100";
+
+    String PARAMETER_ISNULL = "500101";
+
+    String USER_NOT_FOUND = "6001";
+}

+ 71 - 0
src/main/java/com/rf/help/utils/JWTUtil.java

@@ -0,0 +1,71 @@
+package com.rf.help.utils;
+
+import com.auth0.jwt.JWT;
+import com.auth0.jwt.JWTCreator;
+import com.auth0.jwt.algorithms.Algorithm;
+import com.auth0.jwt.exceptions.JWTVerificationException;
+import com.auth0.jwt.exceptions.TokenExpiredException;
+import com.auth0.jwt.interfaces.DecodedJWT;
+import com.rf.help.user.dao.model.UserEntity;
+
+import java.util.Calendar;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * @author lpf
+ * @description:
+ * @date 2021/12/2820:18
+ */
+public class JWTUtil {
+
+    private static final String SING = "xVWGEYTPF1hjnFt$HDZ0f^iet^^q@hZv";
+
+    public static final String AUTH_HEADER_KEY = "Authorization";
+
+    //token前缀
+    public static final String TOKEN_PREFIX = "Bearer ";
+
+    public static String getToken(Map<String, String> map) {
+
+        Calendar instance = Calendar.getInstance();
+        instance.add(Calendar.MONTH, 5);
+        JWTCreator.Builder builder = JWT.create();
+        map.forEach(builder::withClaim);
+        String token = builder.withExpiresAt(instance.getTime())
+                .sign(Algorithm.HMAC256(SING));
+        return token;
+    }
+
+    public static String getTokenByUserInfo(UserEntity userEntity){
+        HashMap<String, String> payload = new HashMap<>();
+        payload.put("phone", userEntity.getPhone());
+        payload.put("userId", userEntity.getId());
+        payload.put("userName", userEntity.getUserName());
+        payload.put("openId", userEntity.getOpenId());
+        return  getToken(payload);
+    }
+
+    /**
+     * 验证token
+     * @param token
+     */
+    public static String verifyToken(String token) throws Exception {
+        try {
+            return JWT.require(Algorithm.HMAC256(SING))
+                    .build()
+                    .verify(token.replace(TOKEN_PREFIX, ""))
+                    .getSubject();
+        } catch (TokenExpiredException e){
+            throw new Exception("token已失效,请重新登录",e);
+        } catch (JWTVerificationException e) {
+            throw new Exception("token验证失败!",e);
+        }
+    }
+
+
+    public static DecodedJWT verify(String token) {
+        return JWT.require(Algorithm.HMAC256(SING)).build().verify(token);
+    }
+
+}

+ 35 - 0
src/main/java/com/rf/help/utils/LocalAssert.java

@@ -0,0 +1,35 @@
+package com.rf.help.utils;
+
+import com.alibaba.druid.util.StringUtils;
+
+import java.util.Collection;
+
+/**
+ * @Description:断言工具类
+ * @Author: mimang
+ * @Date: 2024/9/6
+ */
+public abstract  class LocalAssert {
+    public static void isTrue(boolean expression, String message) throws RuntimeException {
+        if (!expression) {
+            throw new RuntimeException(message);
+        }
+    }
+    public static void isStringEmpty(String param, String message) throws RuntimeException{
+        if(StringUtils.isEmpty(param)) {
+            throw new RuntimeException(message);
+        }
+    }
+
+    public static void isObjectEmpty(Object object, String message) throws RuntimeException {
+        if (object == null) {
+            throw new RuntimeException(message);
+        }
+    }
+
+    public static void isCollectionEmpty(Collection coll, String message) throws RuntimeException {
+        if (coll == null || (coll.size() == 0)) {
+            throw new RuntimeException(message);
+        }
+    }
+}

+ 62 - 0
src/main/java/com/rf/help/utils/Result.java

@@ -0,0 +1,62 @@
+package com.rf.help.utils;
+
+/**
+ * @author zzf
+ * @description:
+ * @date 2021/1/18 19:27
+ */
+
+public class Result<T> {
+    /**
+     * 状态码
+     */
+    private String code;
+
+    /**
+     * 信息
+     */
+    private String msg;
+
+    /**
+     * 返回数据
+     */
+    private T data;
+
+    public Result(String code, String msg, T data) {
+        this.code = code;
+        this.msg = msg;
+        this.data = data;
+    }
+
+    public Result() {
+    }
+
+    public String getCode() {
+        return code;
+    }
+
+    public void setCode(String code) {
+        this.code = code;
+    }
+
+    public String getMsg() {
+        return msg;
+    }
+
+    public void setMsg(String msg) {
+        this.msg = msg;
+    }
+
+    public T getData() {
+        return data;
+    }
+
+    public void setData(T data) {
+        this.data = data;
+    }
+
+    public Result(String code, String msg) {
+        this.code = code;
+        this.msg = msg;
+    }
+}

+ 44 - 0
src/main/java/com/rf/help/utils/WebContextUtil.java

@@ -0,0 +1,44 @@
+package com.rf.help.utils;
+
+import com.alibaba.fastjson.JSONObject;
+
+/**
+ * @Description:线程缓存工具类
+ * @Author: mimang
+ * @Date: 2024/9/6
+ */
+public class WebContextUtil {
+    //本地线程缓存token
+    private static ThreadLocal<String> local = new ThreadLocal<>();
+
+    /**
+     * 设置token信息
+     * @param content
+     */
+    public static void setUserToken(String content){
+        removeUserToken();
+        local.set(content);
+    }
+
+    /**
+     * 获取token信息
+     * @return
+     */
+    /*public static UserEntity getUserToken(){
+        if(local.get() != null){
+            UserEntity userToken = JSONObject.parseObject(local.get() , UserEntity.class);
+            return userToken;
+        }
+        return null;
+    }*/
+
+    /**
+     * 移除token信息
+     * @return
+     */
+    public static void removeUserToken(){
+        if(local.get() != null){
+            local.remove();
+        }
+    }
+}

+ 48 - 0
src/main/resources/config/application-prod.yml

@@ -0,0 +1,48 @@
+server:
+  port: 8060
+  address: 0.0.0.0
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      driver-class-name: com.mysql.cj.jdbc.Driver
+      url: jdbc:mysql://10.11.7.12:3306/post_station1?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
+      username: root
+      password: Gqt@qnrcyz105
+      initial-size: 5
+      min-idle: 5
+      max-active: 20
+      max-wait: 30000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      validation-query: select '1' from dual
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+      pool-prepared-statements: true
+      max-open-prepared-statements: 20
+      max-pool-prepared-statement-per-connection-size: 20
+      filters: stat,wall
+      filter:
+        stat:
+          enabled: true
+          db-type: mysql
+          log-slow-sql: true
+          slow-sql-millis: 2000
+      stat-view-servlet:
+        url-pattern: /druid/*
+        reset-enable: false
+        login-username: testAdmin
+        login-password: asdcjiafh^&912834
+        enabled: true
+        allow:
+      web-stat-filter:
+        url-pattern: /*
+        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
+        enabled: true
+        session-stat-enable: true
+
+
+
+
+

+ 51 - 0
src/main/resources/config/application-test.yml

@@ -0,0 +1,51 @@
+server:
+  port: 8060
+  address: 0.0.0.0
+spring:
+  datasource:
+    type: com.alibaba.druid.pool.DruidDataSource
+    druid:
+      driver-class-name: com.mysql.cj.jdbc.Driver
+      url: jdbc:mysql://49.232.26.44:3306/reciprocal_help?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false&serverTimezone=GMT%2B8
+      username: root
+      password: Mysql@.2020
+      initial-size: 5
+      min-idle: 5
+      max-active: 20
+      max-wait: 30000
+      time-between-eviction-runs-millis: 60000
+      min-evictable-idle-time-millis: 300000
+      validation-query: select '1' from dual
+      test-while-idle: true
+      test-on-borrow: false
+      test-on-return: false
+      pool-prepared-statements: true
+      max-open-prepared-statements: 20
+      max-pool-prepared-statement-per-connection-size: 20
+      filters: stat,wall
+      filter:
+        stat:
+          enabled: true
+          db-type: mysql
+          log-slow-sql: true
+          slow-sql-millis: 2000
+      stat-view-servlet:
+        url-pattern: /druid/*
+        reset-enable: false
+        login-username: testAdmin
+        login-password: asdcjiafh^&912834
+        enabled: true
+        allow:
+      web-stat-filter:
+        url-pattern: /*
+        exclusions: "*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*"
+        enabled: true
+        session-stat-enable: true
+#swagger 显示隐藏配置
+swagger:
+  show: true
+
+
+
+
+

+ 26 - 0
src/main/resources/config/application.yml

@@ -0,0 +1,26 @@
+spring:
+  profiles:
+    active: test
+
+  jpa:
+    hibernate:
+      ddl-auto: update
+    database-platform: org.hibernate.dialect.MySQL5InnoDBDialect
+    show-sql: false
+    open-in-view: false
+  servlet:
+    multipart:
+      enabled: true
+      file-size-threshold: 2KB
+      max-request-size: 1000MB
+      max-file-size: 1000MB
+logging:
+  level:
+    org.springframework.security:
+      - debug
+      - info
+    org.springframework.web: error
+    org.hibernate.SQL: debug
+    org.hibernate.engine.QueryParameters: debug
+    org.hibernate.engine.query.HQLQueryPlan: debug
+    org.hibernate.type.descriptor.sql.BasicBinder: trace

+ 144 - 0
src/main/resources/logback.xml

@@ -0,0 +1,144 @@
+<?xml version="1.0" encoding="utf-8"?>
+<configuration>
+    <property name="LOG_HOME" value="./logs/%d{yyyy-MM-dd}"/>
+    <property name="LOG_NAME" value="reciprocalHelp"/>
+    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
+        <layout class="ch.qos.logback.classic.PatternLayout">
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %highlight(%-5level) %cyan(%logger{35}) |  %msg %n</pattern>
+        </layout>
+    </appender>
+    <appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+    <appender name="POSITION_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-position-sync.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <appender name="EVENT_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-event-sync.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <appender name="TRACK_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-track-sync.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <appender name="FILE_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-file-sync.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <appender name="RESTART_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-restart.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <appender name="REDIS_LOG_APPENDER" class="ch.qos.logback.core.rolling.RollingFileAppender">
+        <rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
+            <fileNamePattern>${LOG_HOME}/${LOG_NAME}-redis.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
+            <maxFileSize>100MB</maxFileSize>
+            <maxHistory>30</maxHistory>
+            <totalSizeCap>10GB</totalSizeCap>
+            <cleanHistoryOnStart>true</cleanHistoryOnStart>
+        </rollingPolicy>
+        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
+            <level>INFO</level>
+        </filter>
+        <encoder>
+            <pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{35} | %msg%n</pattern> 
+        </encoder>
+    </appender>
+
+    <root level="info">
+        <appender-ref ref="CONSOLE"/>
+        <appender-ref ref="FILE"/>
+    </root>
+    <logger name="com.patrol.mobile.controller" level="info" additivity="false">
+        <appender-ref ref="CONSOLE"/>
+    </logger>
+
+    <logger name="POSITION_LOG" level="info" additivity="false">
+        <appender-ref ref="POSITION_LOG_APPENDER"/>
+    </logger>
+    <logger name="EVENT_LOG" level="info" additivity="false">
+        <appender-ref ref="EVENT_LOG_APPENDER"/>
+    </logger>
+    <logger name="TRACK_LOG" level="info" additivity="false">
+        <appender-ref ref="TRACK_LOG_APPENDER"/>
+    </logger>
+    <logger name="FILE_LOG" level="info" additivity="false">
+        <appender-ref ref="FILE_LOG_APPENDER"/>
+    </logger>
+    <logger name="RESTART_LOG" level="info" additivity="false">
+        <appender-ref ref="RESTART_LOG_APPENDER"/>
+    </logger>
+    <logger name="REDIS_LOG" level="info" additivity="false">
+        <appender-ref ref="REDIS_LOG_APPENDER"/>
+    </logger>
+</configuration>

+ 28 - 0
src/main/resources/proguard.conf

@@ -0,0 +1,28 @@
+-ignorewarnings
+-keepdirectories
+
+-keep interface com.your_package.** { *; }
+-keep class com.your_package.main{ *; }
+-keep class com.your_package.model.** { *; }
+
+-keepparameternames
+-keepclassmembers @org.springframework.** class * {
+    *;
+}
+
+-keepclassmembers @org.springframework.** interface * {
+    *;
+}
+
+-keepclassmembers enum * {
+    public static **[] values();
+    public static ** valueOf(java.lang.String);
+}
+
+-keep @org.springframework.** class *
+-keepclassmembers @javax.** class * { *; }
+
+-dontwarn org.springframework.**
+-dontwarn javax.**
+-dontwarn org.yaml.snakeyaml.**
+-dontwarn okhttp3.**

二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743412955790-=-微信图片_20220210103150.jpg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743421867661-=-6LcknnZVJzhB9ca8ed84d5f1b445cdfd101807eea0d8.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422117043-=-zEoubMTvIVXP08d542ce58dca14ae0d88b3b02dade17.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422183594-=-PaJ3VeR5gaZl27ccb01c4c68b7f32adda0fb1b95caef.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422219713-=-uy4qqrMZkhfO43126e9f4ed7b6826ee6d478ff775823.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422240576-=-ZJAb409FAqPOb956267313c221db44a89b26c949cdc0.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422543977-=-wH3Vbk3vs6A01eabddd21c8b1b704fb527cc1bc38bbb.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422706287-=-WZmEYtsH6powb36da84fd8a2c1f8553c2c6cbbd68a18.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422725996-=-GDpyX12ALVQA2ade7b0258d2b4738920acd651764be5.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422738990-=-0VlVuYIAcmQc89bb9249c80f2d0d10557d45872bfd36.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422770610-=-XZ6dwgZ5tkXH8d60685d44425b2767b8775802baecbe.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422866008-=-045HfweuPSx4a647247c511ca29f202cc6e318e76087.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422895245-=-WeNSvYoudc2x14625654a849538b11212ab8e694f590.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743422948319-=-mvoJGIqDL8Mo062200f98c3ca9e6651e24b97afcd2f8.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743423043741-=-O4MJk0nqccND05d418bf2aa807b8d4efc3473abedb0f.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743423096410-=-MJfUYtf5V0Qie3dd4b80600dbdd62079b225dff1fa51.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468061570-=-aJBqL4zRib5U3522054ae7d5323589ec93328687a8dc.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468154690-=-ApzJxSIKUcHV659f794698003651c0a8c21efc17b7f5.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468415270-=-fIbIt2Wld6TD3df55119c5745ecc503ac7c4cd793388.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743468996537-=-1T954WgvkiUC9dc35d387b0981c9e3acdfc7dab0dc8d.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743469133335-=-AXs823UWfv5def9129cb13cea74f74880ddddb85f926.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743469228670-=-jPgcZv6Unzk80bf6b12a17088e287c7f6cac58ed5f30.jpeg


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743470633741-=-0103114133.png


二進制
userAvatar/o8kZk7LgN22vW481fDmbQjo4LbUM-=-1743470811355-=-0103114133.png