From e13e02190b9a33513dfbc39f7c91872347d82e1b Mon Sep 17 00:00:00 2001 From: shangjinshuai <1043076829@qq.com> Date: Tue, 16 Sep 2025 17:19:27 +0800 Subject: [PATCH] Initial commit: first version of project --- .gitattributes | 2 + .gitignore | 33 ++ .mvn/wrapper/maven-wrapper.properties | 2 + mvnw | 295 ++++++++++++++++++ mvnw.cmd | 189 +++++++++++ pom.xml | 142 +++++++++ .../com/example/carbon/CarbonApplication.java | 15 + .../common/aop/PermissionInterceptor.java | 29 ++ .../common/base/constant/ExpConstant.java | 23 ++ .../common/base/constant/SysConstant.java | 57 ++++ .../common/base/core/CommonServiceImpl.java | 84 +++++ .../common/base/entity/BaseHxEntity.java | 142 +++++++++ .../common/base/enums/IntValueEnum.java | 12 + .../common/base/enums/StrValueEnum.java | 12 + .../common/base/enums/TransAbleEnum.java | 144 +++++++++ .../enums/config/EnumInfoInitializer.java | 53 ++++ .../enums/config/JacksonEnumSettings.java | 180 +++++++++++ .../common/base/exception/BaseException.java | 51 +++ .../common/base/exception/BizException.java | 135 ++++++++ .../base/exception/LoginStateException.java | 16 + .../common/base/func/ThrowableConsumer.java | 10 + .../common/base/types/TypeTransfer.java | 7 + .../config/JacksonEnumConvertConfig.java | 36 +++ .../carbon/common/config/MinioConfig.java | 33 ++ .../common/config/MyBatisPlusConfig.java | 24 ++ .../carbon/common/config/RequestConfig.java | 26 ++ .../carbon/common/enums/DeleteStatus.java | 31 ++ .../common/enums/booking/BookingItem.java | 36 +++ .../common/enums/booking/BookingStatus.java | 38 +++ .../common/enums/jrn/PublishStatus.java | 34 ++ .../carbon/common/model/dto/PageAbleDto.java | 20 ++ .../BookingExperiencePageSelectDto.java | 22 ++ .../dto/jrn/JournalismPageSelectDto.java | 22 ++ .../common/model/dto/perm/LoginDto.java | 22 ++ .../entity/booking/HxBookingExperience.java | 74 +++++ .../common/model/entity/jrn/HxJournalism.java | 62 ++++ .../common/model/entity/sys/HxSysAttach.java | 43 +++ .../carbon/common/model/info/FileInfo.java | 33 ++ .../common/model/info/perm/UserLoginInfo.java | 27 ++ .../carbon/common/model/params/PageParam.java | 28 ++ .../BookingExperienceListSelectParam.java | 30 ++ .../params/jrn/JournalismListSelectParam.java | 38 +++ .../carbon/common/model/vo/ResultVo.java | 81 +++++ .../carbon/common/service/EmailService.java | 183 +++++++++++ .../carbon/common/utils/AttachUtil.java | 48 +++ .../example/carbon/common/utils/CodeUtil.java | 89 ++++++ .../carbon/common/utils/CollectionUtil.java | 183 +++++++++++ .../example/carbon/common/utils/DateUtil.java | 27 ++ .../example/carbon/common/utils/FileUtil.java | 250 +++++++++++++++ .../carbon/common/utils/ObjectUtil.java | 44 +++ .../HxBookingExperienceController.java | 75 +++++ .../jrn/HxJournalismController.java | 83 +++++ .../controller/perm/HxLoginController.java | 35 +++ .../controller/sys/HxSysAttachController.java | 42 +++ .../controller/sys/HxSysCommonController.java | 42 +++ .../controller/sys/HxSysMinioController.java | 80 +++++ .../booking/HxBookingExperienceMapper.java | 11 + .../carbon/mapper/jrn/HxJournalismMapper.java | 11 + .../carbon/mapper/sys/HxSysAttachMapper.java | 7 + .../booking/HxBookingExperienceService.java | 28 ++ .../HxBookingExperienceServiceImpl.java | 164 ++++++++++ .../impl/jrn/HxJournalismServiceImpl.java | 220 +++++++++++++ .../service/impl/perm/LoginService.java | 44 +++ .../impl/sys/HxSysAttachServiceImpl.java | 83 +++++ .../impl/sys/HxSysMinioServiceImpl.java | 228 ++++++++++++++ .../service/jrn/HxJournalismService.java | 28 ++ .../service/sys/HxSysAttachService.java | 38 +++ .../carbon/service/sys/HxSysMinioService.java | 45 +++ src/main/resources/application.yml | 82 +++++ 69 files changed, 4563 insertions(+) create mode 100644 .gitattributes create mode 100644 .gitignore create mode 100644 .mvn/wrapper/maven-wrapper.properties create mode 100644 mvnw create mode 100644 mvnw.cmd create mode 100644 pom.xml create mode 100644 src/main/java/com/example/carbon/CarbonApplication.java create mode 100644 src/main/java/com/example/carbon/common/aop/PermissionInterceptor.java create mode 100644 src/main/java/com/example/carbon/common/base/constant/ExpConstant.java create mode 100644 src/main/java/com/example/carbon/common/base/constant/SysConstant.java create mode 100644 src/main/java/com/example/carbon/common/base/core/CommonServiceImpl.java create mode 100644 src/main/java/com/example/carbon/common/base/entity/BaseHxEntity.java create mode 100644 src/main/java/com/example/carbon/common/base/enums/IntValueEnum.java create mode 100644 src/main/java/com/example/carbon/common/base/enums/StrValueEnum.java create mode 100644 src/main/java/com/example/carbon/common/base/enums/TransAbleEnum.java create mode 100644 src/main/java/com/example/carbon/common/base/enums/config/EnumInfoInitializer.java create mode 100644 src/main/java/com/example/carbon/common/base/enums/config/JacksonEnumSettings.java create mode 100644 src/main/java/com/example/carbon/common/base/exception/BaseException.java create mode 100644 src/main/java/com/example/carbon/common/base/exception/BizException.java create mode 100644 src/main/java/com/example/carbon/common/base/exception/LoginStateException.java create mode 100644 src/main/java/com/example/carbon/common/base/func/ThrowableConsumer.java create mode 100644 src/main/java/com/example/carbon/common/base/types/TypeTransfer.java create mode 100644 src/main/java/com/example/carbon/common/config/JacksonEnumConvertConfig.java create mode 100644 src/main/java/com/example/carbon/common/config/MinioConfig.java create mode 100644 src/main/java/com/example/carbon/common/config/MyBatisPlusConfig.java create mode 100644 src/main/java/com/example/carbon/common/config/RequestConfig.java create mode 100644 src/main/java/com/example/carbon/common/enums/DeleteStatus.java create mode 100644 src/main/java/com/example/carbon/common/enums/booking/BookingItem.java create mode 100644 src/main/java/com/example/carbon/common/enums/booking/BookingStatus.java create mode 100644 src/main/java/com/example/carbon/common/enums/jrn/PublishStatus.java create mode 100644 src/main/java/com/example/carbon/common/model/dto/PageAbleDto.java create mode 100644 src/main/java/com/example/carbon/common/model/dto/booking/BookingExperiencePageSelectDto.java create mode 100644 src/main/java/com/example/carbon/common/model/dto/jrn/JournalismPageSelectDto.java create mode 100644 src/main/java/com/example/carbon/common/model/dto/perm/LoginDto.java create mode 100644 src/main/java/com/example/carbon/common/model/entity/booking/HxBookingExperience.java create mode 100644 src/main/java/com/example/carbon/common/model/entity/jrn/HxJournalism.java create mode 100644 src/main/java/com/example/carbon/common/model/entity/sys/HxSysAttach.java create mode 100644 src/main/java/com/example/carbon/common/model/info/FileInfo.java create mode 100644 src/main/java/com/example/carbon/common/model/info/perm/UserLoginInfo.java create mode 100644 src/main/java/com/example/carbon/common/model/params/PageParam.java create mode 100644 src/main/java/com/example/carbon/common/model/params/booking/BookingExperienceListSelectParam.java create mode 100644 src/main/java/com/example/carbon/common/model/params/jrn/JournalismListSelectParam.java create mode 100644 src/main/java/com/example/carbon/common/model/vo/ResultVo.java create mode 100644 src/main/java/com/example/carbon/common/service/EmailService.java create mode 100644 src/main/java/com/example/carbon/common/utils/AttachUtil.java create mode 100644 src/main/java/com/example/carbon/common/utils/CodeUtil.java create mode 100644 src/main/java/com/example/carbon/common/utils/CollectionUtil.java create mode 100644 src/main/java/com/example/carbon/common/utils/DateUtil.java create mode 100644 src/main/java/com/example/carbon/common/utils/FileUtil.java create mode 100644 src/main/java/com/example/carbon/common/utils/ObjectUtil.java create mode 100644 src/main/java/com/example/carbon/controller/booking/HxBookingExperienceController.java create mode 100644 src/main/java/com/example/carbon/controller/jrn/HxJournalismController.java create mode 100644 src/main/java/com/example/carbon/controller/perm/HxLoginController.java create mode 100644 src/main/java/com/example/carbon/controller/sys/HxSysAttachController.java create mode 100644 src/main/java/com/example/carbon/controller/sys/HxSysCommonController.java create mode 100644 src/main/java/com/example/carbon/controller/sys/HxSysMinioController.java create mode 100644 src/main/java/com/example/carbon/mapper/booking/HxBookingExperienceMapper.java create mode 100644 src/main/java/com/example/carbon/mapper/jrn/HxJournalismMapper.java create mode 100644 src/main/java/com/example/carbon/mapper/sys/HxSysAttachMapper.java create mode 100644 src/main/java/com/example/carbon/service/booking/HxBookingExperienceService.java create mode 100644 src/main/java/com/example/carbon/service/impl/booking/HxBookingExperienceServiceImpl.java create mode 100644 src/main/java/com/example/carbon/service/impl/jrn/HxJournalismServiceImpl.java create mode 100644 src/main/java/com/example/carbon/service/impl/perm/LoginService.java create mode 100644 src/main/java/com/example/carbon/service/impl/sys/HxSysAttachServiceImpl.java create mode 100644 src/main/java/com/example/carbon/service/impl/sys/HxSysMinioServiceImpl.java create mode 100644 src/main/java/com/example/carbon/service/jrn/HxJournalismService.java create mode 100644 src/main/java/com/example/carbon/service/sys/HxSysAttachService.java create mode 100644 src/main/java/com/example/carbon/service/sys/HxSysMinioService.java create mode 100644 src/main/resources/application.yml diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 0000000..3b41682 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,2 @@ +/mvnw text eol=lf +*.cmd text eol=crlf diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..667aaef --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..44f3cf2 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,2 @@ +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.11/apache-maven-3.9.11-bin.zip diff --git a/mvnw b/mvnw new file mode 100644 index 0000000..e9cf8d3 --- /dev/null +++ b/mvnw @@ -0,0 +1,295 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.3 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + 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" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +scriptDir="$(dirname "$0")" +scriptName="$(basename "$0")" + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"$scriptDir/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${scriptName#mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c - >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +actualDistributionDir="" + +# First try the expected directory name (for regular distributions) +if [ -d "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" ]; then + if [ -f "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/bin/$MVN_CMD" ]; then + actualDistributionDir="$distributionUrlNameMain" + fi +fi + +# If not found, search for any directory with the Maven executable (for snapshots) +if [ -z "$actualDistributionDir" ]; then + # enable globbing to iterate over items + set +f + for dir in "$TMP_DOWNLOAD_DIR"/*; do + if [ -d "$dir" ]; then + if [ -f "$dir/bin/$MVN_CMD" ]; then + actualDistributionDir="$(basename "$dir")" + break + fi + fi + done + set -f +fi + +if [ -z "$actualDistributionDir" ]; then + verbose "Contents of $TMP_DOWNLOAD_DIR:" + verbose "$(ls -la "$TMP_DOWNLOAD_DIR")" + die "Could not find Maven distribution directory in extracted archive" +fi + +verbose "Found extracted Maven distribution directory: $actualDistributionDir" +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$actualDistributionDir/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$actualDistributionDir" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..2e2dbe0 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,189 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.3 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" ("%__MVNW_CMD__%" %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND -eq $False) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace "^.*$MVNW_REPO_PATTERN",'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' + +$MAVEN_M2_PATH = "$HOME/.m2" +if ($env:MAVEN_USER_HOME) { + $MAVEN_M2_PATH = "$env:MAVEN_USER_HOME" +} + +if (-not (Test-Path -Path $MAVEN_M2_PATH)) { + New-Item -Path $MAVEN_M2_PATH -ItemType Directory | Out-Null +} + +$MAVEN_WRAPPER_DISTS = $null +if ((Get-Item $MAVEN_M2_PATH).Target[0] -eq $null) { + $MAVEN_WRAPPER_DISTS = "$MAVEN_M2_PATH/wrapper/dists" +} else { + $MAVEN_WRAPPER_DISTS = (Get-Item $MAVEN_M2_PATH).Target[0] + "/wrapper/dists" +} + +$MAVEN_HOME_PARENT = "$MAVEN_WRAPPER_DISTS/$distributionUrlNameMain" +$MAVEN_HOME_NAME = ([System.Security.Cryptography.SHA256]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null + +# Find the actual extracted directory name (handles snapshots where filename != directory name) +$actualDistributionDir = "" + +# First try the expected directory name (for regular distributions) +$expectedPath = Join-Path "$TMP_DOWNLOAD_DIR" "$distributionUrlNameMain" +$expectedMvnPath = Join-Path "$expectedPath" "bin/$MVN_CMD" +if ((Test-Path -Path $expectedPath -PathType Container) -and (Test-Path -Path $expectedMvnPath -PathType Leaf)) { + $actualDistributionDir = $distributionUrlNameMain +} + +# If not found, search for any directory with the Maven executable (for snapshots) +if (!$actualDistributionDir) { + Get-ChildItem -Path "$TMP_DOWNLOAD_DIR" -Directory | ForEach-Object { + $testPath = Join-Path $_.FullName "bin/$MVN_CMD" + if (Test-Path -Path $testPath -PathType Leaf) { + $actualDistributionDir = $_.Name + } + } +} + +if (!$actualDistributionDir) { + Write-Error "Could not find Maven distribution directory in extracted archive" +} + +Write-Verbose "Found extracted Maven distribution directory: $actualDistributionDir" +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$actualDistributionDir" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..324809e --- /dev/null +++ b/pom.xml @@ -0,0 +1,142 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 2.2.6.RELEASE + + + + com.example + carbon + 0.0.1-SNAPSHOT + carbon + Demo project for Spring Boot + + + + + + + + + + + + + + + + + 8 + + + + + org.springframework.boot + spring-boot-starter-web + + + + + mysql + mysql-connector-java + 8.0.13 + runtime + + + + + com.alibaba + druid-spring-boot-starter + 1.2.16 + + + + + cn.hutool + hutool-all + 5.8.16 + + + + + com.baomidou + mybatis-plus-boot-starter + 3.4.3.4 + + + + com.github.yulichang + mybatis-plus-join-boot-starter + 1.5.2 + + + + + org.projectlombok + lombok + compile + + + + io.swagger.core.v3 + swagger-annotations + 2.2.20 + + + org.apache.commons + commons-lang3 + 3.9 + + + + + io.minio + minio + 8.5.9 + + + + com.squareup.okhttp3 + okhttp + 4.9.3 + + + + + com.sun.mail + javax.mail + 1.6.2 + + + + org.springframework.boot + spring-boot-starter-mail + + + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + + src/main/java + + **/*.xml + + + + src/main/resources + true + + + + + diff --git a/src/main/java/com/example/carbon/CarbonApplication.java b/src/main/java/com/example/carbon/CarbonApplication.java new file mode 100644 index 0000000..c7854ad --- /dev/null +++ b/src/main/java/com/example/carbon/CarbonApplication.java @@ -0,0 +1,15 @@ +package com.example.carbon; + +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +@MapperScan("com.example.carbon.mapper") +public class CarbonApplication { + + public static void main(String[] args) { + SpringApplication.run(CarbonApplication.class, args); + } + +} diff --git a/src/main/java/com/example/carbon/common/aop/PermissionInterceptor.java b/src/main/java/com/example/carbon/common/aop/PermissionInterceptor.java new file mode 100644 index 0000000..a6cda87 --- /dev/null +++ b/src/main/java/com/example/carbon/common/aop/PermissionInterceptor.java @@ -0,0 +1,29 @@ +package com.example.carbon.common.aop; + +import cn.hutool.core.lang.Validator; +import com.example.carbon.common.base.constant.SysConstant; +import com.example.carbon.common.base.exception.BizException; +import org.springframework.lang.NonNull; +import org.springframework.web.servlet.HandlerInterceptor; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +/** + * @author zx + * @since 2025-03-04 9:27 + */ +public class PermissionInterceptor implements HandlerInterceptor { + + @Override + public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, @NonNull Object handler) { + String token = request.getHeader(SysConstant.REQUEST_TOKEN_PARAM); + if (token == null){ + throw BizException.custom("token缺失!"); + } + if (token.length() != 32 || !Validator.isMatchRegex("^[a-zA-Z0-9_-]+$",token)){ + throw BizException.custom("token格式异常!"); + } + return true; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/common/base/constant/ExpConstant.java b/src/main/java/com/example/carbon/common/base/constant/ExpConstant.java new file mode 100644 index 0000000..8090130 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/constant/ExpConstant.java @@ -0,0 +1,23 @@ +package com.example.carbon.common.base.constant; + +/** + * @author zx + * @since 2025-01-17 19:42 + */ +public class ExpConstant { + + /** 异常代码 - 默认类型 */ + public static final String CODE_DEFAULT = "1"; + + /** 异常代码 - 业务类型 */ + public static final String CODE_BIZ = "10"; + + /** 异常代码 - 登录状态(用户需重新登录) */ + public static final String CODE_LOGIN_STATE = "-1"; + + /** 异常代码 - 关联企业/角色状态变化(需要切换用户当前所处企业) */ + public static final String CODE_STATE_CHANGE = "-2"; + + /** 异常代码 - 权限 */ + public static final String CODE_PERMISSION = "-3"; +} diff --git a/src/main/java/com/example/carbon/common/base/constant/SysConstant.java b/src/main/java/com/example/carbon/common/base/constant/SysConstant.java new file mode 100644 index 0000000..2a81c8d --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/constant/SysConstant.java @@ -0,0 +1,57 @@ +package com.example.carbon.common.base.constant; + +import java.util.HashMap; +import java.util.Map; + +public class SysConstant { + + /** token生成人 */ + public static final String TOKEN_ISSUER = "hx_carbon"; + /** token生成密钥 */ + public static final String TOKEN_KEY = "436319c6-d23f-4c18-a4f7-f1dc306a6471"; + + /** 默认用户密码 */ +// public static final String USER_DEFAULT_PWD = "zlcx@123456"; + public static final String USER_DEFAULT_PWD = "a123456"; + + /** 企业管理员 - 名称 */ + public static final String ORG_MANAGER_NAME = "管理员"; + /** 企业管理员 - 默认密码 */ +// public static final String ORG_MANAGER_DEFAULT_PWD = "zlcx@123456"; + public static final String ORG_MANAGER_DEFAULT_PWD = "a123456"; + + /** 系统管理员 - 名称 */ + public static final String SYSTEM_MANAGER_NAME = "系统管理员"; + /** 系统管理员 - 账号 */ + public static final Map SYSTEM_MANAGER = new HashMap<>(); + static { + SYSTEM_MANAGER.put("huaxing","huaxing"); + SYSTEM_MANAGER.put("zx","huaxing"); + SYSTEM_MANAGER.put("sjs","huaxing"); + SYSTEM_MANAGER.put("lmh","huaxing"); + SYSTEM_MANAGER.put("mj","huaxing"); + SYSTEM_MANAGER.put("cps","huaxing"); + SYSTEM_MANAGER.put("dyf","huaxing"); + } + /** 系统管理员 - 密码 */ + public static final String SYSTEM_MANAGER_PWD = "huaxing"; + + /** 网络请求 - 请求头token参数key */ + public static final String REQUEST_TOKEN_PARAM = "token"; + /** 网络请求 - 请求头org参数key */ + public static final String REQUEST_ORG_PARAM = "OrgCode"; + /** 网络请求 - 成功 */ + public static final String REQUEST_SUCCESS_CODE = "0"; + /** 网络请求 - 失败(默认) */ + public static final String REQUEST_CODE_FAIL = ExpConstant.CODE_DEFAULT; + + public static final String TREE_HEAT_START = "PT-"; + + public static final int GATEWAY_CONNECT_DEFAULT_OUT_TIME = 5000; + + public static final int GATEWAY_HEARTBEAT_INTERVAL_DEFAULT_TIME = 60; + + public static final int GATEWAY_RECONNECT_INTERVAL_DEFAULT_TIME = 5; + + public static final int GATEWAY_MAX_REPEAT_TIMES = 5; +} diff --git a/src/main/java/com/example/carbon/common/base/core/CommonServiceImpl.java b/src/main/java/com/example/carbon/common/base/core/CommonServiceImpl.java new file mode 100644 index 0000000..4c0a657 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/core/CommonServiceImpl.java @@ -0,0 +1,84 @@ +package com.example.carbon.common.base.core; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.example.carbon.common.base.entity.BaseHxEntity; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.enums.DeleteStatus; +import com.github.yulichang.base.MPJBaseMapper; +import com.github.yulichang.base.MPJBaseServiceImpl; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; +import java.util.function.Consumer; + +/** + * @author zx + * @since 2025-03-13 16:11 + */ +public abstract class CommonServiceImpl,T extends BaseHxEntity.Simple> extends MPJBaseServiceImpl { + + protected static final String Limit_1 = "limit 1"; + + public boolean updateWithNull(T entity,LambdaUpdateWrapper updateWrapper) { + if (entity.getId() == null) { + throw BizException.fromUpdate("id不可为null"); + } + return this.update(entity, updateWrapper + .eq(T::getId,entity.getId())); + } + + @Transactional(rollbackFor = Exception.class) + public List removeBatchById(LambdaQueryWrapper queryWrapper){ + return this.removeBatchById(queryWrapper, null); + } + + @Transactional(rollbackFor = Exception.class) + public List removeBatchById(LambdaQueryWrapper queryWrapper, Consumer consumer){ + return customRemoveBatchById(queryWrapper.select(T::getId), consumer); + } + + @Transactional(rollbackFor = Exception.class) + public List customRemoveBatchById(LambdaQueryWrapper queryWrapper, Consumer consumer){ + List dataList = this.list(queryWrapper); + for (T data:dataList){ + data.setDeleteStatus(DeleteStatus.YES); + if (consumer != null){ + consumer.accept(data); + } + } + this.updateBatchById(dataList); + return dataList; + } + + @Transactional(rollbackFor = Exception.class) + public T removeById(LambdaQueryWrapper queryWrapper){ + return this.removeById(queryWrapper,null); + } + + @Transactional(rollbackFor = Exception.class) + public T removeById(LambdaQueryWrapper queryWrapper, Consumer consumer){ + T data = this.getOne(queryWrapper.select(T::getId)); + if (consumer != null){ + consumer.accept(data); + } + this.updateById(data.setDeleteStatus(DeleteStatus.YES)); + return data; + } + + /*@Transactional(rollbackFor = Exception.class) + public void upgradeBatchById(LambdaUpdateWrapper updateWrapper){ + // 获取当前时间和用户信息 + Date now = new Date(); + HxPermUser user = UserContext.getInfo().getUser(); + String updateUser = user != null ? user.getCode() : "system"; + + // 设置更新字段 + updateWrapper.set(T::getUpdateTime, now) + .set(T::getUpdateUser, updateUser); + + // 执行更新操作(自动应用LambdaUpdateWrapper中的条件) + this.update(updateWrapper); + }*/ + +} diff --git a/src/main/java/com/example/carbon/common/base/entity/BaseHxEntity.java b/src/main/java/com/example/carbon/common/base/entity/BaseHxEntity.java new file mode 100644 index 0000000..0b90e77 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/entity/BaseHxEntity.java @@ -0,0 +1,142 @@ +package com.example.carbon.common.base.entity; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.extension.activerecord.Model; +import com.example.carbon.common.base.types.TypeTransfer; +import com.example.carbon.common.enums.DeleteStatus; +import com.fasterxml.jackson.annotation.JsonIgnore; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +import java.io.Serializable; +import java.util.Date; + +@Getter +public abstract class BaseHxEntity> extends Model implements Serializable, TypeTransfer { + + public final static String CREATE_TIME = "createTime"; + public final static String CREATE_USER = "createUser"; + public final static String CREATE_USER_NAME = "createUserName"; + + public final static String UPDATE_TIME = "updateTime"; + public final static String UPDATE_USER = "updateUser"; + public final static String UPDATE_USER_NAME = "updateUserName"; + + public final static String DELETE_STATUS = "deleteStatus"; + + @TableId(value = "id", type = IdType.AUTO) + protected Long id; + + public final T setId(Long id) { + this.id = id; + return this.asType(); + } + + @Getter + @Setter + @ToString(callSuper = true) + public static abstract class Simple> extends BaseHxEntity { + + @Schema(description = "创建时间") + @TableField(fill = FieldFill.INSERT) + protected Date createTime; + + @Schema(description = "创建人") + @TableField(fill = FieldFill.INSERT) + protected String createUser; + + @Schema(description = "修改时间") + @TableField(fill = FieldFill.INSERT_UPDATE) + protected Date updateTime; + + @Schema(description = "修改人") + @TableField(fill = FieldFill.INSERT_UPDATE) + protected String updateUser; + + @Schema(description = "删除状态(0:未删除,1:删除)") + protected DeleteStatus deleteStatus = DeleteStatus.NO; + + public final T setDeleteStatus(DeleteStatus deleteStatus) { + this.deleteStatus = deleteStatus; + return this.asType(); + } + + @JsonIgnore + public DeleteStatus getDeleteStatus() { + return deleteStatus; + } + } + + @Getter + @Setter + @ToString(callSuper = true) + public static abstract class SimpleData> extends Simple{ + + @JsonIgnore + @Override + public final Date getCreateTime() { + return super.getCreateTime(); + } + + @JsonIgnore + @Override + public String getCreateUser() { + return super.getCreateUser(); + } + + @JsonIgnore + @Override + public Date getUpdateTime() { + return super.getUpdateTime(); + } + + @JsonIgnore + @Override + public String getUpdateUser() { + return super.getUpdateUser(); + } + } + + @Getter + @Setter + @ToString(callSuper = true) + public static abstract class SimpleUser> extends BaseHxEntity.Simple{ + + @Schema(description = "创建人姓名") + @TableField(fill = FieldFill.INSERT) + protected String createUserName; + + @Schema(description = "修改人姓名") + @TableField(fill = FieldFill.INSERT_UPDATE) + protected String updateUserName; + } + + @Getter + @Setter + @ToString(callSuper = true) + public static abstract class SimpleUserData> extends BaseHxEntity.SimpleData{ + + @Schema(description = "创建人姓名") + @TableField(fill = FieldFill.INSERT) + protected String createUserName; + + @Schema(description = "修改人姓名") + @TableField(fill = FieldFill.INSERT_UPDATE) + protected String updateUserName; + + @JsonIgnore + public final String getCreateUserName() { + return this.createUserName; + } + + @JsonIgnore + public final String getUpdateUserName() { + return this.updateUserName; + } + } +} diff --git a/src/main/java/com/example/carbon/common/base/enums/IntValueEnum.java b/src/main/java/com/example/carbon/common/base/enums/IntValueEnum.java new file mode 100644 index 0000000..b19c9f3 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/enums/IntValueEnum.java @@ -0,0 +1,12 @@ +package com.example.carbon.common.base.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** 对Int类型数据进行数据库映射 */ +public interface IntValueEnum extends TransAbleEnum,IEnum { + + @Override + default Integer getValue() { + return this.getIntCode(); + } +} diff --git a/src/main/java/com/example/carbon/common/base/enums/StrValueEnum.java b/src/main/java/com/example/carbon/common/base/enums/StrValueEnum.java new file mode 100644 index 0000000..d04cdb5 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/enums/StrValueEnum.java @@ -0,0 +1,12 @@ +package com.example.carbon.common.base.enums; + +import com.baomidou.mybatisplus.annotation.IEnum; + +/** 对String类型数据进行数据库映射 */ +public interface StrValueEnum extends TransAbleEnum,IEnum { + + @Override + default String getValue() { + return this.getCode(); + } +} diff --git a/src/main/java/com/example/carbon/common/base/enums/TransAbleEnum.java b/src/main/java/com/example/carbon/common/base/enums/TransAbleEnum.java new file mode 100644 index 0000000..8501024 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/enums/TransAbleEnum.java @@ -0,0 +1,144 @@ +package com.example.carbon.common.base.enums; + +import com.example.carbon.common.base.enums.config.EnumInfoInitializer; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.utils.CollectionUtil; +import com.example.carbon.common.utils.ObjectUtil; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.Getter; +import lombok.Setter; +import org.apache.commons.lang3.StringUtils; + +import java.io.Serializable; +import java.util.*; + +/** + * 为枚举提供json数据转换与数据库映射功能 + * 1.根据不同需求实现其派生类型 + * a.Int类型数据 {@link IntValueEnum} + * b.String类型数据 {@link StrValueEnum} + * 2.该接口派生类型可通过全局枚举接口获取所有类型 + * a.接口路径:/operation/enum + * b.项目启动时会扫描所有实现类并记录:{@link EnumInfoInitializer} + * */ +public interface TransAbleEnum { + + String JsonCodeKey = "value"; + String JsonNameKey = "label"; + + @JsonValue + String getCode(); + + Serializable getValue(); + + String getName(); + + default Integer getIntCode() { + return Integer.parseInt(this.getCode()); + } + + /** 返回true则转换为Object结构,否则转换为code */ + default boolean transToObjectJson() { + return true; + } + + /** 返回false将在方法{@link com.example.carbon.common.base.enums.TransAbleEnum#findAllEnum}中被忽略 */ + default boolean asVoEnum(){ + return true; + } + + static E fromCode(Class clazz, Integer code){ + return fromCode(clazz, ObjectUtil.toStrOrNull(code)); + } + static E fromCode(Class clazz, String code){ + if (StringUtils.isEmpty(code)){ + return null; + } + E[] values = clazz.getEnumConstants(); + if (values != null){ + for (E value:values){ + if (value.getCode().equals(code)){ + return value; + } + } + } + throw BizException.data("枚举获取错误",clazz.getSimpleName() + ",Code:" + code); + } + + static E fromName(Class clazz, String name){ + if (StringUtils.isEmpty(name)){ + return null; + } + E[] values = clazz.getEnumConstants(); + for (E value:values){ + if (value.getName().equals(name)){ + return value; + } + } + throw BizException.data("枚举获取错误",clazz.getSimpleName() + ",Name:" + name); + } + + /** 通过SimpleName,获取对应枚举 */ + static List fromType(String enumType) { + // 扫描所有枚举子类 + Class clazz = (Class) EnumInfoInitializer.EnumTypeMap.get(enumType); + if (clazz == null) { + throw BizException.data("枚举未注册", enumType); + } + return CollectionUtil.arrayToList(clazz.getEnumConstants()); + } + + /** 获取所有子类型枚举 + * @param filterVo 是否过滤vo数据 {@link com.example.carbon.common.base.enums.TransAbleEnum#asVoEnum} + * */ + static Map> findAllEnum(boolean filterVo) { + Map> enumMap = new HashMap<>(); + for (String simpleName : EnumInfoInitializer.EnumTypeMap.keySet()){ + List enumList = TransAbleEnum.fromType(simpleName); + if (filterVo){ + enumList.removeIf(transEnum -> (transEnum == null || !transEnum.asVoEnum())); + } + enumMap.put(simpleName,enumList); + } + return enumMap; + } + + /** 获取目标枚举所有name */ + static List findAllCodes(Class clazz) { + List codeList = new ArrayList<>(); + E[] values = clazz.getEnumConstants(); + for (E value:values){ + codeList.add(value.getCode()); + } + return codeList; + } + + /** 获取目标枚举所有name */ + static List findAllNames(Class clazz) { + List nameList = new ArrayList<>(); + E[] values = clazz.getEnumConstants(); + for (E value:values){ + nameList.add(value.getName()); + } + return nameList; + } + + @Getter + @Setter + class Holder implements IntValueEnum { + + @JsonProperty(JsonCodeKey) + private String code; + + @JsonProperty(JsonNameKey) + private String name; + + public Holder() {} + + public Holder(String code, String name) { + this.code = code; + this.name = name; + } + } +} diff --git a/src/main/java/com/example/carbon/common/base/enums/config/EnumInfoInitializer.java b/src/main/java/com/example/carbon/common/base/enums/config/EnumInfoInitializer.java new file mode 100644 index 0000000..cec1dd4 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/enums/config/EnumInfoInitializer.java @@ -0,0 +1,53 @@ +package com.example.carbon.common.base.enums.config; + + +import com.example.carbon.common.base.enums.TransAbleEnum; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.utils.CollectionUtil; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.config.BeanDefinition; +import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider; +import org.springframework.core.type.filter.AssignableTypeFilter; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +public class EnumInfoInitializer { + + protected static final Logger Log = LoggerFactory.getLogger(EnumInfoInitializer.class); + + public static final Map> EnumTypeMap = new HashMap<>(); + + public static void initEnum(String packagePath) { + Log.info("TransAbleEnum_PackagePath: {}",packagePath); + if (CollectionUtil.haveLength(EnumTypeMap)){ + return; + } + ClassPathScanningCandidateComponentProvider provider = new ClassPathScanningCandidateComponentProvider(false); + provider.addIncludeFilter(new AssignableTypeFilter(TransAbleEnum.class)); + Set componentSet = provider.findCandidateComponents(packagePath); + for (BeanDefinition component : componentSet){ + String fullName = component.getBeanClassName(); + if (fullName == null){ + continue; + } + int splitIndex = fullName.lastIndexOf(".") + 1; + String simpleName = fullName.substring(splitIndex); + if (EnumTypeMap.containsKey(simpleName)){ + throw BizException.data("枚举注册名称冲突",simpleName); + } + Class clazz = null; + try { + clazz = Class.forName(fullName); + } catch (Exception e) { + throw BizException.data("枚举获取错误",simpleName); + } + if (clazz.isEnum()){ + Log.info("TransAbleEnum: {}",clazz.getName()); + EnumTypeMap.put(simpleName, (Class) clazz); + } + } + } +} diff --git a/src/main/java/com/example/carbon/common/base/enums/config/JacksonEnumSettings.java b/src/main/java/com/example/carbon/common/base/enums/config/JacksonEnumSettings.java new file mode 100644 index 0000000..0d9f1fd --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/enums/config/JacksonEnumSettings.java @@ -0,0 +1,180 @@ +package com.example.carbon.common.base.enums.config; + +import com.example.carbon.common.base.enums.TransAbleEnum; +import com.example.carbon.common.base.exception.BizException; +import com.fasterxml.jackson.annotation.JsonAlias; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.core.JsonGenerator; +import com.fasterxml.jackson.core.JsonParser; +import com.fasterxml.jackson.databind.DeserializationContext; +import com.fasterxml.jackson.databind.JsonDeserializer; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.BeanUtils; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.core.convert.converter.Converter; + +import java.io.IOException; +import java.io.Serializable; +import java.lang.reflect.Field; +import java.util.*; + +public class JacksonEnumSettings { + + public static Jackson2ObjectMapperBuilderCustomizer objDicBuilderCustomizer(){ + return mapperBuilder -> { + for (Class clazz: EnumInfoInitializer.EnumTypeMap.values()){ + mapperBuilder + .serializerByType(clazz,new JacksonEnumSettings.ObjDicSerializer()) + .deserializerByType(clazz,new JacksonEnumSettings.Deserializer()); + } + }; + } + + public static Jackson2ObjectMapperBuilderCustomizer valueDicBuilderCustomizer(){ + return mapperBuilder -> { + for (Class clazz: EnumInfoInitializer.EnumTypeMap.values()){ + mapperBuilder + .serializerByType(clazz,new JacksonEnumSettings.ValueDicSerializer()) + .deserializerByType(clazz,new JacksonEnumSettings.Deserializer()); + } + }; + } + + public static class Translator implements Converter { + + Class clazz; + + public Translator(Class clazz) { + this.clazz = clazz; + } + + @Override + public E convert(String value) { + return TransAbleEnum.fromCode(clazz,value); + } + } + + public static class ObjDicSerializer extends JsonSerializer { + @Override + public void serialize(TransAbleEnum data, JsonGenerator generator, SerializerProvider provider) throws IOException { + Serializable value = data.getValue(); + if (data.transToObjectJson()){ + generator.writeStartObject(); + if (value instanceof Integer){ + generator.writeNumberField(TransAbleEnum.JsonCodeKey,data.getIntCode()); + }else { + generator.writeStringField(TransAbleEnum.JsonCodeKey,data.getCode()); + } + generator.writeStringField(TransAbleEnum.JsonNameKey,data.getName()); + // 写出被标记的变量 --- @JsonProperty + this.writeAllField(generator,data); + generator.writeEndObject(); + }else { + if (value instanceof Integer){ + generator.writeNumber(data.getIntCode()); + }else { + generator.writeString(data.getCode()); + } + } + } + + private void writeAllField(JsonGenerator generator, TransAbleEnum value) throws IOException { + for (Field field:value.getClass().getDeclaredFields()){ + JsonProperty jsonProperty = field.getAnnotation(JsonProperty.class); + if (jsonProperty == null){ + continue; + } + try { + field.setAccessible(true); + Object fieldValue = field.get(value); + if(fieldValue != null){ + String fieldName = jsonProperty.value(); + if (StringUtils.isEmpty(fieldName)){ + fieldName = field.getName(); + } + generator.writeFieldName(fieldName); + if (fieldValue instanceof String){ + generator.writeString(fieldValue.toString()); + }else if (fieldValue instanceof Number){ + generator.writeNumber(fieldValue.toString()); + }else if (fieldValue instanceof Boolean){ + generator.writeBoolean((Boolean) fieldValue); + }else { + generator.writeObject(fieldValue); + } + } + } catch (IllegalAccessException e) { + throw new RuntimeException(e); + } + } + } + } + + public static class ValueDicSerializer extends JsonSerializer { + @Override + public void serialize(TransAbleEnum data, JsonGenerator generator, SerializerProvider provider) throws IOException { + Serializable value = data.getValue(); + if (value instanceof Integer){ + generator.writeNumber(data.getIntCode()); + }else { + generator.writeString(data.getCode()); + } + } + } + + public static class Deserializer extends JsonDeserializer { + + private final Map, List> ClassFieldMap = new HashMap<>(); + + @Override + public TransAbleEnum deserialize(JsonParser jsonParser, DeserializationContext context) throws IOException { + String value = jsonParser.getText(); + if ("{".equals(value)) { + TransAbleEnum.Holder holder = jsonParser.readValueAs(TransAbleEnum.Holder.class); + value = holder.getCode(); + } + String currentName = jsonParser.currentName(); + Object currentValue = jsonParser.getCurrentValue(); + Class clazz = BeanUtils.findPropertyType(currentName, currentValue.getClass()); + if (clazz.equals(Object.class)){ + clazz = this.tryFindFieldClass(currentName, currentValue.getClass()); + } + return TransAbleEnum.fromCode(clazz, value); + } + + private List getClassFieldList(Class clazz){ + List fieldList = ClassFieldMap.computeIfAbsent(clazz, key -> new ArrayList<>()); + this.findAllClassFieldList(fieldList,clazz); + return fieldList; + } + private void findAllClassFieldList(List fieldList,Class clazz){ + Field[] fields = clazz.getDeclaredFields(); + fieldList.addAll(Arrays.asList(fields)); + Class superClass = clazz.getSuperclass(); + if (Object.class.equals(superClass)){ + return; + } + this.findAllClassFieldList(fieldList,superClass); + } + private Class tryFindFieldClass(String name,Class clazz){ + for (Field field:this.getClassFieldList(clazz)){ + JsonProperty propertyAnno = field.getAnnotation(JsonProperty.class); + if (propertyAnno != null && + name.equals(propertyAnno.value())){ + return field.getType(); + } + JsonAlias aliasAnno = field.getAnnotation(JsonAlias.class); + if (aliasAnno != null){ + for (String str:aliasAnno.value()){ + if (name.equals(str)){ + return field.getType(); + } + } + } + } + throw BizException.custom("数据转换错误!","无法获取变量类型",clazz.getName() + "_" + name); + } + } +} diff --git a/src/main/java/com/example/carbon/common/base/exception/BaseException.java b/src/main/java/com/example/carbon/common/base/exception/BaseException.java new file mode 100644 index 0000000..2d3d334 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/exception/BaseException.java @@ -0,0 +1,51 @@ +package com.example.carbon.common.base.exception; + +import com.example.carbon.common.base.constant.ExpConstant; +import lombok.Getter; +import lombok.ToString; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +@Getter +@ToString +public abstract class BaseException extends RuntimeException { + + private static final Logger Logger = LoggerFactory.getLogger(BizException.class); + + protected final String code; + + /** 特殊状况下用于返回部分提示性数据 */ + protected Object data; + + protected BaseException(String code, String message) { + super(message); + if (code == null){ + this.code = ExpConstant.CODE_DEFAULT; + }else { + this.code = code; + } + } + + public final void print(){ + Logger.error("exception info print: start"); + Logger.error(this.getMessage(),this); + Logger.error("data:{}", data); + Logger.error("exception info print: end"); + } + + public final T addData(Object data){ + this.data = data; + return (T)this; + } + + public final D getData(){ + return (D)data; + } + + protected static String buildMsg(String msg,String hint){ + if (hint != null){ + msg = msg + ":" + hint; + } + return msg; + } +} diff --git a/src/main/java/com/example/carbon/common/base/exception/BizException.java b/src/main/java/com/example/carbon/common/base/exception/BizException.java new file mode 100644 index 0000000..7f062cd --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/exception/BizException.java @@ -0,0 +1,135 @@ +package com.example.carbon.common.base.exception; + +import com.example.carbon.common.base.constant.ExpConstant; +import org.apache.commons.lang3.StringUtils; + +public class BizException extends BaseException { + + private BizException(String msg) { + super(ExpConstant.CODE_BIZ, msg); + } + + /** tag!(msg:hint) */ + public static BizException custom(String tag, String msg, String hint){ + return BizException.custom(tag,BaseException.buildMsg(msg,hint)); + } + /** tag!(msg) */ + public static BizException custom(String tag, String msg){ + if (!StringUtils.isEmpty(msg)){ + tag += "!(" + msg + ")"; + } + return new BizException(tag); + } + /** msg! */ + public static BizException custom(String tag){ + return BizException.custom(tag,null); + } + + /** 登录失败!(msg:hint) */ + public static BizException userLogin(String msg, String hint){ + return BizException.userLogin(BaseException.buildMsg(msg,hint)); + } + /** 登录失败!(msg) */ + public static BizException userLogin(String msg){ + return BizException.custom("登录失败",msg); + } + + /** 数据新增失败!(msg:hint) */ + public static BizException fromInsert(String msg, String hint){ + return BizException.fromInsert(BaseException.buildMsg(msg,hint)); + } + /** 数据新增失败!(msg) */ + public static BizException fromInsert(String msg){ + return BizException.custom("数据新增失败",msg); + } + + /** 数据修改失败!(msg:hint) */ + public static BizException fromUpdate(String msg, String hint){ + return BizException.fromUpdate(BaseException.buildMsg(msg,hint)); + } + /** 数据修改失败!(msg) */ + public static BizException fromUpdate(String msg){ + return BizException.custom("数据修改失败",msg); + } + + /** 数据删除失败!(msg:hint) */ + public static BizException fromDelete(String msg, String hint){ + return BizException.fromDelete(BaseException.buildMsg(msg,hint)); + } + /** 数据删除失败!(msg) */ + public static BizException fromDelete(String msg){ + return BizException.custom("数据删除失败",msg); + } + + /** 数据查询失败!(msg:hint) */ + public static BizException fromSelect(String msg, String hint){ + return BizException.fromSelect(BaseException.buildMsg(msg,hint)); + } + /** 数据查询失败!(msg) */ + public static BizException fromSelect(String msg){ + return BizException.custom("数据查询失败",msg); + } + + /** 数据导入失败!(msg:hint) */ + public static BizException fromImport(String msg, String hint){ + return BizException.fromImport(BaseException.buildMsg(msg,hint)); + } + /** 数据导入失败!(msg) */ + public static BizException fromImport(String msg){ + return BizException.custom("数据导入失败",msg); + } + /** 数据导入失败! */ + public static BizException fromImport(){ + return BizException.custom("数据导入失败"); + } + + /** 数据导出失败!(msg:hint) */ + public static BizException fromExport(String msg, String hint){ + return BizException.fromExport(BaseException.buildMsg(msg,hint)); + } + /** 数据导出失败!(msg) */ + public static BizException fromExport(String msg){ + return BizException.custom("数据导出失败",msg); + } + + /** 文件下载失败!(msg:hint) */ + public static BizException fromDownload(String msg, String hint){ + return BizException.fromDownload(BaseException.buildMsg(msg,hint)); + } + /** 文件下载失败!(msg) */ + public static BizException fromDownload(String msg){ + return BizException.custom("文件下载失败",msg); + } + + /** 文件上传失败!(msg) */ + public static BizException fromUpload(String msg){ + return BizException.custom("文件上传失败",msg); + } + + /** 数据错误!(msg:hint) */ + public static BizException data(String msg, String hint){ + return BizException.data(BaseException.buildMsg(msg,hint)); + } + /** 数据错误!(msg) */ + public static BizException data(String msg){ + return BizException.custom("数据错误",msg); + } + + /** 逻辑错误!(msg:hint) */ + public static BizException logic(String msg, String hint){ + return BizException.logic(BaseException.buildMsg(msg,hint)); + } + /** 逻辑错误!(msg) */ + public static BizException logic(String msg){ + return BizException.custom("逻辑错误",msg); + } + + /** 参数错误!(msg:hint) */ + public static BizException param(String msg, String hint){ + return BizException.param(BaseException.buildMsg(msg,hint)); + } + /** 参数错误!(msg) */ + public static BizException param(String msg){ + return BizException.custom("参数错误",msg); + } +} diff --git a/src/main/java/com/example/carbon/common/base/exception/LoginStateException.java b/src/main/java/com/example/carbon/common/base/exception/LoginStateException.java new file mode 100644 index 0000000..efe29e7 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/exception/LoginStateException.java @@ -0,0 +1,16 @@ +package com.example.carbon.common.base.exception; + + +import com.example.carbon.common.base.constant.ExpConstant; + +public class LoginStateException extends BaseException { + + private LoginStateException(String msg) { + super(ExpConstant.CODE_LOGIN_STATE, msg); + } + + /** 登录过期 */ + public static LoginStateException expired() { + return new LoginStateException("登录已失效,请重新登录!"); + } +} diff --git a/src/main/java/com/example/carbon/common/base/func/ThrowableConsumer.java b/src/main/java/com/example/carbon/common/base/func/ThrowableConsumer.java new file mode 100644 index 0000000..0855805 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/func/ThrowableConsumer.java @@ -0,0 +1,10 @@ +package com.example.carbon.common.base.func; + +/** + * @author zx + * @since 2025-01-16 13:41 + */ +@FunctionalInterface +public interface ThrowableConsumer { + void accept(T data) throws Exception; +} diff --git a/src/main/java/com/example/carbon/common/base/types/TypeTransfer.java b/src/main/java/com/example/carbon/common/base/types/TypeTransfer.java new file mode 100644 index 0000000..6a2bd25 --- /dev/null +++ b/src/main/java/com/example/carbon/common/base/types/TypeTransfer.java @@ -0,0 +1,7 @@ +package com.example.carbon.common.base.types; + +public interface TypeTransfer { + default T asType(){ + return (T)this; + } +} diff --git a/src/main/java/com/example/carbon/common/config/JacksonEnumConvertConfig.java b/src/main/java/com/example/carbon/common/config/JacksonEnumConvertConfig.java new file mode 100644 index 0000000..266fb1e --- /dev/null +++ b/src/main/java/com/example/carbon/common/config/JacksonEnumConvertConfig.java @@ -0,0 +1,36 @@ +package com.example.carbon.common.config; + + +import com.example.carbon.common.base.enums.TransAbleEnum; +import com.example.carbon.common.base.enums.config.EnumInfoInitializer; +import com.example.carbon.common.base.enums.config.JacksonEnumSettings; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.convert.converter.Converter; +import org.springframework.core.convert.converter.ConverterFactory; +import org.springframework.format.FormatterRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + + +@Configuration +public class JacksonEnumConvertConfig implements WebMvcConfigurer { + + /** 路径参数枚举映射 */ + @Override + public void addFormatters(FormatterRegistry registry) { + registry.addConverterFactory(new ConverterFactory(){ + @Override + public Converter getConverter(Class clazz) { + return new JacksonEnumSettings.Translator<>(clazz); + } + }); + } + + @Bean + public Jackson2ObjectMapperBuilderCustomizer enumCustomizer(@Value("${mybatis-plus.type-enums-package}") String packagePath){ + EnumInfoInitializer.initEnum(packagePath); + return JacksonEnumSettings.objDicBuilderCustomizer(); + } +} diff --git a/src/main/java/com/example/carbon/common/config/MinioConfig.java b/src/main/java/com/example/carbon/common/config/MinioConfig.java new file mode 100644 index 0000000..aa6d4a8 --- /dev/null +++ b/src/main/java/com/example/carbon/common/config/MinioConfig.java @@ -0,0 +1,33 @@ +package com.example.carbon.common.config; + +import io.minio.MinioClient; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MinioConfig { + + @Value("${minio.domain-url}") + public String domainUrl; + + @Value("${minio.bucket-name}") + public String bucketName; + + @Value("${minio.endpoint}") + private String endpoint; + + @Value("${minio.access-key}") + private String accessKey; + + @Value("${minio.secret-key}") + private String secretKey; + + @Bean + public MinioClient minioClient() { + return MinioClient.builder() + .endpoint(endpoint) + .credentials(accessKey, secretKey) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/common/config/MyBatisPlusConfig.java b/src/main/java/com/example/carbon/common/config/MyBatisPlusConfig.java new file mode 100644 index 0000000..2e6f8c6 --- /dev/null +++ b/src/main/java/com/example/carbon/common/config/MyBatisPlusConfig.java @@ -0,0 +1,24 @@ +package com.example.carbon.common.config; + +import com.baomidou.mybatisplus.annotation.DbType; +import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; +import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +/** + * MybatisPlus配置类 + */ +@Configuration +public class MyBatisPlusConfig { + + /** + * 分页插件 + */ + @Bean + public MybatisPlusInterceptor paginationInterceptor() { + MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); + interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); + return interceptor; + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/common/config/RequestConfig.java b/src/main/java/com/example/carbon/common/config/RequestConfig.java new file mode 100644 index 0000000..9faea73 --- /dev/null +++ b/src/main/java/com/example/carbon/common/config/RequestConfig.java @@ -0,0 +1,26 @@ +package com.example.carbon.common.config; + +import com.example.carbon.common.aop.PermissionInterceptor; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import java.util.Arrays; + +@Configuration +public class RequestConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new PermissionInterceptor()) + .addPathPatterns("/api/**") + .excludePathPatterns(); + } + + @Override + public void addResourceHandlers(ResourceHandlerRegistry registry) { + registry.addResourceHandler("swagger-ui.html") + .addResourceLocations("classpath:/META-INF/resources/"); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/common/enums/DeleteStatus.java b/src/main/java/com/example/carbon/common/enums/DeleteStatus.java new file mode 100644 index 0000000..d2885b5 --- /dev/null +++ b/src/main/java/com/example/carbon/common/enums/DeleteStatus.java @@ -0,0 +1,31 @@ +package com.example.carbon.common.enums; + +import com.example.carbon.common.base.enums.IntValueEnum; +import lombok.Getter; + +@Getter +public enum DeleteStatus implements IntValueEnum { + NO("0","未删除"), + YES("1","已删除"); + + public final String code; + public final String name; + + DeleteStatus(String code, String name) { + this.code = code; + this.name = name; + } + + @Override + public boolean transToObjectJson() { + return false; + } + + public final boolean yes() { + return this == YES; + } + + public final boolean no() { + return this == NO; + } +} diff --git a/src/main/java/com/example/carbon/common/enums/booking/BookingItem.java b/src/main/java/com/example/carbon/common/enums/booking/BookingItem.java new file mode 100644 index 0000000..abf2471 --- /dev/null +++ b/src/main/java/com/example/carbon/common/enums/booking/BookingItem.java @@ -0,0 +1,36 @@ +package com.example.carbon.common.enums.booking; + +import com.example.carbon.common.base.enums.IntValueEnum; +import lombok.Getter; + +/** + * @author :sjs + * @since :2025-09-15 14:33:00 + */ + +@Getter +public enum BookingItem implements IntValueEnum { + SMART_ENERGY_CARBON("1", "智慧能碳平台"), + AI_ENERGY_SAVING("2", "AI+智能节能技术"), + CITY_CARBON_PLATFORM("3", "城市级能碳平台"), + FMCS_INDUSTRIAL("4", "FMCS智能工业平台"), + LIFECYCLE_CARBON_FOOTPRINT("5", "全生命周期产品碳足迹平台"), + ESG_CERTIFICATION_CONSULTING("6", "ESG绿色认证咨询"); + + public final String code; + public final String name; + + BookingItem(String code, String name) { + this.code = code; + this.name = name; + } + + public static BookingItem fromCode(String code) { + for (BookingItem item : values()) { + if (item.code.equals(code)) { + return item; + } + } + throw new IllegalArgumentException("未知的预订服务代码: " + code); + } +} diff --git a/src/main/java/com/example/carbon/common/enums/booking/BookingStatus.java b/src/main/java/com/example/carbon/common/enums/booking/BookingStatus.java new file mode 100644 index 0000000..28619fb --- /dev/null +++ b/src/main/java/com/example/carbon/common/enums/booking/BookingStatus.java @@ -0,0 +1,38 @@ +package com.example.carbon.common.enums.booking; + +import com.example.carbon.common.base.enums.IntValueEnum; +import lombok.Getter; + +/** + * @author :sjs + * @since :2025-09-15 14:42:00 + */ +@Getter +public enum BookingStatus implements IntValueEnum { + + NEW_APPLICATION("1", "新申请"), + TO_BE_CONTACTED("2", "待联系"), + CONTACTED("3", "已联系"), + UNQUALIFIED("4", "不合格"); + + public final String code; + public final String name; + + BookingStatus(String code, String name) { + this.code = code; + this.name = name; + } + + /** + * 根据编码获取枚举值 + */ + public static BookingStatus fromCode(String code) { + for (BookingStatus status : values()) { + if (status.getCode().equals(code)) { + return status; + } + } + throw new IllegalArgumentException("未知的状态编码: " + code); + } +} + diff --git a/src/main/java/com/example/carbon/common/enums/jrn/PublishStatus.java b/src/main/java/com/example/carbon/common/enums/jrn/PublishStatus.java new file mode 100644 index 0000000..6bb45cc --- /dev/null +++ b/src/main/java/com/example/carbon/common/enums/jrn/PublishStatus.java @@ -0,0 +1,34 @@ +package com.example.carbon.common.enums.jrn; + +import com.example.carbon.common.base.enums.IntValueEnum; +import lombok.Getter; + +/** + * @author :sjs + * @since :2025-09-10 10:37:00 + */ +@Getter +public enum PublishStatus implements IntValueEnum { + DRAFT("0", "草稿"), + PUBLISHED("1", "已发布"), + WITHDRAWN("2", "已撤回"), + REEDIT("3", "重新编辑"); + + public final String code; + public final String name; + + PublishStatus(String code, String name) { + this.code = code; + this.name = name; + } + + // 通过 code 查找枚举 + public static PublishStatus fromCode(String code) { + for (PublishStatus status : values()) { + if (status.code.equals(code)) { + return status; + } + } + throw new IllegalArgumentException("未知的发布状态码: " + code); + } +} diff --git a/src/main/java/com/example/carbon/common/model/dto/PageAbleDto.java b/src/main/java/com/example/carbon/common/model/dto/PageAbleDto.java new file mode 100644 index 0000000..57eea69 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/dto/PageAbleDto.java @@ -0,0 +1,20 @@ +package com.example.carbon.common.model.dto; + +import com.example.carbon.common.model.params.PageParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author zx + * @since 2025-01-17 12:28 + */ +@Getter +@Setter +@ToString +public class PageAbleDto { + + @Schema(description = "分页信息",requiredMode = Schema.RequiredMode.REQUIRED) + protected PageParam page; +} diff --git a/src/main/java/com/example/carbon/common/model/dto/booking/BookingExperiencePageSelectDto.java b/src/main/java/com/example/carbon/common/model/dto/booking/BookingExperiencePageSelectDto.java new file mode 100644 index 0000000..8a2cfc6 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/dto/booking/BookingExperiencePageSelectDto.java @@ -0,0 +1,22 @@ +package com.example.carbon.common.model.dto.booking; + +import com.example.carbon.common.model.dto.PageAbleDto; +import com.example.carbon.common.model.params.booking.BookingExperienceListSelectParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author :sjs + * @since :2025-09-16 08:57:00 + */ +@Getter +@Setter +@ToString(callSuper=true) +@Accessors(chain=true) +public class BookingExperiencePageSelectDto extends PageAbleDto { + @Schema(description = "查询参数") + private BookingExperienceListSelectParam selectParam; +} diff --git a/src/main/java/com/example/carbon/common/model/dto/jrn/JournalismPageSelectDto.java b/src/main/java/com/example/carbon/common/model/dto/jrn/JournalismPageSelectDto.java new file mode 100644 index 0000000..5aa1787 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/dto/jrn/JournalismPageSelectDto.java @@ -0,0 +1,22 @@ +package com.example.carbon.common.model.dto.jrn; + +import com.example.carbon.common.model.dto.PageAbleDto; +import com.example.carbon.common.model.params.jrn.JournalismListSelectParam; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author :sjs + * @since :2025-09-10 11:38:00 + */ +@Getter +@Setter +@ToString(callSuper=true) +@Accessors(chain=true) +public class JournalismPageSelectDto extends PageAbleDto { + @Schema(description = "查询参数") + private JournalismListSelectParam selectParam; +} diff --git a/src/main/java/com/example/carbon/common/model/dto/perm/LoginDto.java b/src/main/java/com/example/carbon/common/model/dto/perm/LoginDto.java new file mode 100644 index 0000000..3d7dd62 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/dto/perm/LoginDto.java @@ -0,0 +1,22 @@ +package com.example.carbon.common.model.dto.perm; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author :sjs + * @since :2025-09-11 09:48:00 + */ +@Getter +@Setter +@ToString +public class LoginDto { + + @Schema(description = "用户编号/账号/手机号/邮箱") + private String data; + + @Schema(description = "密码",requiredMode = Schema.RequiredMode.REQUIRED) + private String pwd; +} diff --git a/src/main/java/com/example/carbon/common/model/entity/booking/HxBookingExperience.java b/src/main/java/com/example/carbon/common/model/entity/booking/HxBookingExperience.java new file mode 100644 index 0000000..bc2f3c1 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/entity/booking/HxBookingExperience.java @@ -0,0 +1,74 @@ +package com.example.carbon.common.model.entity.booking; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.example.carbon.common.base.entity.BaseHxEntity; +import com.example.carbon.common.enums.booking.BookingItem; +import com.example.carbon.common.enums.booking.BookingStatus; +import com.example.carbon.common.utils.CodeUtil; +import com.example.carbon.common.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; +import org.apache.commons.lang3.StringUtils; + +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.stream.Collectors; + +/** + * @author :sjs + * @since :2025-09-15 14:03:00 + */ +@Getter +@Setter +@ToString(callSuper = true) +@Accessors(chain = true) +@TableName("hx_booking_experience") +public class HxBookingExperience extends BaseHxEntity.SimpleData { + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED) + private String code; + + @Schema(description = "产品服务(“,”分隔)", requiredMode = Schema.RequiredMode.REQUIRED) + private String bookingItems; + + @Schema(description = "姓名", requiredMode = Schema.RequiredMode.REQUIRED) + private String name; + + @Schema(description = "单位", requiredMode = Schema.RequiredMode.REQUIRED) + private String unit; + + @Schema(description = "电话", requiredMode = Schema.RequiredMode.REQUIRED) + private String phone; + + @Schema(description = "邮箱", requiredMode = Schema.RequiredMode.REQUIRED) + private String mail; + + @Schema(description = "留言") + private String message; + + @Schema(description = "预约状态", requiredMode = Schema.RequiredMode.REQUIRED) + private BookingStatus bookingStatus; + + public final HxBookingExperience generateCode() { + return this.setCode(CodeUtil.generateUniqueCode(12)); + } + + public String getApplicationTimeStr() { + return DateUtil.formatDate(createTime, "yyyy-MM-dd HH:mm"); + } + + public List getBookingItemList() { + if (StringUtils.isBlank(bookingItems)) { + return Collections.emptyList(); + } + + return Arrays.stream(bookingItems.split(",")) + .map(String::trim) + .filter(code -> !code.isEmpty()) + .map(BookingItem::fromCode) + .collect(Collectors.toList()); + } +} diff --git a/src/main/java/com/example/carbon/common/model/entity/jrn/HxJournalism.java b/src/main/java/com/example/carbon/common/model/entity/jrn/HxJournalism.java new file mode 100644 index 0000000..bbb2ada --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/entity/jrn/HxJournalism.java @@ -0,0 +1,62 @@ +package com.example.carbon.common.model.entity.jrn; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.example.carbon.common.base.entity.BaseHxEntity; +import com.example.carbon.common.enums.jrn.PublishStatus; +import com.example.carbon.common.utils.CodeUtil; +import com.example.carbon.common.utils.DateUtil; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.util.Date; + +/** + * @author :sjs + * @since :2025-09-10 10:28:00 + */ +@Getter +@Setter +@ToString(callSuper = true) +@Accessors(chain = true) +@TableName("hx_journalism") +public class HxJournalism extends BaseHxEntity.SimpleData { + + @Schema(description = "编码", requiredMode = Schema.RequiredMode.REQUIRED) + private String code; + + @Schema(description = "标题", requiredMode = Schema.RequiredMode.REQUIRED) + private String title; + + @Schema(description = "内容", requiredMode = Schema.RequiredMode.REQUIRED) + private String content; + + @Schema(description = "作者") + private String author; + + @Schema(description = "编辑") + private String editor; + + @Schema(description = "封面图片Id") + private Long attachId; + + @Schema(description = "发布时间") + private Date publishTime; + + @Schema(description = "发布状态(0草稿,1已发布,2已撤回,3重新编辑)") + private PublishStatus publishStatus; + + public final HxJournalism generateCode() { + return this.setCode(CodeUtil.generateUniqueCode(12)); + } + + public String getPublishTimeStr() { + return DateUtil.formatDate(publishTime, "yyyy-MM-dd HH:mm"); + } + + public String getCreateTimeStr() { + return DateUtil.formatDate(createTime, "yyyy-MM-dd HH:mm"); + } +} diff --git a/src/main/java/com/example/carbon/common/model/entity/sys/HxSysAttach.java b/src/main/java/com/example/carbon/common/model/entity/sys/HxSysAttach.java new file mode 100644 index 0000000..4188626 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/entity/sys/HxSysAttach.java @@ -0,0 +1,43 @@ +package com.example.carbon.common.model.entity.sys; + +import com.baomidou.mybatisplus.annotation.TableName; +import com.example.carbon.common.base.entity.BaseHxEntity; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +@Getter +@Setter +@ToString(callSuper = true) +@Accessors(chain = true) +@TableName("hx_sys_attach") +public class HxSysAttach extends BaseHxEntity.Simple { + + private static final long serialVersionUID = 1L; + + @Schema(description = "组织编码") + private String orgCode; + + @Schema(description = "桶名称") + private String bucketName; + + @Schema(description = "链接") + private String link; + + @Schema(description = "域名") + private String domainUrl; + + @Schema(description = "定义名称") + private String name; + + @Schema(description = "原始名称") + private String originalName; + + @Schema(description = "扩展名") + private String extension; + + @Schema(description = "文件大小") + private Long attachSize; +} diff --git a/src/main/java/com/example/carbon/common/model/info/FileInfo.java b/src/main/java/com/example/carbon/common/model/info/FileInfo.java new file mode 100644 index 0000000..dc80b11 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/info/FileInfo.java @@ -0,0 +1,33 @@ +package com.example.carbon.common.model.info; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author zx + * @since 2025-01-24 11:41 + */ +@Getter +@Setter +@ToString(callSuper=true) +@Accessors(chain = true) +public class FileInfo { + + @Schema(description = "文件路径") + private String path; + + @Schema(description = "文件全名") + private String fullName; + + @Schema(description = "文件名") + private String name; + + @Schema(description = "文件后缀名") + private String suffix; + + @Schema(description = "文件大小(Mb)") + private String size; +} diff --git a/src/main/java/com/example/carbon/common/model/info/perm/UserLoginInfo.java b/src/main/java/com/example/carbon/common/model/info/perm/UserLoginInfo.java new file mode 100644 index 0000000..d15642e --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/info/perm/UserLoginInfo.java @@ -0,0 +1,27 @@ +package com.example.carbon.common.model.info.perm; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +/** + * @author :sjs + * @since :2025-09-11 09:49:42 + */ +@Getter +@Setter +@ToString(callSuper = true) +@Accessors(chain = true) +public class UserLoginInfo { + + @Schema(description = "账号") + private String account; + + @Schema(description = "用户名") + private String userName; + + @Schema(description = "token") + private String token; +} diff --git a/src/main/java/com/example/carbon/common/model/params/PageParam.java b/src/main/java/com/example/carbon/common/model/params/PageParam.java new file mode 100644 index 0000000..da2531a --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/params/PageParam.java @@ -0,0 +1,28 @@ +package com.example.carbon.common.model.params; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; + +/** + * @author zx + * @since 2025-01-17 12:23 + */ +@Setter +@Getter +@ToString +public class PageParam { + + @Schema(description = "页码",requiredMode = Schema.RequiredMode.REQUIRED) + protected int pageNum = 1; + + @Schema(description = "数据量",requiredMode = Schema.RequiredMode.REQUIRED) + protected int pageSize = 10; + + public final IPage toPage(){ + return Page.of(pageNum,pageSize); + } +} diff --git a/src/main/java/com/example/carbon/common/model/params/booking/BookingExperienceListSelectParam.java b/src/main/java/com/example/carbon/common/model/params/booking/BookingExperienceListSelectParam.java new file mode 100644 index 0000000..6ddc2b0 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/params/booking/BookingExperienceListSelectParam.java @@ -0,0 +1,30 @@ +package com.example.carbon.common.model.params.booking; + +import com.example.carbon.common.model.entity.booking.HxBookingExperience; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.time.LocalDate; + +/** + * @author :sjs + * @since :2025-09-16 08:32:00 + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class BookingExperienceListSelectParam extends HxBookingExperience { + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "申请开始时间") + private LocalDate applicationStartTime; + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "申请结束时间") + private LocalDate applicationEndTime; +} diff --git a/src/main/java/com/example/carbon/common/model/params/jrn/JournalismListSelectParam.java b/src/main/java/com/example/carbon/common/model/params/jrn/JournalismListSelectParam.java new file mode 100644 index 0000000..e25dfde --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/params/jrn/JournalismListSelectParam.java @@ -0,0 +1,38 @@ +package com.example.carbon.common.model.params.jrn; + +import com.example.carbon.common.model.entity.jrn.HxJournalism; +import com.fasterxml.jackson.annotation.JsonFormat; +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Getter; +import lombok.Setter; +import lombok.ToString; +import lombok.experimental.Accessors; + +import java.time.LocalDate; + +/** + * @author :sjs + * @since :2025-09-10 11:21:00 + */ +@Getter +@Setter +@ToString +@Accessors(chain = true) +public class JournalismListSelectParam extends HxJournalism { + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "发布开始时间") + private LocalDate publishStartTime; + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "发布结束时间") + private LocalDate publishEndTime; + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "创建开始时间") + private LocalDate createStartTime; + + @JsonFormat(pattern = "yyyy-MM-dd") + @Schema(description = "创建结束时间") + private LocalDate createEndTime; +} diff --git a/src/main/java/com/example/carbon/common/model/vo/ResultVo.java b/src/main/java/com/example/carbon/common/model/vo/ResultVo.java new file mode 100644 index 0000000..34ca249 --- /dev/null +++ b/src/main/java/com/example/carbon/common/model/vo/ResultVo.java @@ -0,0 +1,81 @@ +package com.example.carbon.common.model.vo; + + +import com.example.carbon.common.base.constant.SysConstant; +import com.example.carbon.common.base.exception.BaseException; +import lombok.Getter; +import lombok.Setter; + +import java.io.Serializable; + +@Getter +public class ResultVo implements Serializable { + + final String code; + + final String msg; + + final T data; + + private ResultVo(String code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + private ResultVo(BaseException exception) { + this.code = exception.getCode(); + this.msg = exception.getMessage(); + this.data = exception.getData(); + } + + public final boolean isSuccess() { + return SysConstant.REQUEST_SUCCESS_CODE.equals(this.code); + } + + public static ResultVo ok() { + return ResultVo.ok(null); + } + + public static ResultVo ok(D data) { + return new ResultVo<>(SysConstant.REQUEST_SUCCESS_CODE,"操作成功!",data); + } + + public static ResultVo ok(String msg, D data) { + return new ResultVo<>(SysConstant.REQUEST_SUCCESS_CODE,msg,data); + } + + public static ResultVo fail(String msg) { + return ResultVo.fail(msg,null); + } + + public static ResultVo fail(String msg, D data) { + return new ResultVo<>(SysConstant.REQUEST_CODE_FAIL,msg,data); + } + + public static ResultVo fail(BaseException exception) { + return new ResultVo<>(exception); + } + + public static ResultVo status(boolean flag) { + return flag ? ResultVo.ok() : ResultVo.fail("操作失败!"); + } + + public static ResultVo status(boolean flag, String successMsg, String failMsg) { + return flag ? ResultVo.ok(successMsg) : ResultVo.fail(failMsg); + } + + @Getter + @Setter + public static class Builder{ + String code; + + String msg; + + T data; + + public final ResultVo build(){ + return new ResultVo<>(code,msg,data); + } + } +} diff --git a/src/main/java/com/example/carbon/common/service/EmailService.java b/src/main/java/com/example/carbon/common/service/EmailService.java new file mode 100644 index 0000000..713d629 --- /dev/null +++ b/src/main/java/com/example/carbon/common/service/EmailService.java @@ -0,0 +1,183 @@ +package com.example.carbon.common.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.FileSystemResource; +import org.springframework.mail.javamail.JavaMailSender; +import org.springframework.mail.javamail.JavaMailSenderImpl; +import org.springframework.mail.javamail.MimeMessageHelper; +import org.springframework.stereotype.Service; +import org.springframework.util.StringUtils; + +import javax.annotation.PostConstruct; +import javax.mail.Message; +import javax.mail.MessagingException; +import javax.mail.internet.InternetAddress; +import javax.mail.internet.MimeMessage; +import java.io.File; +import java.util.List; +import java.util.Properties; + +/** + * @author :sjs + * @since :2025-09-16 13:55:00 + */ +@Service +public class EmailService { + // 使用@Value注解从配置文件中注入邮件服务器主机地址 + @Value("${mail.host}") + private String host; + + // 从配置文件中注入邮件服务器端口号 + @Value("${mail.port}") + private int port; + + // 从配置文件中注入发件人邮箱用户名 + @Value("${mail.username}") + private String username; + + // 从配置文件中注入发件人邮箱密码或授权码 + @Value("${mail.password}") + private String password; + + // 从配置文件中注入邮件协议(通常为smtp) + @Value("${mail.protocol}") + private String protocol; + + // 从配置文件中注入是否需要进行身份验证 + @Value("${mail.properties.mail.smtp.auth}") + private boolean auth; + + // 从配置文件中注入是否启用STARTTLS加密 + @Value("${mail.properties.mail.smtp.starttls.enable}") + private boolean starttlsEnable; + + // 从配置文件中注入是否启用SSL加密 + @Value("${mail.properties.mail.smtp.ssl.enable}") + private boolean sslEnable; + + // 从配置文件中注入是否启用调试模式 + @Value("${mail.debug}") + private boolean debug; + + // Spring提供的邮件发送器实例 + private JavaMailSender mailSender; + + /** + * 初始化方法,在Bean创建后自动执行 + * 根据配置文件中的参数初始化邮件发送器 + */ + @PostConstruct // 此注解表示方法在依赖注入完成后自动执行 + public void init() { + JavaMailSenderImpl mailSenderImpl = new JavaMailSenderImpl(); + mailSenderImpl.setHost(host); + mailSenderImpl.setPort(port); + mailSenderImpl.setUsername(username); + mailSenderImpl.setPassword(password); + mailSenderImpl.setProtocol(protocol); + + // 获取JavaMail属性配置对象,用于设置更详细的邮件服务器参数 + Properties props = mailSenderImpl.getJavaMailProperties(); + props.put("mail.smtp.auth", auth); + props.put("mail.smtp.starttls.enable", starttlsEnable); + props.put("mail.smtp.ssl.enable", sslEnable); + props.put("mail.debug", debug); + + // 设置连接超时时间(单位:毫秒) + props.put("mail.smtp.connectiontimeout", 5000); + // 设置读取超时时间(单位:毫秒) + props.put("mail.smtp.timeout", 5000); + // 设置写入超时时间(单位:毫秒) + props.put("mail.smtp.writetimeout", 5000); + + // 将配置好的邮件发送器实例赋值给成员变量 + mailSender = mailSenderImpl; + } + + /** + * 发送邮件的主要方法 + * 支持发送文本或HTML格式的邮件,并可添加附件和抄送人 + * + * @param from 发件人邮箱地址 + * @param to 收件人邮箱地址数组 + * @param cc 抄送人邮箱地址数组(可选) + * @param subject 邮件主题 + * @param content 邮件内容 + * @param isHtml 邮件内容是否为HTML格式 + * @param attachments 附件文件列表(可选) + * @throws MessagingException 当邮件发送失败时抛出异常 + */ + public void sendEmail(String from, String[] to, String[] cc, String subject, + String content, boolean isHtml, List attachments) + throws MessagingException { + + // 创建MIME类型邮件消息(支持HTML和附件) + MimeMessage message = mailSender.createMimeMessage(); + // 创建MimeMessageHelper辅助类,true表示支持多部分消息(如附件) + MimeMessageHelper helper = new MimeMessageHelper(message, true, "UTF-8"); + + // 设置发件人邮箱地址 + helper.setFrom(from); + // 设置收件人邮箱地址 + helper.setTo(to); + + // 如果提供了抄送人地址,则设置抄送人 + if (cc != null && cc.length > 0) { + helper.setCc(cc); + } + + // 设置邮件主题 + helper.setSubject(subject); + // 设置邮件内容,第二个参数指定内容是否为HTML格式 + helper.setText(content, isHtml); + + // 如果提供了附件,则添加附件到邮件中 + if (attachments != null && !attachments.isEmpty()) { + for (File file : attachments) { + // 检查文件是否存在 + if (file.exists()) { + // 添加附件,使用文件名作为附件的名称 + helper.addAttachment(file.getName(), file); + } + } + } + + // 发送邮件 + mailSender.send(message); + } + + /** + * 发送简单文本邮件的简化方法 + * + * @param to 收件人邮箱地址 + * @param subject 邮件主题 + * @param content 邮件文本内容 + * @throws MessagingException 当邮件发送失败时抛出异常 + */ + public void sendEmail(String[] to, String subject, String content) throws MessagingException { + // 调用主发送方法,使用配置中的用户名作为发件人,不包含抄送人和附件,内容为纯文本 + sendEmail(username, to, null, subject, content, false, null); + } + + /** + * 发送HTML格式邮件的简化方法 + * + * @param to 收件人邮箱地址 + * @param subject 邮件主题 + * @param htmlContent HTML格式的邮件内容 + * @throws MessagingException 当邮件发送失败时抛出异常 + */ + public void sendHtmlEmail(String[] to, String subject, String htmlContent) throws MessagingException { + // 调用主发送方法,使用配置中的用户名作为发件人,不包含抄送人和附件,内容为HTML格式 + sendEmail(username, to, null, subject, htmlContent, true, null); + } + + /** + * 获取底层的邮件发送器实例 + * 可用于进行更高级的自定义操作 + * + * @return JavaMailSender实例 + */ + public JavaMailSender getMailSender() { + return mailSender; + } +} diff --git a/src/main/java/com/example/carbon/common/utils/AttachUtil.java b/src/main/java/com/example/carbon/common/utils/AttachUtil.java new file mode 100644 index 0000000..880ac5e --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/AttachUtil.java @@ -0,0 +1,48 @@ +package com.example.carbon.common.utils; + +import org.apache.commons.lang3.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +public class AttachUtil { + private static final Logger Log = LoggerFactory.getLogger(AttachUtil.class); + + + /** + * 路径拼接 + * @param params + * @return + */ + public static String joinPath(String... params) { + StringBuilder path = new StringBuilder(); + boolean isFirst = true; + for (String param : params) { + if (param == null || param.isEmpty()) { + continue; + } + // 去除首尾的 / + if (param.startsWith("/")) { + param = param.substring(1); + } + if (param.endsWith("/")) { + param = param.substring(0, param.length() - 1); + } + // 如果不是第一个参数且不以 http:// 开头,则添加 / + if (!isFirst && !param.startsWith("http://")) { + path.append("/"); + } + path.append(param); + isFirst = false; + } + return path.toString(); + } + + /** + * 获取文件后缀 + * @param fileName + * @return + */ + public static String getFileSuffix(String fileName) { + return StringUtils.substringAfterLast(fileName, "."); + } +} diff --git a/src/main/java/com/example/carbon/common/utils/CodeUtil.java b/src/main/java/com/example/carbon/common/utils/CodeUtil.java new file mode 100644 index 0000000..f46fa2f --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/CodeUtil.java @@ -0,0 +1,89 @@ +package com.example.carbon.common.utils; + + +import com.example.carbon.common.base.exception.BizException; + +import java.util.UUID; + +public class CodeUtil { + + public static char[] BaseChars = new char[]{'a', 'b', 'c', 'd', 'e', 'f', + 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', + 't', 'u', 'v', 'w', 'x', 'y', 'z', '0', '1', '2', '3', '4', '5', + '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', + 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', + 'W', 'X', 'Y', 'Z'}; + + /** + * 生成28位UUID(去除“-”) + */ + public static String generateUUID() { + String uuid = UUID.randomUUID().toString(); + return uuid.replaceAll("-", ""); + } + + /** + * 生成8位唯一标识 + */ + public static String generateUniqueCode() { + String uuid = UUID.randomUUID().toString(); + return hexToBase62(uuid.toCharArray()); + } + + /** + * 将位16进制编码转为一位62进制编码 + */ + public static String hexToBase62(char[] array) { + final int HexNum = 4; + StringBuilder builder = new StringBuilder(); + StringBuilder strBuilder = new StringBuilder(); + for (char c : array) { + if (c != '-') { + strBuilder.append(c); + } + if (strBuilder.length() == HexNum) { + // 将n位str转化为int 16进制下的表示 + long x = Long.parseLong(strBuilder.toString(), 16); + // 用该16进制数取模62(十六进制表示为314(14即E)),结果作为索引取出字符 + int index = (int) (x % 0x3E); + builder.append(BaseChars[index]); + // 清空 + strBuilder.delete(0, HexNum); + } + } + return builder.toString(); + } + + + /** + * 生成指定位数的唯一标识 + * @param length 要生成的唯一标识的位数 + * @return 生成的唯一标识字符串 + */ + public static String generateUniqueCode(int length) { + if (length < 8) { + throw BizException.data("不支持8位以下的code"); + } + StringBuilder builder = new StringBuilder(); + String uuid = UUID.randomUUID().toString().replace("-", ""); + int uuidLength = uuid.length(); + int groupSize = uuidLength / length; + for (int i = 0; i < length; i++) { + int startIndex = i * groupSize; + int endIndex = Math.min(startIndex + groupSize, uuidLength); + String str = uuid.substring(startIndex, endIndex); + int x = Integer.parseInt(str, 16); + builder.append(BaseChars[x % 0x3E]); + } + return builder.toString(); + } + + /** + * 生成指定前缀+尾数位数的唯一标识 + * @param suffixLength 后缀长度 + * @return 生成的唯一标识字符串 + */ + public static String generateUniqueCode(String prefix, int suffixLength) { + return prefix + generateUniqueCode(suffixLength); + } +} diff --git a/src/main/java/com/example/carbon/common/utils/CollectionUtil.java b/src/main/java/com/example/carbon/common/utils/CollectionUtil.java new file mode 100644 index 0000000..2722495 --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/CollectionUtil.java @@ -0,0 +1,183 @@ +package com.example.carbon.common.utils; + +import org.springframework.beans.BeanUtils; + +import java.math.BigDecimal; +import java.util.*; +import java.util.function.Consumer; +import java.util.function.Function; +import java.util.function.ObjIntConsumer; +import java.util.function.Supplier; + +public class CollectionUtil { + + public static List copyList(List dataArray, Supplier creator) { + List resultList = new ArrayList<>(dataArray); + for (D data : dataArray) { + D newData = creator.get(); + resultList.add(newData); + BeanUtils.copyProperties(data,newData); + } + return resultList; + } + + public static void loop(D[] dataArray, ObjIntConsumer onLoop) { + for (int i = 0, s = dataArray.length; i < s; i++) { + onLoop.accept(dataArray[i], i); + } + } + + public static void loop(List dataList, ObjIntConsumer onLoop) { + for (int i = 0, s = dataList.size(); i < s; i++) { + onLoop.accept(dataList.get(i), i); + } + } + + public static List listCollection(Collection collection, Function transformer) { + List result = new ArrayList<>(); + for (D data : collection) { + R newData = transformer.apply(data); + result.add(newData); + } + return result; + } + + public static List listValue(Collection collection, Function getter) { + if (collection == null) { + return null; + } + List result = new ArrayList(); + for (D data : collection) { + V value = getter.apply(data); + result.add(value); + } + return result; + } + + public static BigDecimal sumValue(Collection collection, Function getter) { + BigDecimal decimal = new BigDecimal("0"); + for (T data : collection) { + BigDecimal value = getter.apply(data); + decimal = decimal.add(value); + } + return decimal; + } + + public static Set setCollection(Set set, Collection collection, Function keyGetter) { + for (T data : collection) { + K key = keyGetter.apply(data); + set.add(key); + } + return set; + } + + public static Set setCollection(Collection collection, Function keyGetter) { + return setCollection(new HashSet<>(), collection, keyGetter); + } + + public static Map mapCollection(Map map, Collection collection, Function keyGetter) { + for (T data : collection) { + K key = keyGetter.apply(data); + map.put(key, data); + } + return map; + } + + public static Map mapCollection(Collection collection, Function keyGetter) { + return mapCollection(new HashMap<>(), collection, keyGetter); + } + + public static Map mapValue(Map map, Collection collection, Function keyGetter, Function valueGetter) { + for (T data : collection) { + K key = keyGetter.apply(data); + V value = valueGetter.apply(data); + map.put(key, value); + } + return map; + } + + public static Map mapValue(Collection collection, Function keyGetter, Function valueGetter) { + return mapValue(new HashMap<>(), collection, keyGetter, valueGetter); + } + + public static List arrayToList(T[] array) { + return new ArrayList<>(Arrays.asList(array)); + } + + public static R[] toArray(List dataList,Function translator){ + Object[] array = new Object[dataList.size()]; + for (int i = 0,s = dataList.size(); i < s; i++) { + array[i] = translator.apply(dataList.get(i)); + } + return (R[]) array; + } + + public static boolean haveLength(Collection collection) { + return collection != null && !collection.isEmpty(); + } + + public static boolean isEmpty(Collection collection) { + return collection == null || collection.isEmpty(); + } + + public static boolean haveLength(Map map) { + return map != null && !map.isEmpty(); + } + + public static boolean isEmpty(Map map) { + return map == null || map.isEmpty(); + } + + public static T findFirst(Collection collection, Function filter) { + for (T data : collection) { + if (filter.apply(data)) { + return data; + } + } + return null; + } + + /** 返回(dataList)中所不包含的(newList)中的元素 */ + public static List diffList(Collection dataList, Collection newList, Function getter) { + return CollectionUtil.diffList(dataList, newList, getter,getter); + } + + /** 返回(dataList)中所不包含的(newList)中的数据/元素 */ + public static List diffList(Collection dataList, Collection newList, Function getter, Function translator) { + List resultList = new ArrayList<>(); + Map dataMap = CollectionUtil.mapCollection(dataList,getter); + for (T newData : newList) { + K paramKey = getter.apply(newData); + T data = dataMap.get(paramKey); + if (data == null){ + resultList.add(translator.apply(newData)); + } + } + return resultList; + } + + public static void findAllParentInMapFromSelf(Map map, Function pKeyGetter,T data, Consumer callback){ + callback.accept(data); + CollectionUtil.findAllParentInMap(map,pKeyGetter,data,callback); + } + + public static void findAllParentInMap(Map map, Function pKeyGetter,T data, Consumer callback){ + String parentKey = pKeyGetter.apply(data); + while (parentKey != null){ + T parentData = map.get(parentKey); + if (parentData != null){ + callback.accept(parentData); + parentKey = pKeyGetter.apply(parentData); + }else { + parentKey = null; + } + } + } + + public static List combineList(List list_1, List list_2){ + List result = new ArrayList<>(); + result.addAll(list_1); + result.addAll(list_2); + return result; + } +} diff --git a/src/main/java/com/example/carbon/common/utils/DateUtil.java b/src/main/java/com/example/carbon/common/utils/DateUtil.java new file mode 100644 index 0000000..e3f81ee --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/DateUtil.java @@ -0,0 +1,27 @@ +package com.example.carbon.common.utils; + +import java.time.LocalDateTime; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Date; + +/** + * @author :sjs + * @since :2025-09-10 16:15:00 + */ +public class DateUtil { + public static String formatDate (Date date, String pattern) { + if (date == null ) { + return null; + } + // 将 Date 转换为 LocalDateTime + LocalDateTime localDateTime = date.toInstant() + .atZone(ZoneId.systemDefault()) + .toLocalDateTime(); + + // 创建格式化器 + DateTimeFormatter formatter = DateTimeFormatter.ofPattern(pattern); + + return localDateTime.format(formatter); + } +} diff --git a/src/main/java/com/example/carbon/common/utils/FileUtil.java b/src/main/java/com/example/carbon/common/utils/FileUtil.java new file mode 100644 index 0000000..88b45ba --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/FileUtil.java @@ -0,0 +1,250 @@ +package com.example.carbon.common.utils; + +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.base.func.ThrowableConsumer; +import com.example.carbon.common.model.info.FileInfo; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.util.StringUtils; +import org.springframework.web.multipart.MultipartFile; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.*; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +public class FileUtil { + + protected static final Logger Log = LoggerFactory.getLogger(FileUtil.class); + + private static final int BufferSize = 4096; + + // 定义合法的文件名字符集(只允许字母、数字、下划线、点和短横线) + private static final String VALID_CHARACTERS = "^[a-zA-Z0-9_\\u4e00-\\u9fa5.-]+$"; + + // 最大文件名长度(可根据需求调整) + private static final int MAX_FILE_NAME_LENGTH = 255; + + private static final Set ALLOWED_EXTENSIONS = + Stream.of(".jpg", ".jpeg", ".png", ".gif", ".bmp") + .collect(Collectors.toSet()); + + /** + * 校验文件名是否合法 + * + * @param fileName 待校验的文件名 + * @return true 如果文件名合法,false 否则 + */ + public static void isValidFileName(String fileName) { + if (fileName == null || fileName.isEmpty()) { + throw BizException.data("文件名不能为空"); + } + // 检查文件名长度是否超过限制 + if (fileName.length() > MAX_FILE_NAME_LENGTH) { + throw BizException.data("文件名长度不能超过200长度"); + } + // 检查文件名是否包含非法字符 + if (!fileName.matches(VALID_CHARACTERS)) { + throw BizException.data("文件名包含非法字符,只允许汉字、字母、数字、下划线、点和短横线的文件名称"); + } + + // 验证文件类型 + String fileExtension = fileName.substring(fileName.lastIndexOf(".")); + if (!ALLOWED_EXTENSIONS.contains(fileExtension)) { + throw BizException.custom("不支持的文件类型: " + fileExtension); + } + } + + /** 删除文件及其下属文件 **/ + public static void clearTargetFile(File targetFile){ + // 文件不存在 + if (!targetFile.exists()){ + return; + } + Log.info("ClearFile:{}",targetFile.getAbsoluteFile()); + // 文件为单体文件 + if (targetFile.isFile()){ + boolean success = targetFile.delete(); + if (!success){ + throw BizException.data("文件删除错误",targetFile.getAbsolutePath()); + } + } + // 文件为文件夹 + List dirList = new ArrayList<>(); + try { + iterateAllFileOfDir(targetFile, dir -> dirList.add(0,dir), file -> { + boolean success = file.delete(); + if (!success){ + throw BizException.data("文件删除错误",file.getAbsolutePath()); + } + }); + } catch (Exception exception) { + Log.error("File_Iterate_Fail:",exception); + throw BizException.data("文件删除错误","迭代错误"); + } + for (File file : dirList){ + boolean success = file.delete(); + if (!success){ + throw BizException.data("文件夹删除错误",file.getAbsolutePath()); + } + } + } + + /** 获取所有下属文件 */ + public static List findAllFileOfDir(File targetFile){ + List fileList = new ArrayList<>(); + try { + iterateAllFileOfDir(targetFile, null, fileList::add); + } catch (Exception exception) { + Log.error("File_Iterate_Fail:",exception); + throw BizException.data("文件删除错误","迭代错误"); + } + return fileList; + } + + /** 获取所有下属文件夹 */ + public static List findAllDirOfDir(File targetFile){ + List dirList = new ArrayList<>(); + try { + iterateAllFileOfDir(targetFile, dirList::add, null); + } catch (Exception exception) { + Log.error("File_Iterate_Fail:",exception); + throw BizException.data("文件删除错误","迭代错误"); + } + return dirList; + } + + /** 获取所有下属文件与文件夹 */ + public static List findAllFileAndDirOfDir(File targetFile){ + List fileList = new ArrayList<>(); + try { + iterateAllFileOfDir(targetFile, fileList::add, fileList::add); + } catch (Exception exception) { + Log.error("File_Iterate_Fail:",exception); + throw BizException.data("文件删除错误","迭代错误"); + } + return fileList; + } + + /** 迭代所有文件 */ + protected static void iterateAllFileOfDir(File targetFile, ThrowableConsumer onDir, ThrowableConsumer onFile) throws Exception{ + List dirHolder = new ArrayList<>(); + dirHolder.add(targetFile); + while (!dirHolder.isEmpty()){ + File targetDir = dirHolder.remove(0); + File[] fileArray = targetDir.listFiles(); + if (fileArray != null){ + for (File file:fileArray){ + if (file.isDirectory()){ + dirHolder.add(file); + if (onDir != null){ + onDir.accept(file); + } + }else { + if (onFile != null) { + onFile.accept(file); + } + } + } + } + } + } + + public static InputStream toInputStream(File file){ + if (!file.exists()){ + throw BizException.custom("文件流转换错误","文件不存在",file.getAbsolutePath()); + } + try (InputStream inputStream = Files.newInputStream(file.toPath())) { + return inputStream; + } catch (IOException exception) { + Log.error("File_InputStream_Fail:",exception); + throw BizException.custom("文件流转换错误",exception.getMessage()); + } + } + + /** 文件流转文件 + * @param filePath --- 目标文件路径 + * @param inputStream --- 文件流 + * @return 目标文件 + * **/ + public static File asFile(String filePath, InputStream inputStream){ + if (inputStream == null){ + return null; + } + File file = new File(filePath); + // 文件存在且删除失败 + if (file.exists() && !file.delete()){ + return null; + } + try (OutputStream outputStream = Files.newOutputStream(Paths.get(filePath))){ + int readLength = 0; + byte[] buffer = new byte[BufferSize]; + while ((readLength = inputStream.read(buffer, 0, BufferSize)) != -1) { + outputStream.write(buffer, 0, readLength); + } + outputStream.flush(); + } catch (IOException exception) { + Log.error("StreamToFile:",exception); + if (file.exists()){ + boolean success = file.delete(); + if (!success){ + throw BizException.data("文件转换错误",file.getAbsolutePath()); + } + } + throw BizException.data("文件转换错误",file.getAbsolutePath()); + } + return file; + } + + /** 创建文件夹 + * @param dirPath --- 路径 + * @return 是否成功创建目标文件夹 + * **/ + public static boolean createDir(String dirPath){ + Log.info("createDir:{}",dirPath); + File targetFile = new File(dirPath); + // 文件不存在 + if (!targetFile.exists()){ + return targetFile.mkdir(); + } + // 文件夹存在 + if (targetFile.isDirectory()){ + return true; + } + // 存在文件 --- 删除-重新创建 + boolean deleteResult = targetFile.delete(); + if (deleteResult){ + return targetFile.mkdir(); + }else { + return false; + } + } + + /** 获取文件信息 + * @param filePath --- 路径 + * **/ + public static FileInfo getFileInfo(String filePath){ + File file = new File(filePath); + String name = file.getName(); + String[] nameArray = name.split("\\."); + String size = new BigDecimal(file.length()/1048576) + .setScale(2, RoundingMode.HALF_UP) + .toString(); + return new FileInfo() + .setPath(filePath) + .setFullName(file.getName()) + .setName(nameArray[0]) + .setSuffix(nameArray[1]) + .setSize(size); + } + +} diff --git a/src/main/java/com/example/carbon/common/utils/ObjectUtil.java b/src/main/java/com/example/carbon/common/utils/ObjectUtil.java new file mode 100644 index 0000000..b04be95 --- /dev/null +++ b/src/main/java/com/example/carbon/common/utils/ObjectUtil.java @@ -0,0 +1,44 @@ +package com.example.carbon.common.utils; + +import org.springframework.beans.BeanUtils; + +import java.util.function.Function; + +public class ObjectUtil { + + public static V getValueOrNull(T data, Function getter){ + if (data == null){ + return null; + } + return getter.apply(data); + } + + public static V getValueOrDefault(T data,V defaultValue, Function getter){ + if (data == null){ + return defaultValue; + } + return getter.apply(data); + } + + public static String toStrOrNull(T data){ + if (data == null){ + return null; + } + return data.toString(); + } + + public static String toStrOrEmpty(T data){ + if (data == null){ + return ""; + } + return data.toString(); + } + + public static R copy(T data,R result){ + if (data == null){ + return null; + } + BeanUtils.copyProperties(data,result); + return result; + } +} diff --git a/src/main/java/com/example/carbon/controller/booking/HxBookingExperienceController.java b/src/main/java/com/example/carbon/controller/booking/HxBookingExperienceController.java new file mode 100644 index 0000000..4589f2f --- /dev/null +++ b/src/main/java/com/example/carbon/controller/booking/HxBookingExperienceController.java @@ -0,0 +1,75 @@ +package com.example.carbon.controller.booking; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.model.dto.booking.BookingExperiencePageSelectDto; +import com.example.carbon.common.model.entity.booking.HxBookingExperience; +import com.example.carbon.common.model.params.booking.BookingExperienceListSelectParam; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.service.booking.HxBookingExperienceService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author :sjs + * @since :2025-09-15 16:26:00 + */ +@RestController +@RequestMapping("/api/bookingExperience") +@Tag(name = "预约申请") +public class HxBookingExperienceController { + + final HxBookingExperienceService service; + + @Autowired + public HxBookingExperienceController(HxBookingExperienceService service) { + this.service = service; + } + + @PostMapping(value = "/save") + @ResponseBody + @Operation(summary = "新增/修改") + public ResultVo save(@RequestBody HxBookingExperience entity) { + if (entity.getId() == null) { + service.insertData(entity); + } else { + service.updateData(entity); + } + return ResultVo.ok(); + } + + @PostMapping("/getDetailById") + @ResponseBody + @Operation(summary ="查询") + public ResultVo getDetailById(@RequestBody String id) { + HxBookingExperience entity = service.getDetailById(id); + return ResultVo.ok(entity); + } + + @PostMapping("/getList") + @ResponseBody + @Operation(summary = "查询列表") + public ResultVo> getList(@RequestBody BookingExperienceListSelectParam param) { + List entityList = service.getList(param); + return ResultVo.ok(entityList); + } + + @PostMapping("/getPageList") + @ResponseBody + @Operation(summary = "查询分页列表") + public ResultVo> getPageList(@RequestBody BookingExperiencePageSelectDto dto) { + IPage bookingExperienceIPage = service.getPageList(dto.getPage().toPage(),dto.getSelectParam()); + return ResultVo.ok(bookingExperienceIPage); + } + + @PostMapping("/updateBookingStatus") + @ResponseBody + @Operation(summary = "修改预约状态") + public ResultVo updateBookingStatus(@RequestBody HxBookingExperience entity) { + service.updateBookingStatus(entity.getId(), entity.getBookingStatus()); + return ResultVo.ok(); + } +} diff --git a/src/main/java/com/example/carbon/controller/jrn/HxJournalismController.java b/src/main/java/com/example/carbon/controller/jrn/HxJournalismController.java new file mode 100644 index 0000000..408d93c --- /dev/null +++ b/src/main/java/com/example/carbon/controller/jrn/HxJournalismController.java @@ -0,0 +1,83 @@ +package com.example.carbon.controller.jrn; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.model.dto.jrn.JournalismPageSelectDto; +import com.example.carbon.common.model.entity.jrn.HxJournalism; +import com.example.carbon.common.model.params.jrn.JournalismListSelectParam; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.service.jrn.HxJournalismService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +/** + * @author :sjs + * @since :2025-09-10 10:17:00 + */ +@RestController +@RequestMapping("/api/journalism") +@Tag(name = "新闻中心") +public class HxJournalismController { + + final HxJournalismService service; + + @Autowired + public HxJournalismController(HxJournalismService service) { + this.service = service; + } + + @PostMapping(value = "/save") + @ResponseBody + @Operation(summary = "新增/修改") + public ResultVo save(@RequestBody HxJournalism entity) { + if (entity.getId() == null) { + service.insertData(entity); + } else { + service.updateData(entity); + } + return ResultVo.ok(); + } + + @PostMapping("/deleteByCodeList") + @ResponseBody + @Operation(summary ="删除节点") + public ResultVo deleteByCodeList(@RequestBody List codeList) { + service.deleteByCodeList(codeList); + return ResultVo.ok(); + } + + @PostMapping("/getDetailById") + @ResponseBody + @Operation(summary ="查询") + public ResultVo getDetailById(@RequestBody String id) { + HxJournalism entity = service.getDetailById(id); + return ResultVo.ok(entity); + } + + @PostMapping("/getList") + @ResponseBody + @Operation(summary = "查询列表") + public ResultVo> getList(@RequestBody JournalismListSelectParam param) { + List journalismList = service.getList(param); + return ResultVo.ok(journalismList); + } + + @PostMapping("/getPageList") + @ResponseBody + @Operation(summary = "查询分页列表") + public ResultVo> getPageList(@RequestBody JournalismPageSelectDto dto) { + IPage journalismIPage = service.getPageList(dto.getPage().toPage(),dto.getSelectParam()); + return ResultVo.ok(journalismIPage); + } + + @PostMapping("/publishByCodeList") + @ResponseBody + @Operation(summary ="发布") + public ResultVo publishByCodeList(@RequestBody List codeList) { + service.publishByCodeList(codeList); + return ResultVo.ok(); + } +} diff --git a/src/main/java/com/example/carbon/controller/perm/HxLoginController.java b/src/main/java/com/example/carbon/controller/perm/HxLoginController.java new file mode 100644 index 0000000..748d745 --- /dev/null +++ b/src/main/java/com/example/carbon/controller/perm/HxLoginController.java @@ -0,0 +1,35 @@ +package com.example.carbon.controller.perm; + +import com.example.carbon.common.model.dto.perm.LoginDto; +import com.example.carbon.common.model.info.perm.UserLoginInfo; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.service.impl.perm.LoginService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; + +/** + * @author :sjs + * @since :2025-09-11 09:45:00 + */ +@RestController +@RequestMapping("/perm") +@Tag(name = "用户") +public class HxLoginController { + + final LoginService loginService; + + @Autowired + public HxLoginController(LoginService loginService) { + this.loginService = loginService; + } + + @PostMapping("/login") + @ResponseBody + @Operation(summary = "登录") + public ResultVo login(@RequestBody LoginDto dto) { + UserLoginInfo info = loginService.login(dto.getData(),dto.getPwd()); + return ResultVo.ok(info); + } +} diff --git a/src/main/java/com/example/carbon/controller/sys/HxSysAttachController.java b/src/main/java/com/example/carbon/controller/sys/HxSysAttachController.java new file mode 100644 index 0000000..c5e5373 --- /dev/null +++ b/src/main/java/com/example/carbon/controller/sys/HxSysAttachController.java @@ -0,0 +1,42 @@ +package com.example.carbon.controller.sys; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.service.sys.HxSysAttachService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestMethod; +import org.springframework.web.bind.annotation.ResponseBody; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/api/attach") +@AllArgsConstructor +@Tag(name = "系统管理 - 附件") +public class HxSysAttachController { + + private final HxSysAttachService attachService; + + @RequestMapping(value = "/detail", method = {RequestMethod.GET}) + @ResponseBody + @Operation(summary = "详情") + public ResultVo detail(String id) { + return ResultVo.ok(attachService.detail(id)); + } + @RequestMapping(value = "/page", method = {RequestMethod.GET}) + @ResponseBody + @Operation(summary = "分页") + public ResultVo> page(HxSysAttach entity, int pageNum, int pageSize) { + return ResultVo.ok(attachService.page(entity, pageNum, pageSize)); + } + @RequestMapping(value = "/save", method = {RequestMethod.GET}) + @ResponseBody + @Operation(summary = "保存") + public ResultVo save(HxSysAttach entity) { + return ResultVo.ok(attachService.save(entity)); + } + +} diff --git a/src/main/java/com/example/carbon/controller/sys/HxSysCommonController.java b/src/main/java/com/example/carbon/controller/sys/HxSysCommonController.java new file mode 100644 index 0000000..59ebfe4 --- /dev/null +++ b/src/main/java/com/example/carbon/controller/sys/HxSysCommonController.java @@ -0,0 +1,42 @@ +package com.example.carbon.controller.sys; + + +import com.example.carbon.common.base.enums.TransAbleEnum; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.common.utils.CollectionUtil; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.*; + +import java.util.List; +import java.util.Map; + +/** + * @author sjs + * @since 2025-09-11 16:59 + */ +@RestController +@RequestMapping("/system/common") +@Tag(name = "系统管理 - 通用") +public class HxSysCommonController { + + @PostMapping("/getEnum") + @ResponseBody + @Operation(summary = "按需获取继承自TransAbleEnum的枚举类") + public ResultVo> getEnum(@RequestBody String enumType) { + return ResultVo.ok(TransAbleEnum.fromType(enumType)); + } + + @PostMapping("/getAllEnum") + @ResponseBody + @Operation(summary = "获取所有枚举") + public ResultVo>> getAllEnum(@RequestBody(required = false) List typeList) { + Map> enumMap = TransAbleEnum.findAllEnum(true); + if (CollectionUtil.isEmpty(typeList)) { + return ResultVo.ok(enumMap); + } + // 过滤指定enum + Map> resultMap = CollectionUtil.mapValue(typeList,type -> type, enumMap::get); + return ResultVo.ok(resultMap); + } +} diff --git a/src/main/java/com/example/carbon/controller/sys/HxSysMinioController.java b/src/main/java/com/example/carbon/controller/sys/HxSysMinioController.java new file mode 100644 index 0000000..b151d79 --- /dev/null +++ b/src/main/java/com/example/carbon/controller/sys/HxSysMinioController.java @@ -0,0 +1,80 @@ +package com.example.carbon.controller.sys; + +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import com.example.carbon.common.model.vo.ResultVo; +import com.example.carbon.common.utils.FileUtil; +import com.example.carbon.service.sys.HxSysAttachService; +import com.example.carbon.service.impl.sys.HxSysMinioServiceImpl; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.util.Arrays; +import java.util.List; + +@RestController +@AllArgsConstructor +@SuppressWarnings("all") +@RequestMapping("/api/minio") +@Tag(name = "Minio文件服务") +public class HxSysMinioController { + + private final HxSysMinioServiceImpl minioService; + private final HxSysAttachService hxSysAttachService; + + @PostMapping("/createBucket") + @Operation(summary = "创建bucket", description = "创建bucket") + @Parameter(name = "bucketName", description = "bucket名称") + public ResultVo createBucket(@RequestParam("bucketName") String bucketName) { + minioService.createBucket(bucketName); + return ResultVo.ok(); + } + + @PostMapping("/upload") + @Operation(summary = "上传文件", description = "上传文件") + @Parameter(name = "file", description = "文件") + public ResultVo uploadFile(@RequestParam("file") MultipartFile file) { + String originalFilename = file.getOriginalFilename(); + FileUtil.isValidFileName(originalFilename); + HxSysAttach sysAttach = minioService.uploadFile("", originalFilename, file); + return ResultVo.ok(sysAttach); + } + + @PostMapping(value = "/download") + @Operation(summary = "下载文件", description = "下载文件") + @Parameter(name = "attachId", description = "附件ID", required = true) + public void download(@RequestParam("attachId") Long attachId, HttpServletResponse response) { + minioService.downloadFile(attachId,response); + } + + @PostMapping("/createFolder") + @Operation(summary = "创建文件夹", description = "创建文件夹") + @Parameter(name = "folderName", description = "文件夹名称") + public ResultVo createFolder(@RequestParam("folderName") String folderName) { + minioService.createFolder(folderName); + return ResultVo.ok(); + } + + // 文件打包下载 + @PostMapping("/downloadFilesAsZip") + @Operation(summary = "创建文件夹", description = "创建文件夹") + @Parameter(name = "attachIds", description = "附件ID多个用英文逗号间隔") + public void downloadFilesAsZip(@RequestParam("attachIds") String attachIds) throws IOException { + try { + List attachList = hxSysAttachService.listByIds(Arrays.asList(attachIds.split(","))); + minioService.downloadFilesAsZip(attachList); + } catch (Exception e) { + throw BizException.fromDownload( "文件打包下载失败"); + } + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/mapper/booking/HxBookingExperienceMapper.java b/src/main/java/com/example/carbon/mapper/booking/HxBookingExperienceMapper.java new file mode 100644 index 0000000..8804ac7 --- /dev/null +++ b/src/main/java/com/example/carbon/mapper/booking/HxBookingExperienceMapper.java @@ -0,0 +1,11 @@ +package com.example.carbon.mapper.booking; + +import com.example.carbon.common.model.entity.booking.HxBookingExperience; +import com.github.yulichang.base.MPJBaseMapper; + +/** + * @author :sjs + * @since :2025-09-15 16:31:00 + */ +public interface HxBookingExperienceMapper extends MPJBaseMapper { +} diff --git a/src/main/java/com/example/carbon/mapper/jrn/HxJournalismMapper.java b/src/main/java/com/example/carbon/mapper/jrn/HxJournalismMapper.java new file mode 100644 index 0000000..8c151c2 --- /dev/null +++ b/src/main/java/com/example/carbon/mapper/jrn/HxJournalismMapper.java @@ -0,0 +1,11 @@ +package com.example.carbon.mapper.jrn; + +import com.example.carbon.common.model.entity.jrn.HxJournalism; +import com.github.yulichang.base.MPJBaseMapper; + +/** + * @author :sjs + * @since :2025-09-10 10:47:00 + */ +public interface HxJournalismMapper extends MPJBaseMapper { +} diff --git a/src/main/java/com/example/carbon/mapper/sys/HxSysAttachMapper.java b/src/main/java/com/example/carbon/mapper/sys/HxSysAttachMapper.java new file mode 100644 index 0000000..e80ddad --- /dev/null +++ b/src/main/java/com/example/carbon/mapper/sys/HxSysAttachMapper.java @@ -0,0 +1,7 @@ +package com.example.carbon.mapper.sys; + +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import com.github.yulichang.base.MPJBaseMapper; + +public interface HxSysAttachMapper extends MPJBaseMapper { +} diff --git a/src/main/java/com/example/carbon/service/booking/HxBookingExperienceService.java b/src/main/java/com/example/carbon/service/booking/HxBookingExperienceService.java new file mode 100644 index 0000000..3bbf508 --- /dev/null +++ b/src/main/java/com/example/carbon/service/booking/HxBookingExperienceService.java @@ -0,0 +1,28 @@ +package com.example.carbon.service.booking; + + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.enums.booking.BookingStatus; +import com.example.carbon.common.model.entity.booking.HxBookingExperience; +import com.example.carbon.common.model.params.booking.BookingExperienceListSelectParam; +import com.github.yulichang.base.MPJBaseService; + +import java.util.List; + +/** + * @author :sjs + * @since :2025-09-15 16:28:00 + */ +public interface HxBookingExperienceService extends MPJBaseService { + void insertData(HxBookingExperience entity); + + void updateData(HxBookingExperience entity); + + HxBookingExperience getDetailById(String id); + + List getList(BookingExperienceListSelectParam param); + + IPage getPageList(IPage page, BookingExperienceListSelectParam param); + + void updateBookingStatus(Long id, BookingStatus bookingStatus); +} diff --git a/src/main/java/com/example/carbon/service/impl/booking/HxBookingExperienceServiceImpl.java b/src/main/java/com/example/carbon/service/impl/booking/HxBookingExperienceServiceImpl.java new file mode 100644 index 0000000..fb2d456 --- /dev/null +++ b/src/main/java/com/example/carbon/service/impl/booking/HxBookingExperienceServiceImpl.java @@ -0,0 +1,164 @@ +package com.example.carbon.service.impl.booking; + +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.base.core.CommonServiceImpl; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.enums.DeleteStatus; +import com.example.carbon.common.enums.booking.BookingItem; +import com.example.carbon.common.enums.booking.BookingStatus; +import com.example.carbon.common.model.entity.booking.HxBookingExperience; +import com.example.carbon.common.model.params.booking.BookingExperienceListSelectParam; +import com.example.carbon.common.service.EmailService; +import com.example.carbon.mapper.booking.HxBookingExperienceMapper; +import com.example.carbon.service.booking.HxBookingExperienceService; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import javax.mail.MessagingException; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.*; +import java.util.stream.Collectors; + +/** + * @author :sjs + * @since :2025-09-15 16:30:00 + */ +@Service +public class HxBookingExperienceServiceImpl extends CommonServiceImpl implements HxBookingExperienceService { + + // 从配置文件中注入收件人邮箱账号列表 + @Value("${mail.to-list}") + private String toList; + + // 从配置文件中注入主题 + @Value("${mail.subject}") + private String subject; + + final EmailService emailService; + + @Autowired + public HxBookingExperienceServiceImpl(EmailService emailService) { + this.emailService = emailService; + } + + @Override + @Transactional + public void insertData(HxBookingExperience entity) { + entity.setCreateTime(new Date()); + + // 生成Code + entity.generateCode(); + + // 新增 + this.save(entity); + + // 发送邮件 + sendBookingEmail(entity); + } + + /** + * 构建并发送预约邮件 + */ + private void sendBookingEmail(HxBookingExperience entity) { + // 获取收件人列表,允许逗号前后有空格 + String[] defaultTo = toList.split("\\s*,\\s*"); + + // 获取预约项目名称 + String bookingItemNames = entity.getBookingItemList().stream() + .map(BookingItem::getName) + .collect(Collectors.joining(", ")); + + // 构建邮件内容 + String emailContent = buildEmailContent(entity, bookingItemNames); + + try { + // 调用邮件服务的简化方法发送文本邮件 + emailService.sendHtmlEmail(defaultTo, subject, emailContent); + } catch (MessagingException e) { + throw BizException.data("邮件发送失败"); + } + } + + /** + * 构建邮件内容HTML + */ + private String buildEmailContent(HxBookingExperience entity, String bookingItemNames) { + StringBuilder contentBuilder = new StringBuilder(); + contentBuilder.append("关注的产品服务:").append(bookingItemNames).append("
") + .append("姓名:").append(entity.getName()).append("
") + .append("单位:").append(entity.getUnit()).append("
") + .append("电话:").append(entity.getPhone()).append("
") + .append("邮箱:").append(entity.getMail()).append("
") + .append("留言:").append(entity.getMessage() != null ? entity.getMessage() : "无"); + + return contentBuilder.toString(); + } + + @Override + public void updateData(HxBookingExperience entity) { + entity.setUpdateTime(new Date()); + this.updateById(entity); + } + + @Override + public HxBookingExperience getDetailById(String id) { + return this.getOne( + JoinWrappers.lambda(HxBookingExperience.class) + .eq(HxBookingExperience::getDeleteStatus, DeleteStatus.NO) + .eq(HxBookingExperience::getId, id) + ); + } + + @Override + public List getList(BookingExperienceListSelectParam param) { + return this.selectJoinList(HxBookingExperience.class, this.createListLambdaWrapper(param)); + } + + @Override + public IPage getPageList(IPage page, BookingExperienceListSelectParam param) { + return this.selectJoinListPage(page, HxBookingExperience.class, this.createListLambdaWrapper(param)); + } + + private MPJLambdaWrapper createListLambdaWrapper(BookingExperienceListSelectParam param) { + MPJLambdaWrapper wrapper = JoinWrappers.lambda(HxBookingExperience.class) + .eq(HxBookingExperience::getDeleteStatus, DeleteStatus.NO) + + // 过滤姓名 + .like(param.getName() != null, HxBookingExperience::getName, param.getName()) + // 过滤状态 + .eq(param.getBookingStatus() != null, HxBookingExperience::getBookingStatus, param.getBookingStatus()); + + // 过滤发布起始时间 + if (param.getApplicationStartTime() != null) { + // 转换为当天的开始时间 (00:00:00) + LocalDateTime startDateTime = param.getApplicationStartTime().atStartOfDay(); + wrapper.ge(HxBookingExperience::getCreateTime, startDateTime); + } + // 过滤发布结束时间 + if (param.getApplicationEndTime() != null) { + // 转换为当天的结束时间 (23:59:59.999999999) + LocalDateTime endDateTime = param.getApplicationEndTime().atTime(LocalTime.MAX); + wrapper.le(HxBookingExperience::getCreateTime, endDateTime); + } + + return wrapper.selectAll(HxBookingExperience.class) + .orderByDesc(HxBookingExperience::getCreateTime); + } + + @Override + public void updateBookingStatus(Long id, BookingStatus bookingStatus) { + // 只更新 bookingStatus 字段 + this.update( + new LambdaUpdateWrapper() + .eq(HxBookingExperience::getId, id) + .eq(HxBookingExperience::getDeleteStatus, DeleteStatus.NO) + .set(HxBookingExperience::getBookingStatus, bookingStatus) + ); + } +} diff --git a/src/main/java/com/example/carbon/service/impl/jrn/HxJournalismServiceImpl.java b/src/main/java/com/example/carbon/service/impl/jrn/HxJournalismServiceImpl.java new file mode 100644 index 0000000..bfee6d5 --- /dev/null +++ b/src/main/java/com/example/carbon/service/impl/jrn/HxJournalismServiceImpl.java @@ -0,0 +1,220 @@ +package com.example.carbon.service.impl.jrn; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.example.carbon.common.base.core.CommonServiceImpl; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.enums.DeleteStatus; +import com.example.carbon.common.enums.jrn.PublishStatus; +import com.example.carbon.common.model.entity.jrn.HxJournalism; +import com.example.carbon.common.model.params.jrn.JournalismListSelectParam; +import com.example.carbon.common.utils.CollectionUtil; +import com.example.carbon.mapper.jrn.HxJournalismMapper; +import com.example.carbon.service.jrn.HxJournalismService; +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.CollectionUtils; + +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; +import java.util.function.BiFunction; + +/** + * @author :sjs + * @since :2025-09-10 10:45:00 + */ +@Service +public class HxJournalismServiceImpl extends CommonServiceImpl implements HxJournalismService { + + @Override + public void insertData(HxJournalism entity) { + // 检测title是否重复 + this.checkTitleConflict(entity, BizException::fromUpdate); + entity.setCreateTime(new Date()); + + // 发布时间处理 + if (PublishStatus.PUBLISHED.equals(entity.getPublishStatus())) { + entity.setPublishStatus(PublishStatus.PUBLISHED); + entity.setPublishTime(new Date()); + } else { + entity.setPublishStatus(PublishStatus.DRAFT); + entity.setPublishTime(null); + } + + // 生成Code + entity.generateCode(); + + // 新增 + this.save(entity); + } + + @Override + public void updateData(HxJournalism entity) { + // 检测title是否重复 + this.checkTitleConflict(entity, BizException::fromUpdate); + entity.setUpdateTime(new Date()); + + HxJournalism originalEntity = this.getById(entity.getId()); + PublishStatus targetStatus = entity.getPublishStatus(); + PublishStatus originalStatus = originalEntity.getPublishStatus(); + + // 处理发布操作 + if (targetStatus == PublishStatus.PUBLISHED) { + handlePublishOperation(entity, originalEntity, originalStatus); + } + // 处理保存操作 + else { + handleSaveOperation(entity, originalEntity, originalStatus); + } + + this.updateById(entity); + } + private void checkTitleConflict(HxJournalism entity, BiFunction expCreator) { + long num = this.count(Wrappers.lambdaQuery(HxJournalism.class) + .eq(HxJournalism::getDeleteStatus, DeleteStatus.NO) + .eq(entity.getTitle() != null, HxJournalism::getTitle,entity.getTitle()) + .ne(entity.getId() != null, HxJournalism::getId, entity.getId()) + .eq(HxJournalism::getTitle,entity.getTitle())); + if (num > 0){ + throw expCreator.apply("已存在同名标题",entity.getTitle()); + } + } + private void handlePublishOperation(HxJournalism entity, HxJournalism originalEntity, PublishStatus originalStatus) { + if (originalStatus == PublishStatus.PUBLISHED) { + // 保持原有发布状态和时间 + entity.setPublishStatus(originalStatus); + entity.setPublishTime(originalEntity.getPublishTime()); + return; + } + + // 设置发布状态并处理发布时间 + entity.setPublishStatus(PublishStatus.PUBLISHED); + entity.setPublishTime(originalEntity.getPublishTime() != null ? + originalEntity.getPublishTime() : new Date()); + } + private void handleSaveOperation(HxJournalism entity, HxJournalism originalEntity, PublishStatus originalStatus) { + // 设置发布时间 + entity.setPublishTime(originalEntity.getPublishTime()); + + // 仅当原状态为已发布时才需要修改状态 + if (originalStatus == PublishStatus.PUBLISHED) { + entity.setPublishStatus(PublishStatus.REEDIT); + } else { + entity.setPublishStatus(originalStatus); + } + } + + @Override + public void deleteByCodeList(List codeList) { + if (CollectionUtil.isEmpty(codeList)){ + return; + } + // 删除标题 + this.removeBatchById(Wrappers.lambdaQuery(HxJournalism.class) + .eq(HxJournalism::getDeleteStatus, DeleteStatus.NO) + .in(HxJournalism::getCode,codeList) + .select(HxJournalism::getId, HxJournalism::getCode)); + } + + @Override + public HxJournalism getDetailById(String id) { + return this.getOne( + JoinWrappers.lambda(HxJournalism.class) + .eq(HxJournalism::getDeleteStatus, DeleteStatus.NO) + .eq(HxJournalism::getId, id) + ); + } + + @Override + public List getList(JournalismListSelectParam param) { + return this.list(this.createListLambdaWrapper(param)); + } + + @Override + public IPage getPageList(IPage page, JournalismListSelectParam param) { + return this.selectJoinListPage(page, HxJournalism.class, this.createListLambdaWrapper(param)); + } + private MPJLambdaWrapper createListLambdaWrapper(JournalismListSelectParam param) { + MPJLambdaWrapper wrapper = JoinWrappers.lambda(HxJournalism.class) + .eq(HxJournalism::getDeleteStatus, DeleteStatus.NO) + + // 过滤标题 + .like(param.getTitle() != null, HxJournalism::getTitle, param.getTitle()) + // 过滤状态 + .eq(param.getPublishStatus() != null, HxJournalism::getPublishStatus, param.getPublishStatus()); + + // 过滤发布起始时间 + if (param.getPublishStartTime() != null) { + // 转换为当天的开始时间 (00:00:00) + LocalDateTime startDateTime = param.getPublishStartTime().atStartOfDay(); + wrapper.ge(HxJournalism::getPublishTime, startDateTime); + } + // 过滤发布结束时间 + if (param.getPublishEndTime() != null) { + // 转换为当天的结束时间 (23:59:59.999999999) + LocalDateTime endDateTime = param.getPublishEndTime().atTime(LocalTime.MAX); + wrapper.le(HxJournalism::getPublishTime, endDateTime); + } + // 过滤创建起始时间 + if (param.getCreateStartTime() != null) { + // 转换为当天的开始时间 (00:00:00) + LocalDateTime startDateTime = param.getCreateStartTime().atStartOfDay(); + wrapper.ge(HxJournalism::getCreateTime, startDateTime); + } + // 过滤创建结束时间 + if (param.getCreateEndTime() != null) { + // 转换为当天的结束时间 (23:59:59.999999999) + LocalDateTime endDateTime = param.getCreateEndTime().atTime(LocalTime.MAX); + wrapper.le(HxJournalism::getCreateTime, endDateTime); + } + + return wrapper.selectAll(HxJournalism.class) + .orderByDesc(HxJournalism::getCreateTime); + } + + @Override + @Transactional + public void publishByCodeList(List codeList) { + if (CollectionUtils.isEmpty(codeList)) { + return; + } + + // 查询所有需要更新的记录 + List journalismList = this.list( + JoinWrappers.lambda(HxJournalism.class) + .eq(HxJournalism::getDeleteStatus, DeleteStatus.NO) + .in(HxJournalism::getCode, codeList) + ); + + if (CollectionUtils.isEmpty(journalismList)) { + return; + } + + // 批量更新 + List updateList = new ArrayList<>(); + Date now = new Date(); + + for (HxJournalism journalism : journalismList) { + HxJournalism updateEntity = new HxJournalism() + .setId(journalism.getId()) + .setPublishStatus(PublishStatus.PUBLISHED); + + // 处理发布时间:如果原值为null则设为当前时间,否则保持原值 + if (journalism.getPublishTime() == null) { + updateEntity.setPublishTime(now); + } else { + updateEntity.setPublishTime(journalism.getPublishTime()); + } + + updateList.add(updateEntity); + } + + // 批量更新 + this.updateBatchById(updateList); + } +} diff --git a/src/main/java/com/example/carbon/service/impl/perm/LoginService.java b/src/main/java/com/example/carbon/service/impl/perm/LoginService.java new file mode 100644 index 0000000..1e220b6 --- /dev/null +++ b/src/main/java/com/example/carbon/service/impl/perm/LoginService.java @@ -0,0 +1,44 @@ +package com.example.carbon.service.impl.perm; + +import com.example.carbon.common.base.constant.SysConstant; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.model.info.perm.UserLoginInfo; +import com.example.carbon.common.utils.CodeUtil; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +/** + * @author :sjs + * @since :2025-09-11 09:53:00 + */ +@Service +public class LoginService { + + public UserLoginInfo login(String account, String pwd) { + if (StringUtils.isEmpty(account)) { + throw BizException.userLogin("用户名不可为空"); + } + String managerPwd = SysConstant.SYSTEM_MANAGER.get(account); + if (managerPwd == null) { + throw BizException.userLogin("未发现用户", account); + } + // 检测用户状态 + this.checkUserInfo(managerPwd, pwd); + // 获取用户信息 + return this.onLoginSuccess(account); + } + + private void checkUserInfo(String managerPwd, String pwd) { + if (!pwd.equals(managerPwd)) { + throw BizException.userLogin("密码错误"); + } + } + + private UserLoginInfo onLoginSuccess(String account) { + // 登录返回数据 + return new UserLoginInfo() + .setAccount(account) + .setUserName(SysConstant.SYSTEM_MANAGER_NAME) + .setToken(CodeUtil.generateUUID()); + } +} diff --git a/src/main/java/com/example/carbon/service/impl/sys/HxSysAttachServiceImpl.java b/src/main/java/com/example/carbon/service/impl/sys/HxSysAttachServiceImpl.java new file mode 100644 index 0000000..c165063 --- /dev/null +++ b/src/main/java/com/example/carbon/service/impl/sys/HxSysAttachServiceImpl.java @@ -0,0 +1,83 @@ +package com.example.carbon.service.impl.sys; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.example.carbon.common.base.core.CommonServiceImpl; +import com.example.carbon.common.enums.DeleteStatus; +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import com.example.carbon.common.utils.CollectionUtil; +import com.example.carbon.mapper.sys.HxSysAttachMapper; +import com.example.carbon.service.sys.HxSysAttachService; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +@AllArgsConstructor +@SuppressWarnings("all") +public class HxSysAttachServiceImpl extends CommonServiceImpl implements HxSysAttachService { + + + /** + * 删除文件 + * @param ids + * @return + */ + @Override + public void deleteBatch(List ids) { + if (CollectionUtil.isEmpty(ids)){ + return; + } + this.removeBatchById(Wrappers.lambdaQuery(HxSysAttach.class) + .eq(HxSysAttach::getDeleteStatus, DeleteStatus.NO) + .in(HxSysAttach::getId, ids)); + } + + + /** + * 获取文件详情 + * @param id + * @return + */ + @Override + public HxSysAttach detail(String id) { + return this.getById(id); + } + + /** + * 分页查询文件 + * @param entity + * @param pageNum + * @param pageSize + * @return + */ + @Override + public IPage page(HxSysAttach entity, int pageNum, int pageSize) { + LambdaQueryWrapper wrapper = new LambdaQueryWrapper<>(); + wrapper.eq(StringUtils.isNotEmpty(entity.getOrgCode()), HxSysAttach::getOrgCode, entity.getOrgCode()); + wrapper.eq(StringUtils.isNotEmpty(entity.getBucketName()), HxSysAttach::getBucketName, entity.getBucketName()); + wrapper.eq(StringUtils.isNotEmpty(entity.getLink()), HxSysAttach::getLink, entity.getLink()); + wrapper.eq(HxSysAttach::getDeleteStatus, DeleteStatus.NO); + return baseMapper.selectPage(new Page<>(pageNum, pageSize), wrapper); + } + + @Override + public Map getAttachPath(List idList) { + if (CollectionUtil.isEmpty(idList)){ + return Collections.emptyMap(); + } + List attachList = this.list(Wrappers.lambdaQuery(HxSysAttach.class) + .eq(HxSysAttach::getDeleteStatus, DeleteStatus.NO) + .in(HxSysAttach::getId, idList) + .select(HxSysAttach::getId, HxSysAttach::getLink)); + return CollectionUtil.mapValue(attachList,HxSysAttach::getId,HxSysAttach::getLink); + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/service/impl/sys/HxSysMinioServiceImpl.java b/src/main/java/com/example/carbon/service/impl/sys/HxSysMinioServiceImpl.java new file mode 100644 index 0000000..836c7c6 --- /dev/null +++ b/src/main/java/com/example/carbon/service/impl/sys/HxSysMinioServiceImpl.java @@ -0,0 +1,228 @@ +package com.example.carbon.service.impl.sys; + +import cn.hutool.core.date.DateUtil; +import com.example.carbon.common.base.exception.BizException; +import com.example.carbon.common.config.MinioConfig; +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import com.example.carbon.common.utils.AttachUtil; +import com.example.carbon.service.sys.HxSysAttachService; +import com.example.carbon.service.sys.HxSysMinioService; +import io.minio.*; +import io.minio.errors.MinioException; +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import javax.validation.constraints.NotEmpty; +import javax.validation.constraints.NotNull; +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +@Slf4j +@Service +@AllArgsConstructor +@SuppressWarnings("all") +public class HxSysMinioServiceImpl implements HxSysMinioService { + + private final String SYSTEM_SPACE = "system"; + private final String YYYY_MONTH_DAY = "yyyyMMdd"; + + private final MinioConfig minioConfig; + + private final MinioClient minioClient; + + private final HxSysAttachService attachService; + + /** + * 创建存储桶 + * @param bucketName 存储桶名称 + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + @Override + public void createBucket(String bucketName) { + try { + boolean found = minioClient.bucketExists(BucketExistsArgs.builder().bucket(bucketName).build()); + if (!found) { + minioClient.makeBucket(MakeBucketArgs.builder().bucket(bucketName).build()); + } + } catch (MinioException e) { + e.printStackTrace(); + log.error("创建存储桶失败", e); + throw BizException.custom("创建存储桶失败"); + } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) { + e.printStackTrace(); + throw BizException.custom("创建存储桶失败"); + } + } + + /** + * 上传文件 + * + * @param bucketName 存储桶名称 + * @param originalFilename 对象名称 + * @param file 上传的文件 + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + @Override + public HxSysAttach uploadFile(String name, @NotEmpty String originalFilename, @NotNull MultipartFile file) { + try { +// String tokenOrgCode = UserContext.getInfo().getTokenInfo().getRequestOrgCode(); +// String orgCode = StringUtils.isEmpty(tokenOrgCode) ? SYSTEM_SPACE : tokenOrgCode; + String orgCode = SYSTEM_SPACE; + String bucketName = minioConfig.bucketName; + this.createBucket(bucketName); + // 组织标识 + InputStream inputStream = file.getInputStream(); + // 文件夹名称以年月日开头 + String folderName = DateUtil.format(DateUtil.date(), YYYY_MONTH_DAY); + ObjectWriteResponse objectWriteResponse = minioClient.putObject( + PutObjectArgs.builder() + .bucket(bucketName) + .object(AttachUtil.joinPath(orgCode, folderName, originalFilename)) + .stream(inputStream, file.getSize(), -1) + .contentType(file.getContentType()) + .build() + ); + inputStream.close(); + // 保存文件到 系统文件表 + return this.saveAttach(bucketName, orgCode, folderName, "", originalFilename, file); + } catch (MinioException e) { + e.printStackTrace(); + log.error("上传文件失败", e); + throw BizException.fromUpload(originalFilename); + } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) { + e.printStackTrace(); + log.error("上传文件失败", e); + throw BizException.fromUpload(e.getMessage()); + } + + + } + + /** + * 保存文件到 系统文件表 + * @param bucketName 存储桶名称 + * @param orgCode 组织标识 + * @param folderName 文件夹名称 + * @param originalFilename 对象名称 + * @param name 自定义名称 + * @param file 上传的文件 + * @return + */ + private HxSysAttach saveAttach(String bucketName, String orgCode, String folderName, String name, String originalFilename, MultipartFile file) { + String path = AttachUtil.joinPath(minioConfig.domainUrl, bucketName, orgCode, folderName, originalFilename); +// int lastIndex = originalFilename.lastIndexOf('.'); +// String newName = lastIndex != -1 ? originalFilename.substring(0, lastIndex) : originalFilename; + HxSysAttach sysAttach = new HxSysAttach() + .setLink(path) + .setName(originalFilename) + .setOriginalName(AttachUtil.joinPath(orgCode, folderName, originalFilename)) + .setExtension(AttachUtil.getFileSuffix(originalFilename)) + .setAttachSize(file.getSize()) + .setDomainUrl(minioConfig.domainUrl) + .setBucketName(bucketName) + .setOrgCode(orgCode); + // 保存文件到 系统文件表 + boolean save = attachService.save(sysAttach); + if (!save) { + log.error("文件保存失败"); + throw BizException.custom("文件保存失败"); + } + return sysAttach; + } + + /** + * 下载文件 + * @param bucketName 存储桶名称 + * @param objectName 对象名称 + * @return 文件输入流 + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + @Override + public void downloadFile(Long attachId, HttpServletResponse response) { + HxSysAttach sysAttach = attachService.getById(attachId); + // 构建获取对象的参数 + GetObjectArgs getObjectArgs = GetObjectArgs.builder() + .bucket(minioConfig.bucketName) + .object(sysAttach.getOriginalName()) + .build(); + try (GetObjectResponse getObjectResponse = minioClient.getObject(getObjectArgs); + InputStream inputStream = getObjectResponse; + OutputStream outputStream = response.getOutputStream()) { + // 编码文件名 + String encodedFilename = URLEncoder.encode(sysAttach.getName(), StandardCharsets.UTF_8.toString()); + // 设置响应头 + response.setContentType("application/octet-stream"); + response.setHeader("Content-Disposition", "attachment; filename=" + encodedFilename); + + // 将文件内容写入响应输出流 + byte[] buffer = new byte[4096]; + int bytesRead; + while ((bytesRead = inputStream.read(buffer)) != -1) { + outputStream.write(buffer, 0, bytesRead); + } + outputStream.flush(); + } catch (MinioException e) { + e.printStackTrace(); + log.error("下载文件失败", e); + throw BizException.fromDownload(sysAttach.getName()); + } catch (IOException | NoSuchAlgorithmException | InvalidKeyException e) { + e.printStackTrace(); + log.error("下载文件失败", e); + throw BizException.fromDownload(e.getMessage()); + } + } + + /** + * 下载多个文件 + * @param attachList 文件列表 + */ + @Override + public void downloadFilesAsZip(List attachList) { + } + + + /** + * 创建文件夹 + * @param minioClient MinIO客户端 + * @param bucketName 存储桶名称 + * @param folderName 文件夹名称 + * @throws Exception + */ + public void createFolder(String folderName) { + try { + String bucketName = minioConfig.bucketName; + // 检查存储桶是否存在,如果不存在则创建 + boolean bucketExists = minioClient.bucketExists(io.minio.BucketExistsArgs.builder().bucket(bucketName).build()); + if (!bucketExists) { + minioClient.makeBucket(io.minio.MakeBucketArgs.builder().bucket(bucketName).build()); + } + // 在 MinIO 中,通过上传一个以文件夹名结尾且为空的对象来模拟创建文件夹 + minioClient.putObject(io.minio.PutObjectArgs.builder() + .bucket(bucketName) + .object(folderName + "/") + .stream(new java.io.ByteArrayInputStream(new byte[0]), 0, -1) + .build()); + log.info("文件夹创建成功:" + folderName); + } catch (Exception e) { + e.printStackTrace(); + log.error("文件夹创建失败:" + folderName, e); + throw BizException.custom("文件夹创建失败:" + folderName); + } + + } +} \ No newline at end of file diff --git a/src/main/java/com/example/carbon/service/jrn/HxJournalismService.java b/src/main/java/com/example/carbon/service/jrn/HxJournalismService.java new file mode 100644 index 0000000..3fba4c6 --- /dev/null +++ b/src/main/java/com/example/carbon/service/jrn/HxJournalismService.java @@ -0,0 +1,28 @@ +package com.example.carbon.service.jrn; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.example.carbon.common.model.entity.jrn.HxJournalism; +import com.example.carbon.common.model.params.jrn.JournalismListSelectParam; +import com.github.yulichang.base.MPJBaseService; + +import java.util.List; + +/** + * @author :sjs + * @since :2025-09-10 10:44:00 + */ +public interface HxJournalismService extends MPJBaseService { + void deleteByCodeList(List codeList); + + HxJournalism getDetailById(String id); + + List getList(JournalismListSelectParam param); + + IPage getPageList(IPage page, JournalismListSelectParam param); + + void publishByCodeList(List codeList); + + void insertData(HxJournalism entity); + + void updateData(HxJournalism entity); +} diff --git a/src/main/java/com/example/carbon/service/sys/HxSysAttachService.java b/src/main/java/com/example/carbon/service/sys/HxSysAttachService.java new file mode 100644 index 0000000..2f2e0e7 --- /dev/null +++ b/src/main/java/com/example/carbon/service/sys/HxSysAttachService.java @@ -0,0 +1,38 @@ +package com.example.carbon.service.sys; + +import com.baomidou.mybatisplus.core.metadata.IPage; +import com.baomidou.mybatisplus.extension.service.IService; +import com.example.carbon.common.model.entity.sys.HxSysAttach; + +import java.util.List; +import java.util.Map; + +public interface HxSysAttachService extends IService { + + /** + * 批量删除 + * @param ids + * @return + */ + void deleteBatch(List ids); + + /** + * 详情 + * @param id + * @return + */ + HxSysAttach detail(String id); + + /** + * 分页 + * @param entity + * @param pageNum + * @param pageSize + * @return + */ + IPage page(HxSysAttach entity, int pageNum, int pageSize); + + + /** 获取附件路径 */ + Map getAttachPath(List idList); +} diff --git a/src/main/java/com/example/carbon/service/sys/HxSysMinioService.java b/src/main/java/com/example/carbon/service/sys/HxSysMinioService.java new file mode 100644 index 0000000..7ba0392 --- /dev/null +++ b/src/main/java/com/example/carbon/service/sys/HxSysMinioService.java @@ -0,0 +1,45 @@ +package com.example.carbon.service.sys; + + +import com.example.carbon.common.model.entity.sys.HxSysAttach; +import org.springframework.web.multipart.MultipartFile; + +import javax.servlet.http.HttpServletResponse; +import java.io.IOException; +import java.security.InvalidKeyException; +import java.security.NoSuchAlgorithmException; +import java.util.List; + +public interface HxSysMinioService { + + /** + * 创建bucket + * @param bucketName + */ + void createBucket(String bucketName); + + /** + * 上传文件 + * + * @param name + * @param originalName + * @param file + */ + HxSysAttach uploadFile(String name, String originalName, MultipartFile file); + + /** + * 下载文件 + * @param attachId + * @return + */ + void downloadFile(Long attachId, HttpServletResponse response); + + /** + * 下载文件包 + * @param attachList + * @throws IOException + * @throws NoSuchAlgorithmException + * @throws InvalidKeyException + */ + void downloadFilesAsZip(List attachList); +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..98a5475 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,82 @@ +# 服务器配置 +server: + port: 8081 + +spring: + jackson: + default-property-inclusion: non_null + time-zone: GMT+8 + date-format: yyyy-MM-dd HH:mm:ss + servlet: + multipart: + max-file-size: 10MB + max-request-size: 10MB + datasource: + url: jdbc:mysql://1.95.44.128:13306/carbon_homepage?serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + username: root + password: Zlcxdb2025!. + driver-class-name: com.mysql.cj.jdbc.Driver + druid: + initial-size: 5 + min-idle: 5 + max-active: 50 + max-wait: 60000 + time-between-eviction-runs-millis: 60000 + min-evictable-idle-time-millis: 300000 + validation-query: SELECT 1 + test-while-idle: true + test-on-borrow: false + test-on-return: false + pool-prepared-statements: true + max-pool-prepared-statement-per-connection-size: 20 + +# Mybatis-Plus 配置 +mybatis-plus: + # mapper.xml 文件扫描 +# mapper-locations: classpath*:/mapper/*.xml + configuration: + # 日志实现(查看SQL语句) + log-impl: org.apache.ibatis.logging.stdout.StdOutImpl + type-enums-package: com.example.carbon.common.enums + +# minio +minio: + domain-url: http://1.95.44.128:9001 + bucket-name: carbon + endpoint: http://1.95.44.128:9001 + access-key: FJto3nnu25h1YeC6JCGZ + secret-key: jJOwKCRL6g3aQ0FxaSNpXhP3Sz09oMqXS0ZK6wwg + +# mail +mail: + # 邮件服务器主机地址(根据企业邮箱提供商提供的信息填写) + host: smtp.qq.com + # 邮件服务器端口(SSL加密通常使用465或994,TLS加密通常使用587) + port: 465 + # 发件人邮箱地址(通常是完整的邮箱地址) + username: 1043076829@qq.com + # 发件人邮箱密码或授权码(注意不是登录密码,是企业邮箱提供的SMTP授权码) + password: oxbfoyozpobkbccj + # 邮件协议(发送邮件使用smtp协议) + protocol: smtp + + # 收件人邮箱列表,多个用逗号分隔 + to-list: 1043076829@qq.com,rearlly@qq.com + # 发件主题 + subject: demo预约体验申请 + + # JavaMail属性配置 + properties: + mail: + smtp: + # 是否需要身份验证(通常需要) + auth: true + # 是否启用STARTTLS加密(与SSL二选一) + starttls: + enable: false + # 是否启用SSL加密(与STARTTLS二选一) + ssl: + enable: true + # 是否启用调试模式(开发环境可开启,生产环境应关闭) + debug: false +