commit
5d485c0edc
108 changed files with 6087 additions and 0 deletions
@ -0,0 +1,144 @@ |
||||
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/ |
||||
### Java template |
||||
# Compiled class file |
||||
*.class |
||||
|
||||
# Log file |
||||
*.log |
||||
|
||||
# BlueJ files |
||||
*.ctxt |
||||
|
||||
# Mobile Tools for Java (J2ME) |
||||
.mtj.tmp/ |
||||
|
||||
# Package Files # |
||||
*.jar |
||||
*.war |
||||
*.nar |
||||
*.ear |
||||
*.zip |
||||
*.tar.gz |
||||
*.rar |
||||
|
||||
# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml |
||||
hs_err_pid* |
||||
|
||||
### Maven template |
||||
target/ |
||||
pom.xml.tag |
||||
pom.xml.releaseBackup |
||||
pom.xml.versionsBackup |
||||
pom.xml.next |
||||
release.properties |
||||
dependency-reduced-pom.xml |
||||
buildNumber.properties |
||||
.mvn/timing.properties |
||||
# https://github.com/takari/maven-wrapper#usage-without-binary-jar |
||||
.mvn/wrapper/maven-wrapper.jar |
||||
|
||||
### JetBrains template |
||||
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider |
||||
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 |
||||
|
||||
# User-specific stuff |
||||
.idea/**/workspace.xml |
||||
.idea/**/tasks.xml |
||||
.idea/**/usage.statistics.xml |
||||
.idea/**/dictionaries |
||||
.idea/**/shelf |
||||
|
||||
# Generated files |
||||
.idea/**/contentModel.xml |
||||
|
||||
# Sensitive or high-churn files |
||||
.idea/**/dataSources/ |
||||
.idea/**/dataSources.ids |
||||
.idea/**/dataSources.local.xml |
||||
.idea/**/sqlDataSources.xml |
||||
.idea/**/dynamic.xml |
||||
.idea/**/uiDesigner.xml |
||||
.idea/**/dbnavigator.xml |
||||
|
||||
# Gradle |
||||
.idea/**/gradle.xml |
||||
.idea/**/libraries |
||||
|
||||
# Gradle and Maven with auto-import |
||||
# When using Gradle or Maven with auto-import, you should exclude module files, |
||||
# since they will be recreated, and may cause churn. Uncomment if using |
||||
# auto-import. |
||||
# .idea/artifacts |
||||
# .idea/compiler.xml |
||||
# .idea/jarRepositories.xml |
||||
# .idea/modules.xml |
||||
# .idea/*.iml |
||||
# .idea/modules |
||||
# *.iml |
||||
# *.ipr |
||||
|
||||
# CMake |
||||
cmake-build-*/ |
||||
|
||||
# Mongo Explorer plugin |
||||
.idea/**/mongoSettings.xml |
||||
|
||||
# File-based project format |
||||
*.iws |
||||
|
||||
# IntelliJ |
||||
out/ |
||||
|
||||
# mpeltonen/sbt-idea plugin |
||||
.idea_modules/ |
||||
|
||||
# JIRA plugin |
||||
atlassian-ide-plugin.xml |
||||
|
||||
# Cursive Clojure plugin |
||||
.idea/replstate.xml |
||||
|
||||
# Crashlytics plugin (for Android Studio and IntelliJ) |
||||
com_crashlytics_export_strings.xml |
||||
crashlytics.properties |
||||
crashlytics-build.properties |
||||
fabric.properties |
||||
|
||||
# Editor-based Rest Client |
||||
.idea/httpRequests |
||||
|
||||
# Android studio 3.1+ serialized cache file |
||||
.idea/caches/build_file_checksums.ser |
||||
|
@ -0,0 +1,2 @@ |
||||
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.8.1/apache-maven-3.8.1-bin.zip |
||||
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar |
@ -0,0 +1,13 @@ |
||||
# Docker 镜像构建 |
||||
FROM maven:3.8.1-jdk-8-slim as builder |
||||
|
||||
# Copy local code to the container image. |
||||
WORKDIR /app |
||||
COPY pom.xml . |
||||
COPY src ./src |
||||
|
||||
# Build a release artifact. |
||||
RUN mvn package -DskipTests |
||||
|
||||
# Run the web service on container startup. |
||||
CMD ["java","-jar","/app/target/springboot-init-0.0.1-SNAPSHOT.jar","--spring.profiles.active=prod"] |
@ -0,0 +1,158 @@ |
||||
# SpringBoot 项目初始模板 |
||||
|
||||
基于 Java SpringBoot 的项目初始模板,整合了常用框架和主流业务的示例代码。 |
||||
|
||||
只需 1 分钟即可完成内容网站的后端!!!大家还可以在此基础上快速开发自己的项目。 |
||||
|
||||
[toc] |
||||
|
||||
## 模板特点 |
||||
|
||||
### 主流框架 & 特性 |
||||
|
||||
- Spring Boot 2.7.x(贼新) |
||||
- Spring MVC |
||||
- MyBatis + MyBatis Plus 数据访问(开启分页) |
||||
- Spring Boot 调试工具和项目处理器 |
||||
- Spring AOP 切面编程 |
||||
- Spring Scheduler 定时任务 |
||||
- Spring 事务注解 |
||||
|
||||
### 数据存储 |
||||
|
||||
- MySQL 数据库 |
||||
- Redis 内存数据库 |
||||
- Elasticsearch 搜索引擎 |
||||
- 腾讯云 COS 对象存储 |
||||
|
||||
### 工具类 |
||||
|
||||
- Easy Excel 表格处理 |
||||
- Hutool 工具库 |
||||
- Gson 解析库 |
||||
- Apache Commons Lang3 工具类 |
||||
- Lombok 注解 |
||||
|
||||
### 业务特性 |
||||
|
||||
- Spring Session Redis 分布式登录 |
||||
- 全局请求响应拦截器(记录日志) |
||||
- 全局异常处理器 |
||||
- 自定义错误码 |
||||
- 封装通用响应类 |
||||
- Swagger + Knife4j 接口文档 |
||||
- 自定义权限注解 + 全局校验 |
||||
- 全局跨域处理 |
||||
- 长整数丢失精度解决 |
||||
- 多环境配置 |
||||
|
||||
|
||||
## 业务功能 |
||||
|
||||
- 提供示例 SQL(用户、帖子、帖子点赞、帖子收藏表) |
||||
- 用户登录、注册、注销、更新、检索、权限管理 |
||||
- 帖子创建、删除、编辑、更新、数据库检索、ES 灵活检索 |
||||
- 帖子点赞、取消点赞 |
||||
- 帖子收藏、取消收藏、检索已收藏帖子 |
||||
- 帖子全量同步 ES、增量同步 ES 定时任务 |
||||
- 支持微信开放平台登录 |
||||
- 支持微信公众号订阅、收发消息、设置菜单 |
||||
- 支持分业务的文件上传 |
||||
|
||||
### 单元测试 |
||||
|
||||
- JUnit5 单元测试 |
||||
- 示例单元测试类 |
||||
|
||||
### 架构设计 |
||||
|
||||
- 合理分层 |
||||
|
||||
|
||||
### MySQL 数据库 |
||||
|
||||
1)修改 `application.yml` 的数据库配置为你自己的: |
||||
|
||||
```yml |
||||
spring: |
||||
datasource: |
||||
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
url: jdbc:mysql://localhost:3306/my_db |
||||
username: root |
||||
password: 123456 |
||||
``` |
||||
|
||||
2)执行 `sql/create_table.sql` 中的数据库语句,自动创建库表 |
||||
|
||||
3)启动项目,访问 `http://localhost:8101/api/doc.html` 即可打开接口文档,不需要写前端就能在线调试接口了~ |
||||
|
||||
![](doc/swagger.png) |
||||
|
||||
### Redis 分布式登录 |
||||
|
||||
1)修改 `application.yml` 的 Redis 配置为你自己的: |
||||
|
||||
```yml |
||||
spring: |
||||
redis: |
||||
database: 1 |
||||
host: localhost |
||||
port: 6379 |
||||
timeout: 5000 |
||||
password: 123456 |
||||
``` |
||||
|
||||
2)修改 `application.yml` 中的 session 存储方式: |
||||
|
||||
```yml |
||||
spring: |
||||
session: |
||||
store-type: redis |
||||
``` |
||||
|
||||
3)移除 `MainApplication` 类开头 `@SpringBootApplication` 注解内的 exclude 参数: |
||||
|
||||
修改前: |
||||
|
||||
```java |
||||
@SpringBootApplication(exclude = {RedisAutoConfiguration.class}) |
||||
``` |
||||
|
||||
修改后: |
||||
|
||||
|
||||
```java |
||||
@SpringBootApplication |
||||
``` |
||||
|
||||
### Elasticsearch 搜索引擎 |
||||
|
||||
1)修改 `application.yml` 的 Elasticsearch 配置为你自己的: |
||||
|
||||
```yml |
||||
spring: |
||||
elasticsearch: |
||||
uris: http://localhost:9200 |
||||
username: root |
||||
password: 123456 |
||||
``` |
||||
|
||||
2)复制 `sql/post_es_mapping.json` 文件中的内容,通过调用 Elasticsearch 的接口或者 Kibana Dev Tools 来创建索引(相当于数据库建表) |
||||
|
||||
``` |
||||
PUT post_v1 |
||||
{ |
||||
参数见 sql/post_es_mapping.json 文件 |
||||
} |
||||
``` |
||||
|
||||
这步不会操作的话需要补充下 Elasticsearch 的知识,或者自行百度一下~ |
||||
|
||||
3)开启同步任务,将数据库的帖子同步到 Elasticsearch |
||||
|
||||
找到 job 目录下的 `FullSyncPostToEs` 和 `IncSyncPostToEs` 文件,取消掉 `@Component` 注解的注释,再次执行程序即可触发同步: |
||||
|
||||
```java |
||||
// todo 取消注释开启任务 |
||||
//@Component |
||||
``` |
After Width: | Height: | Size: 445 KiB |
@ -0,0 +1,316 @@ |
||||
#!/bin/sh |
||||
# ---------------------------------------------------------------------------- |
||||
# Licensed to the Apache Software Foundation (ASF) under one |
||||
# or more contributor license agreements. See the NOTICE file |
||||
# distributed with this work for additional information |
||||
# regarding copyright ownership. The ASF licenses this file |
||||
# to you under the Apache License, Version 2.0 (the |
||||
# "License"); you may not use this file except in compliance |
||||
# with the License. You may obtain a copy of the License at |
||||
# |
||||
# https://www.apache.org/licenses/LICENSE-2.0 |
||||
# |
||||
# Unless required by applicable law or agreed to in writing, |
||||
# software distributed under the License is distributed on an |
||||
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
# KIND, either express or implied. See the License for the |
||||
# specific language governing permissions and limitations |
||||
# under the License. |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
# ---------------------------------------------------------------------------- |
||||
# Maven Start Up Batch script |
||||
# |
||||
# Required ENV vars: |
||||
# ------------------ |
||||
# JAVA_HOME - location of a JDK home dir |
||||
# |
||||
# Optional ENV vars |
||||
# ----------------- |
||||
# M2_HOME - location of maven2's installed home dir |
||||
# MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
# e.g. to debug Maven itself, use |
||||
# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
# MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
# ---------------------------------------------------------------------------- |
||||
|
||||
if [ -z "$MAVEN_SKIP_RC" ] ; then |
||||
|
||||
if [ -f /usr/local/etc/mavenrc ] ; then |
||||
. /usr/local/etc/mavenrc |
||||
fi |
||||
|
||||
if [ -f /etc/mavenrc ] ; then |
||||
. /etc/mavenrc |
||||
fi |
||||
|
||||
if [ -f "$HOME/.mavenrc" ] ; then |
||||
. "$HOME/.mavenrc" |
||||
fi |
||||
|
||||
fi |
||||
|
||||
# OS specific support. $var _must_ be set to either true or false. |
||||
cygwin=false; |
||||
darwin=false; |
||||
mingw=false |
||||
case "`uname`" in |
||||
CYGWIN*) cygwin=true ;; |
||||
MINGW*) mingw=true;; |
||||
Darwin*) darwin=true |
||||
# Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home |
||||
# See https://developer.apple.com/library/mac/qa/qa1170/_index.html |
||||
if [ -z "$JAVA_HOME" ]; then |
||||
if [ -x "/usr/libexec/java_home" ]; then |
||||
export JAVA_HOME="`/usr/libexec/java_home`" |
||||
else |
||||
export JAVA_HOME="/Library/Java/Home" |
||||
fi |
||||
fi |
||||
;; |
||||
esac |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
if [ -r /etc/gentoo-release ] ; then |
||||
JAVA_HOME=`java-config --jre-home` |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$M2_HOME" ] ; then |
||||
## resolve links - $0 may be a link to maven's home |
||||
PRG="$0" |
||||
|
||||
# need this for relative symlinks |
||||
while [ -h "$PRG" ] ; do |
||||
ls=`ls -ld "$PRG"` |
||||
link=`expr "$ls" : '.*-> \(.*\)$'` |
||||
if expr "$link" : '/.*' > /dev/null; then |
||||
PRG="$link" |
||||
else |
||||
PRG="`dirname "$PRG"`/$link" |
||||
fi |
||||
done |
||||
|
||||
saveddir=`pwd` |
||||
|
||||
M2_HOME=`dirname "$PRG"`/.. |
||||
|
||||
# make it fully qualified |
||||
M2_HOME=`cd "$M2_HOME" && pwd` |
||||
|
||||
cd "$saveddir" |
||||
# echo Using m2 at $M2_HOME |
||||
fi |
||||
|
||||
# For Cygwin, ensure paths are in UNIX format before anything is touched |
||||
if $cygwin ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --unix "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --unix "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --unix "$CLASSPATH"` |
||||
fi |
||||
|
||||
# For Mingw, ensure paths are in UNIX format before anything is touched |
||||
if $mingw ; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME="`(cd "$M2_HOME"; pwd)`" |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ]; then |
||||
javaExecutable="`which javac`" |
||||
if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then |
||||
# readlink(1) is not available as standard on Solaris 10. |
||||
readLink=`which readlink` |
||||
if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then |
||||
if $darwin ; then |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" |
||||
else |
||||
javaExecutable="`readlink -f \"$javaExecutable\"`" |
||||
fi |
||||
javaHome="`dirname \"$javaExecutable\"`" |
||||
javaHome=`expr "$javaHome" : '\(.*\)/bin'` |
||||
JAVA_HOME="$javaHome" |
||||
export JAVA_HOME |
||||
fi |
||||
fi |
||||
fi |
||||
|
||||
if [ -z "$JAVACMD" ] ; then |
||||
if [ -n "$JAVA_HOME" ] ; then |
||||
if [ -x "$JAVA_HOME/jre/sh/java" ] ; then |
||||
# IBM's JDK on AIX uses strange locations for the executables |
||||
JAVACMD="$JAVA_HOME/jre/sh/java" |
||||
else |
||||
JAVACMD="$JAVA_HOME/bin/java" |
||||
fi |
||||
else |
||||
JAVACMD="`\\unset -f command; \\command -v java`" |
||||
fi |
||||
fi |
||||
|
||||
if [ ! -x "$JAVACMD" ] ; then |
||||
echo "Error: JAVA_HOME is not defined correctly." >&2 |
||||
echo " We cannot execute $JAVACMD" >&2 |
||||
exit 1 |
||||
fi |
||||
|
||||
if [ -z "$JAVA_HOME" ] ; then |
||||
echo "Warning: JAVA_HOME environment variable is not set." |
||||
fi |
||||
|
||||
CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher |
||||
|
||||
# traverses directory structure from process work directory to filesystem root |
||||
# first directory with .mvn subdirectory is considered project base directory |
||||
find_maven_basedir() { |
||||
|
||||
if [ -z "$1" ] |
||||
then |
||||
echo "Path not specified to find_maven_basedir" |
||||
return 1 |
||||
fi |
||||
|
||||
basedir="$1" |
||||
wdir="$1" |
||||
while [ "$wdir" != '/' ] ; do |
||||
if [ -d "$wdir"/.mvn ] ; then |
||||
basedir=$wdir |
||||
break |
||||
fi |
||||
# workaround for JBEAP-8937 (on Solaris 10/Sparc) |
||||
if [ -d "${wdir}" ]; then |
||||
wdir=`cd "$wdir/.."; pwd` |
||||
fi |
||||
# end of workaround |
||||
done |
||||
echo "${basedir}" |
||||
} |
||||
|
||||
# concatenates all lines of a file |
||||
concat_lines() { |
||||
if [ -f "$1" ]; then |
||||
echo "$(tr -s '\n' ' ' < "$1")" |
||||
fi |
||||
} |
||||
|
||||
BASE_DIR=`find_maven_basedir "$(pwd)"` |
||||
if [ -z "$BASE_DIR" ]; then |
||||
exit 1; |
||||
fi |
||||
|
||||
########################################################################################## |
||||
# Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
# This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
########################################################################################## |
||||
if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found .mvn/wrapper/maven-wrapper.jar" |
||||
fi |
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." |
||||
fi |
||||
if [ -n "$MVNW_REPOURL" ]; then |
||||
jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" |
||||
else |
||||
jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" |
||||
fi |
||||
while IFS="=" read key value; do |
||||
case "$key" in (wrapperUrl) jarUrl="$value"; break ;; |
||||
esac |
||||
done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Downloading from: $jarUrl" |
||||
fi |
||||
wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" |
||||
if $cygwin; then |
||||
wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` |
||||
fi |
||||
|
||||
if command -v wget > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found wget ... using wget" |
||||
fi |
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then |
||||
wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" |
||||
else |
||||
wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" |
||||
fi |
||||
elif command -v curl > /dev/null; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Found curl ... using curl" |
||||
fi |
||||
if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then |
||||
curl -o "$wrapperJarPath" "$jarUrl" -f |
||||
else |
||||
curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f |
||||
fi |
||||
|
||||
else |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo "Falling back to using Java to download" |
||||
fi |
||||
javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" |
||||
# For Cygwin, switch paths to Windows format before running javac |
||||
if $cygwin; then |
||||
javaClass=`cygpath --path --windows "$javaClass"` |
||||
fi |
||||
if [ -e "$javaClass" ]; then |
||||
if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Compiling MavenWrapperDownloader.java ..." |
||||
fi |
||||
# Compiling the Java class |
||||
("$JAVA_HOME/bin/javac" "$javaClass") |
||||
fi |
||||
if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then |
||||
# Running the downloader |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo " - Running MavenWrapperDownloader.java ..." |
||||
fi |
||||
("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") |
||||
fi |
||||
fi |
||||
fi |
||||
fi |
||||
########################################################################################## |
||||
# End of extension |
||||
########################################################################################## |
||||
|
||||
export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} |
||||
if [ "$MVNW_VERBOSE" = true ]; then |
||||
echo $MAVEN_PROJECTBASEDIR |
||||
fi |
||||
MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" |
||||
|
||||
# For Cygwin, switch paths to Windows format before running java |
||||
if $cygwin; then |
||||
[ -n "$M2_HOME" ] && |
||||
M2_HOME=`cygpath --path --windows "$M2_HOME"` |
||||
[ -n "$JAVA_HOME" ] && |
||||
JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` |
||||
[ -n "$CLASSPATH" ] && |
||||
CLASSPATH=`cygpath --path --windows "$CLASSPATH"` |
||||
[ -n "$MAVEN_PROJECTBASEDIR" ] && |
||||
MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` |
||||
fi |
||||
|
||||
# Provide a "standardized" way to retrieve the CLI args that will |
||||
# work with both Windows and non-Windows executions. |
||||
MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" |
||||
export MAVEN_CMD_LINE_ARGS |
||||
|
||||
WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
exec "$JAVACMD" \ |
||||
$MAVEN_OPTS \ |
||||
$MAVEN_DEBUG_OPTS \ |
||||
-classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ |
||||
"-Dmaven.home=${M2_HOME}" \ |
||||
"-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ |
||||
${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" |
@ -0,0 +1,188 @@ |
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Licensed to the Apache Software Foundation (ASF) under one |
||||
@REM or more contributor license agreements. See the NOTICE file |
||||
@REM distributed with this work for additional information |
||||
@REM regarding copyright ownership. The ASF licenses this file |
||||
@REM to you under the Apache License, Version 2.0 (the |
||||
@REM "License"); you may not use this file except in compliance |
||||
@REM with the License. You may obtain a copy of the License at |
||||
@REM |
||||
@REM https://www.apache.org/licenses/LICENSE-2.0 |
||||
@REM |
||||
@REM Unless required by applicable law or agreed to in writing, |
||||
@REM software distributed under the License is distributed on an |
||||
@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
||||
@REM KIND, either express or implied. See the License for the |
||||
@REM specific language governing permissions and limitations |
||||
@REM under the License. |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM ---------------------------------------------------------------------------- |
||||
@REM Maven Start Up Batch script |
||||
@REM |
||||
@REM Required ENV vars: |
||||
@REM JAVA_HOME - location of a JDK home dir |
||||
@REM |
||||
@REM Optional ENV vars |
||||
@REM M2_HOME - location of maven2's installed home dir |
||||
@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands |
||||
@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending |
||||
@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven |
||||
@REM e.g. to debug Maven itself, use |
||||
@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 |
||||
@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files |
||||
@REM ---------------------------------------------------------------------------- |
||||
|
||||
@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' |
||||
@echo off |
||||
@REM set title of command window |
||||
title %0 |
||||
@REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' |
||||
@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% |
||||
|
||||
@REM set %HOME% to equivalent of $HOME |
||||
if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") |
||||
|
||||
@REM Execute a user defined script before this one |
||||
if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre |
||||
@REM check for pre script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* |
||||
if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* |
||||
:skipRcPre |
||||
|
||||
@setlocal |
||||
|
||||
set ERROR_CODE=0 |
||||
|
||||
@REM To isolate internal variables from possible post scripts, we use another setlocal |
||||
@setlocal |
||||
|
||||
@REM ==== START VALIDATION ==== |
||||
if not "%JAVA_HOME%" == "" goto OkJHome |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME not found in your environment. >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
:OkJHome |
||||
if exist "%JAVA_HOME%\bin\java.exe" goto init |
||||
|
||||
echo. |
||||
echo Error: JAVA_HOME is set to an invalid directory. >&2 |
||||
echo JAVA_HOME = "%JAVA_HOME%" >&2 |
||||
echo Please set the JAVA_HOME variable in your environment to match the >&2 |
||||
echo location of your Java installation. >&2 |
||||
echo. |
||||
goto error |
||||
|
||||
@REM ==== END VALIDATION ==== |
||||
|
||||
:init |
||||
|
||||
@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". |
||||
@REM Fallback to current working directory if not found. |
||||
|
||||
set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% |
||||
IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir |
||||
|
||||
set EXEC_DIR=%CD% |
||||
set WDIR=%EXEC_DIR% |
||||
:findBaseDir |
||||
IF EXIST "%WDIR%"\.mvn goto baseDirFound |
||||
cd .. |
||||
IF "%WDIR%"=="%CD%" goto baseDirNotFound |
||||
set WDIR=%CD% |
||||
goto findBaseDir |
||||
|
||||
:baseDirFound |
||||
set MAVEN_PROJECTBASEDIR=%WDIR% |
||||
cd "%EXEC_DIR%" |
||||
goto endDetectBaseDir |
||||
|
||||
:baseDirNotFound |
||||
set MAVEN_PROJECTBASEDIR=%EXEC_DIR% |
||||
cd "%EXEC_DIR%" |
||||
|
||||
:endDetectBaseDir |
||||
|
||||
IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig |
||||
|
||||
@setlocal EnableExtensions EnableDelayedExpansion |
||||
for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a |
||||
@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% |
||||
|
||||
:endReadAdditionalConfig |
||||
|
||||
SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" |
||||
set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" |
||||
set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain |
||||
|
||||
set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" |
||||
|
||||
FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( |
||||
IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B |
||||
) |
||||
|
||||
@REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central |
||||
@REM This allows using the maven wrapper in projects that prohibit checking in binary data. |
||||
if exist %WRAPPER_JAR% ( |
||||
if "%MVNW_VERBOSE%" == "true" ( |
||||
echo Found %WRAPPER_JAR% |
||||
) |
||||
) else ( |
||||
if not "%MVNW_REPOURL%" == "" ( |
||||
SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" |
||||
) |
||||
if "%MVNW_VERBOSE%" == "true" ( |
||||
echo Couldn't find %WRAPPER_JAR%, downloading it ... |
||||
echo Downloading from: %DOWNLOAD_URL% |
||||
) |
||||
|
||||
powershell -Command "&{"^ |
||||
"$webclient = new-object System.Net.WebClient;"^ |
||||
"if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ |
||||
"$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ |
||||
"}"^ |
||||
"[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ |
||||
"}" |
||||
if "%MVNW_VERBOSE%" == "true" ( |
||||
echo Finished downloading %WRAPPER_JAR% |
||||
) |
||||
) |
||||
@REM End of extension |
||||
|
||||
@REM Provide a "standardized" way to retrieve the CLI args that will |
||||
@REM work with both Windows and non-Windows executions. |
||||
set MAVEN_CMD_LINE_ARGS=%* |
||||
|
||||
%MAVEN_JAVA_EXE% ^ |
||||
%JVM_CONFIG_MAVEN_PROPS% ^ |
||||
%MAVEN_OPTS% ^ |
||||
%MAVEN_DEBUG_OPTS% ^ |
||||
-classpath %WRAPPER_JAR% ^ |
||||
"-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ |
||||
%WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* |
||||
if ERRORLEVEL 1 goto error |
||||
goto end |
||||
|
||||
:error |
||||
set ERROR_CODE=1 |
||||
|
||||
:end |
||||
@endlocal & set ERROR_CODE=%ERROR_CODE% |
||||
|
||||
if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost |
||||
@REM check for post script, once with legacy .bat ending and once with .cmd ending |
||||
if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" |
||||
if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" |
||||
:skipRcPost |
||||
|
||||
@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' |
||||
if "%MAVEN_BATCH_PAUSE%"=="on" pause |
||||
|
||||
if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% |
||||
|
||||
cmd /C exit /B %ERROR_CODE% |
@ -0,0 +1,143 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<project xmlns="http://maven.apache.org/POM/4.0.0" |
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> |
||||
<modelVersion>4.0.0</modelVersion> |
||||
<parent> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-parent</artifactId> |
||||
<version>2.7.2</version> |
||||
<relativePath/> <!-- lookup parent from repository --> |
||||
</parent> |
||||
<groupId>com.yupi</groupId> |
||||
<artifactId>springboot-init</artifactId> |
||||
<version>0.0.1-SNAPSHOT</version> |
||||
<name>springboot-init</name> |
||||
<properties> |
||||
<java.version>1.8</java.version> |
||||
</properties> |
||||
<dependencies> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-freemarker</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-web</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-aop</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.mybatis.spring.boot</groupId> |
||||
<artifactId>mybatis-spring-boot-starter</artifactId> |
||||
<version>2.2.2</version> |
||||
</dependency> |
||||
<!-- https://mvnrepository.com/artifact/com.baomidou/mybatis-plus-boot-starter --> |
||||
<dependency> |
||||
<groupId>com.baomidou</groupId> |
||||
<artifactId>mybatis-plus-boot-starter</artifactId> |
||||
<version>3.5.2</version> |
||||
</dependency> |
||||
<!-- redis --> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-data-redis</artifactId> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.session</groupId> |
||||
<artifactId>spring-session-data-redis</artifactId> |
||||
</dependency> |
||||
<!-- elasticsearch--> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-data-elasticsearch</artifactId> |
||||
</dependency> |
||||
<!-- https://mvnrepository.com/artifact/com.github.binarywang/wx-java-mp-spring-boot-starter --> |
||||
<dependency> |
||||
<groupId>com.github.binarywang</groupId> |
||||
<artifactId>wx-java-mp-spring-boot-starter</artifactId> |
||||
<version>4.4.0</version> |
||||
</dependency> |
||||
<!-- https://doc.xiaominfo.com/knife4j/documentation/get_start.html--> |
||||
<dependency> |
||||
<groupId>com.github.xiaoymin</groupId> |
||||
<artifactId>knife4j-spring-boot-starter</artifactId> |
||||
<version>3.0.3</version> |
||||
</dependency> |
||||
<!-- https://cloud.tencent.com/document/product/436/10199--> |
||||
<dependency> |
||||
<groupId>com.qcloud</groupId> |
||||
<artifactId>cos_api</artifactId> |
||||
<version>5.6.89</version> |
||||
</dependency> |
||||
<!-- https://mvnrepository.com/artifact/org.apache.commons/commons-lang3 --> |
||||
<dependency> |
||||
<groupId>org.apache.commons</groupId> |
||||
<artifactId>commons-lang3</artifactId> |
||||
</dependency> |
||||
<!-- https://mvnrepository.com/artifact/com.google.code.gson/gson --> |
||||
<dependency> |
||||
<groupId>com.google.code.gson</groupId> |
||||
<artifactId>gson</artifactId> |
||||
<version>2.9.1</version> |
||||
</dependency> |
||||
<!-- https://github.com/alibaba/easyexcel --> |
||||
<dependency> |
||||
<groupId>com.alibaba</groupId> |
||||
<artifactId>easyexcel</artifactId> |
||||
<version>3.1.1</version> |
||||
</dependency> |
||||
<!-- https://hutool.cn/docs/index.html#/--> |
||||
<dependency> |
||||
<groupId>cn.hutool</groupId> |
||||
<artifactId>hutool-all</artifactId> |
||||
<version>5.8.8</version> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-devtools</artifactId> |
||||
<scope>runtime</scope> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>mysql</groupId> |
||||
<artifactId>mysql-connector-java</artifactId> |
||||
<scope>runtime</scope> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-configuration-processor</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
<optional>true</optional> |
||||
</dependency> |
||||
<dependency> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-starter-test</artifactId> |
||||
<scope>test</scope> |
||||
</dependency> |
||||
</dependencies> |
||||
|
||||
<build> |
||||
<plugins> |
||||
<plugin> |
||||
<groupId>org.springframework.boot</groupId> |
||||
<artifactId>spring-boot-maven-plugin</artifactId> |
||||
<configuration> |
||||
<excludes> |
||||
<exclude> |
||||
<groupId>org.projectlombok</groupId> |
||||
<artifactId>lombok</artifactId> |
||||
</exclude> |
||||
</excludes> |
||||
</configuration> |
||||
</plugin> |
||||
</plugins> |
||||
</build> |
||||
|
||||
</project> |
@ -0,0 +1,65 @@ |
||||
# 数据库初始化 |
||||
|
||||
-- 创建库 |
||||
create database if not exists my_db; |
||||
|
||||
-- 切换库 |
||||
use my_db; |
||||
|
||||
-- 用户表 |
||||
create table if not exists user |
||||
( |
||||
id bigint auto_increment comment 'id' primary key, |
||||
userAccount varchar(256) not null comment '账号', |
||||
userPassword varchar(512) not null comment '密码', |
||||
unionId varchar(256) null comment '微信开放平台id', |
||||
mpOpenId varchar(256) null comment '公众号openId', |
||||
userName varchar(256) null comment '用户昵称', |
||||
userAvatar varchar(1024) null comment '用户头像', |
||||
userProfile varchar(512) null comment '用户简介', |
||||
userRole varchar(256) default 'user' not null comment '用户角色:user/admin/ban', |
||||
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', |
||||
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', |
||||
isDelete tinyint default 0 not null comment '是否删除', |
||||
index idx_unionId (unionId) |
||||
) comment '用户' collate = utf8mb4_unicode_ci; |
||||
|
||||
-- 帖子表 |
||||
create table if not exists post |
||||
( |
||||
id bigint auto_increment comment 'id' primary key, |
||||
title varchar(512) null comment '标题', |
||||
content text null comment '内容', |
||||
tags varchar(1024) null comment '标签列表(json 数组)', |
||||
thumbNum int default 0 not null comment '点赞数', |
||||
favourNum int default 0 not null comment '收藏数', |
||||
userId bigint not null comment '创建用户 id', |
||||
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', |
||||
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', |
||||
isDelete tinyint default 0 not null comment '是否删除', |
||||
index idx_userId (userId) |
||||
) comment '帖子' collate = utf8mb4_unicode_ci; |
||||
|
||||
-- 帖子点赞表(硬删除) |
||||
create table if not exists post_thumb |
||||
( |
||||
id bigint auto_increment comment 'id' primary key, |
||||
postId bigint not null comment '帖子 id', |
||||
userId bigint not null comment '创建用户 id', |
||||
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', |
||||
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', |
||||
index idx_postId (postId), |
||||
index idx_userId (userId) |
||||
) comment '帖子点赞'; |
||||
|
||||
-- 帖子收藏表(硬删除) |
||||
create table if not exists post_favour |
||||
( |
||||
id bigint auto_increment comment 'id' primary key, |
||||
postId bigint not null comment '帖子 id', |
||||
userId bigint not null comment '创建用户 id', |
||||
createTime datetime default CURRENT_TIMESTAMP not null comment '创建时间', |
||||
updateTime datetime default CURRENT_TIMESTAMP not null on update CURRENT_TIMESTAMP comment '更新时间', |
||||
index idx_postId (postId), |
||||
index idx_userId (userId) |
||||
) comment '帖子收藏'; |
@ -0,0 +1,52 @@ |
||||
{ |
||||
"aliases": { |
||||
"post": {} |
||||
}, |
||||
"mappings": { |
||||
"properties": { |
||||
"title": { |
||||
"type": "text", |
||||
"analyzer": "ik_max_word", |
||||
"search_analyzer": "ik_smart", |
||||
"fields": { |
||||
"keyword": { |
||||
"type": "keyword", |
||||
"ignore_above": 256 |
||||
} |
||||
} |
||||
}, |
||||
"content": { |
||||
"type": "text", |
||||
"analyzer": "ik_max_word", |
||||
"search_analyzer": "ik_smart", |
||||
"fields": { |
||||
"keyword": { |
||||
"type": "keyword", |
||||
"ignore_above": 256 |
||||
} |
||||
} |
||||
}, |
||||
"tags": { |
||||
"type": "keyword" |
||||
}, |
||||
"thumbNum": { |
||||
"type": "long" |
||||
}, |
||||
"favourNum": { |
||||
"type": "long" |
||||
}, |
||||
"userId": { |
||||
"type": "keyword" |
||||
}, |
||||
"createTime": { |
||||
"type": "date" |
||||
}, |
||||
"updateTime": { |
||||
"type": "date" |
||||
}, |
||||
"isDelete": { |
||||
"type": "keyword" |
||||
} |
||||
} |
||||
} |
||||
} |
Binary file not shown.
@ -0,0 +1,25 @@ |
||||
package cc.bnblogs.springbootinit; |
||||
|
||||
import org.mybatis.spring.annotation.MapperScan; |
||||
import org.springframework.boot.SpringApplication; |
||||
import org.springframework.boot.autoconfigure.SpringBootApplication; |
||||
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration; |
||||
import org.springframework.context.annotation.EnableAspectJAutoProxy; |
||||
import org.springframework.scheduling.annotation.EnableScheduling; |
||||
|
||||
/** |
||||
* 主类(项目启动入口) |
||||
*/ |
||||
// todo 如需开启 Redis,须移除 exclude 中的内容
|
||||
//@SpringBootApplication(exclude = {RedisAutoConfiguration.class})
|
||||
@SpringBootApplication |
||||
@MapperScan("cc.bnblogs.springbootinit.mapper") |
||||
@EnableScheduling |
||||
@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) |
||||
public class MainApplication { |
||||
|
||||
public static void main(String[] args) { |
||||
SpringApplication.run(MainApplication.class, args); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,23 @@ |
||||
package cc.bnblogs.springbootinit.annotation; |
||||
|
||||
import java.lang.annotation.ElementType; |
||||
import java.lang.annotation.Retention; |
||||
import java.lang.annotation.RetentionPolicy; |
||||
import java.lang.annotation.Target; |
||||
|
||||
/** |
||||
* 权限校验 |
||||
*/ |
||||
@Target(ElementType.METHOD) |
||||
@Retention(RetentionPolicy.RUNTIME) |
||||
public @interface AuthCheck { |
||||
|
||||
/** |
||||
* 必须有某个角色 |
||||
* |
||||
* @return |
||||
*/ |
||||
String mustRole() default ""; |
||||
|
||||
} |
||||
|
@ -0,0 +1,66 @@ |
||||
package cc.bnblogs.springbootinit.aop; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.enums.UserRoleEnum; |
||||
import cc.bnblogs.springbootinit.annotation.AuthCheck; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.web.context.request.RequestAttributes; |
||||
import org.springframework.web.context.request.RequestContextHolder; |
||||
import org.springframework.web.context.request.ServletRequestAttributes; |
||||
|
||||
/** |
||||
* 权限校验 AOP |
||||
*/ |
||||
@Aspect |
||||
@Component |
||||
public class AuthInterceptor { |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
/** |
||||
* 执行拦截 |
||||
* |
||||
* @param joinPoint |
||||
* @param authCheck |
||||
* @return |
||||
*/ |
||||
@Around("@annotation(authCheck)") |
||||
public Object doInterceptor(ProceedingJoinPoint joinPoint, AuthCheck authCheck) throws Throwable { |
||||
String mustRole = authCheck.mustRole(); |
||||
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); |
||||
HttpServletRequest request = ((ServletRequestAttributes) requestAttributes).getRequest(); |
||||
// 当前登录用户
|
||||
User loginUser = userService.getLoginUser(request); |
||||
// 必须有该权限才通过
|
||||
if (StringUtils.isNotBlank(mustRole)) { |
||||
UserRoleEnum mustUserRoleEnum = UserRoleEnum.getEnumByValue(mustRole); |
||||
if (mustUserRoleEnum == null) { |
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); |
||||
} |
||||
String userRole = loginUser.getUserRole(); |
||||
// 如果被封号,直接拒绝
|
||||
if (UserRoleEnum.BAN.equals(mustUserRoleEnum)) { |
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); |
||||
} |
||||
// 必须有管理员权限
|
||||
if (UserRoleEnum.ADMIN.equals(mustUserRoleEnum)) { |
||||
if (!mustRole.equals(userRole)) { |
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); |
||||
} |
||||
} |
||||
} |
||||
// 通过权限校验,放行
|
||||
return joinPoint.proceed(); |
||||
} |
||||
} |
||||
|
@ -0,0 +1,53 @@ |
||||
package cc.bnblogs.springbootinit.aop; |
||||
|
||||
import java.util.UUID; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.aspectj.lang.ProceedingJoinPoint; |
||||
import org.aspectj.lang.annotation.Around; |
||||
import org.aspectj.lang.annotation.Aspect; |
||||
import org.springframework.stereotype.Component; |
||||
import org.springframework.util.StopWatch; |
||||
import org.springframework.web.context.request.RequestAttributes; |
||||
import org.springframework.web.context.request.RequestContextHolder; |
||||
import org.springframework.web.context.request.ServletRequestAttributes; |
||||
|
||||
/** |
||||
* 请求响应日志 AOP |
||||
**/ |
||||
@Aspect |
||||
@Component |
||||
@Slf4j |
||||
public class LogInterceptor { |
||||
|
||||
/** |
||||
* 执行拦截 |
||||
*/ |
||||
@Around("execution(* com.yupi.springbootinit.controller.*.*(..))") |
||||
public Object doInterceptor(ProceedingJoinPoint point) throws Throwable { |
||||
// 计时
|
||||
StopWatch stopWatch = new StopWatch(); |
||||
stopWatch.start(); |
||||
// 获取请求路径
|
||||
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); |
||||
HttpServletRequest httpServletRequest = ((ServletRequestAttributes) requestAttributes).getRequest(); |
||||
// 生成请求唯一 id
|
||||
String requestId = UUID.randomUUID().toString(); |
||||
String url = httpServletRequest.getRequestURI(); |
||||
// 获取请求参数
|
||||
Object[] args = point.getArgs(); |
||||
String reqParam = "[" + StringUtils.join(args, ", ") + "]"; |
||||
// 输出请求日志
|
||||
log.info("request start,id: {}, path: {}, ip: {}, params: {}", requestId, url, |
||||
httpServletRequest.getRemoteHost(), reqParam); |
||||
// 执行原方法
|
||||
Object result = point.proceed(); |
||||
// 输出响应日志
|
||||
stopWatch.stop(); |
||||
long totalTimeMillis = stopWatch.getTotalTimeMillis(); |
||||
log.info("request end, id: {}, cost: {}ms", requestId, totalTimeMillis); |
||||
return result; |
||||
} |
||||
} |
||||
|
@ -0,0 +1,31 @@ |
||||
package cc.bnblogs.springbootinit.common; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 通用返回类 |
||||
*/ |
||||
@Data |
||||
public class BaseResponse<T> implements Serializable { |
||||
|
||||
private int code; |
||||
|
||||
private T data; |
||||
|
||||
private String message; |
||||
|
||||
public BaseResponse(int code, T data, String message) { |
||||
this.code = code; |
||||
this.data = data; |
||||
this.message = message; |
||||
} |
||||
|
||||
public BaseResponse(int code, T data) { |
||||
this(code, data, ""); |
||||
} |
||||
|
||||
public BaseResponse(ErrorCode errorCode) { |
||||
this(errorCode.getCode(), null, errorCode.getMessage()); |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.common; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 删除请求 |
||||
*/ |
||||
@Data |
||||
public class DeleteRequest implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,40 @@ |
||||
package cc.bnblogs.springbootinit.common; |
||||
|
||||
/** |
||||
* 自定义错误码 |
||||
*/ |
||||
public enum ErrorCode { |
||||
|
||||
SUCCESS(0, "ok"), |
||||
PARAMS_ERROR(40000, "请求参数错误"), |
||||
NOT_LOGIN_ERROR(40100, "未登录"), |
||||
NO_AUTH_ERROR(40101, "无权限"), |
||||
NOT_FOUND_ERROR(40400, "请求数据不存在"), |
||||
FORBIDDEN_ERROR(40300, "禁止访问"), |
||||
SYSTEM_ERROR(50000, "系统内部异常"), |
||||
OPERATION_ERROR(50001, "操作失败"); |
||||
|
||||
/** |
||||
* 状态码 |
||||
*/ |
||||
private final int code; |
||||
|
||||
/** |
||||
* 信息 |
||||
*/ |
||||
private final String message; |
||||
|
||||
ErrorCode(int code, String message) { |
||||
this.code = code; |
||||
this.message = message; |
||||
} |
||||
|
||||
public int getCode() { |
||||
return code; |
||||
} |
||||
|
||||
public String getMessage() { |
||||
return message; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,31 @@ |
||||
package cc.bnblogs.springbootinit.common; |
||||
|
||||
import cc.bnblogs.springbootinit.constant.CommonConstant; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 分页请求 |
||||
*/ |
||||
@Data |
||||
public class PageRequest { |
||||
|
||||
/** |
||||
* 当前页号 |
||||
*/ |
||||
private long current = 1; |
||||
|
||||
/** |
||||
* 页面大小 |
||||
*/ |
||||
private long pageSize = 10; |
||||
|
||||
/** |
||||
* 排序字段 |
||||
*/ |
||||
private String sortField; |
||||
|
||||
/** |
||||
* 排序顺序(默认升序) |
||||
*/ |
||||
private String sortOrder = CommonConstant.SORT_ORDER_ASC; |
||||
} |
@ -0,0 +1,49 @@ |
||||
package cc.bnblogs.springbootinit.common; |
||||
|
||||
/** |
||||
* 返回工具类 |
||||
*/ |
||||
public class ResultUtils { |
||||
|
||||
/** |
||||
* 成功 |
||||
* |
||||
* @param data |
||||
* @param <T> |
||||
* @return |
||||
*/ |
||||
public static <T> BaseResponse<T> success(T data) { |
||||
return new BaseResponse<>(0, data, "ok"); |
||||
} |
||||
|
||||
/** |
||||
* 失败 |
||||
* |
||||
* @param errorCode |
||||
* @return |
||||
*/ |
||||
public static BaseResponse error(ErrorCode errorCode) { |
||||
return new BaseResponse<>(errorCode); |
||||
} |
||||
|
||||
/** |
||||
* 失败 |
||||
* |
||||
* @param code |
||||
* @param message |
||||
* @return |
||||
*/ |
||||
public static BaseResponse error(int code, String message) { |
||||
return new BaseResponse(code, null, message); |
||||
} |
||||
|
||||
/** |
||||
* 失败 |
||||
* |
||||
* @param errorCode |
||||
* @return |
||||
*/ |
||||
public static BaseResponse error(ErrorCode errorCode, String message) { |
||||
return new BaseResponse(errorCode.getCode(), null, message); |
||||
} |
||||
} |
@ -0,0 +1,25 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry; |
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; |
||||
|
||||
/** |
||||
* 全局跨域配置 |
||||
*/ |
||||
@Configuration |
||||
public class CorsConfig implements WebMvcConfigurer { |
||||
|
||||
@Override |
||||
public void addCorsMappings(CorsRegistry registry) { |
||||
// 覆盖所有请求
|
||||
registry.addMapping("/**") |
||||
// 允许发送 Cookie
|
||||
.allowCredentials(true) |
||||
// 放行哪些域名(必须用 patterns,否则 * 会和 allowCredentials 冲突)
|
||||
.allowedOriginPatterns("*") |
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS") |
||||
.allowedHeaders("*") |
||||
.exposedHeaders("*"); |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import com.qcloud.cos.COSClient; |
||||
import com.qcloud.cos.ClientConfig; |
||||
import com.qcloud.cos.auth.BasicCOSCredentials; |
||||
import com.qcloud.cos.auth.COSCredentials; |
||||
import com.qcloud.cos.region.Region; |
||||
import lombok.Data; |
||||
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* 腾讯云对象存储客户端 |
||||
*/ |
||||
@Configuration |
||||
@ConfigurationProperties(prefix = "cos.client") |
||||
@Data |
||||
public class CosClientConfig { |
||||
|
||||
/** |
||||
* accessKey |
||||
*/ |
||||
private String accessKey; |
||||
|
||||
/** |
||||
* secretKey |
||||
*/ |
||||
private String secretKey; |
||||
|
||||
/** |
||||
* 区域 |
||||
*/ |
||||
private String region; |
||||
|
||||
/** |
||||
* 桶名 |
||||
*/ |
||||
private String bucket; |
||||
|
||||
@Bean |
||||
public COSClient cosClient() { |
||||
// 初始化用户身份信息(secretId, secretKey)
|
||||
COSCredentials cred = new BasicCOSCredentials(accessKey, secretKey); |
||||
// 设置bucket的区域, COS地域的简称请参照 https://www.qcloud.com/document/product/436/6224
|
||||
ClientConfig clientConfig = new ClientConfig(new Region(region)); |
||||
// 生成cos客户端
|
||||
return new COSClient(cred, clientConfig); |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper; |
||||
import com.fasterxml.jackson.databind.module.SimpleModule; |
||||
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer; |
||||
import org.springframework.boot.jackson.JsonComponent; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; |
||||
|
||||
/** |
||||
* Spring MVC Json 配置 |
||||
*/ |
||||
@JsonComponent |
||||
public class JsonConfig { |
||||
|
||||
/** |
||||
* 添加 Long 转 json 精度丢失的配置 |
||||
*/ |
||||
@Bean |
||||
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { |
||||
ObjectMapper objectMapper = builder.createXmlMapper(false).build(); |
||||
SimpleModule module = new SimpleModule(); |
||||
module.addSerializer(Long.class, ToStringSerializer.instance); |
||||
module.addSerializer(Long.TYPE, ToStringSerializer.instance); |
||||
objectMapper.registerModule(module); |
||||
return objectMapper; |
||||
} |
||||
} |
@ -0,0 +1,36 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
import org.springframework.context.annotation.Profile; |
||||
import springfox.documentation.builders.ApiInfoBuilder; |
||||
import springfox.documentation.builders.PathSelectors; |
||||
import springfox.documentation.builders.RequestHandlerSelectors; |
||||
import springfox.documentation.spi.DocumentationType; |
||||
import springfox.documentation.spring.web.plugins.Docket; |
||||
import springfox.documentation.swagger2.annotations.EnableSwagger2; |
||||
|
||||
/** |
||||
* Knife4j 接口文档配置 |
||||
* https://doc.xiaominfo.com/knife4j/documentation/get_start.html
|
||||
*/ |
||||
@Configuration |
||||
@EnableSwagger2 |
||||
@Profile({"dev", "test"}) |
||||
public class Knife4jConfig { |
||||
|
||||
@Bean |
||||
public Docket defaultApi2() { |
||||
return new Docket(DocumentationType.SWAGGER_2) |
||||
.apiInfo(new ApiInfoBuilder() |
||||
.title("接口文档") |
||||
.description("springboot-init") |
||||
.version("1.0") |
||||
.build()) |
||||
.select() |
||||
// 指定 Controller 扫描包路径
|
||||
.apis(RequestHandlerSelectors.basePackage("cc.bnblogs.springbootinit.controller")) |
||||
.paths(PathSelectors.any()) |
||||
.build(); |
||||
} |
||||
} |
@ -0,0 +1,31 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.DbType; |
||||
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor; |
||||
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor; |
||||
import org.mybatis.spring.annotation.MapperScan; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* MyBatis Plus 配置 |
||||
* |
||||
* @author https://github.com/liyupi
|
||||
*/ |
||||
@Configuration |
||||
@MapperScan("com.yupi.springbootinit.mapper") |
||||
public class MyBatisPlusConfig { |
||||
|
||||
/** |
||||
* 拦截器配置 |
||||
* |
||||
* @return |
||||
*/ |
||||
@Bean |
||||
public MybatisPlusInterceptor mybatisPlusInterceptor() { |
||||
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); |
||||
// 分页插件
|
||||
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); |
||||
return interceptor; |
||||
} |
||||
} |
@ -0,0 +1,48 @@ |
||||
package cc.bnblogs.springbootinit.config; |
||||
|
||||
import lombok.Data; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import me.chanjar.weixin.mp.api.impl.WxMpServiceImpl; |
||||
import me.chanjar.weixin.mp.config.impl.WxMpDefaultConfigImpl; |
||||
import org.springframework.boot.context.properties.ConfigurationProperties; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* 微信开放平台配置 |
||||
*/ |
||||
@Slf4j |
||||
@Configuration |
||||
@ConfigurationProperties(prefix = "wx.open") |
||||
@Data |
||||
public class WxOpenConfig { |
||||
|
||||
private String appId; |
||||
|
||||
private String appSecret; |
||||
|
||||
private WxMpService wxMpService; |
||||
|
||||
/** |
||||
* 单例模式(不用 @Bean 是为了防止和公众号的 service 冲突) |
||||
* |
||||
* @return |
||||
*/ |
||||
public WxMpService getWxMpService() { |
||||
if (wxMpService != null) { |
||||
return wxMpService; |
||||
} |
||||
synchronized (this) { |
||||
if (wxMpService != null) { |
||||
return wxMpService; |
||||
} |
||||
WxMpDefaultConfigImpl config = new WxMpDefaultConfigImpl(); |
||||
config.setAppId(appId); |
||||
config.setSecret(appSecret); |
||||
WxMpService service = new WxMpServiceImpl(); |
||||
service.setWxMpConfigStorage(config); |
||||
wxMpService = service; |
||||
return wxMpService; |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.constant; |
||||
|
||||
/** |
||||
* 通用常量 |
||||
*/ |
||||
public interface CommonConstant { |
||||
|
||||
/** |
||||
* 升序 |
||||
*/ |
||||
String SORT_ORDER_ASC = "ascend"; |
||||
|
||||
/** |
||||
* 降序 |
||||
*/ |
||||
String SORT_ORDER_DESC = " descend"; |
||||
|
||||
} |
@ -0,0 +1,13 @@ |
||||
package cc.bnblogs.springbootinit.constant; |
||||
|
||||
/** |
||||
* 文件常量 |
||||
*/ |
||||
public interface FileConstant { |
||||
|
||||
/** |
||||
* COS 访问地址 |
||||
* todo 需替换配置 |
||||
*/ |
||||
String COS_HOST = "https://yupi.icu"; |
||||
} |
@ -0,0 +1,31 @@ |
||||
package cc.bnblogs.springbootinit.constant; |
||||
|
||||
/** |
||||
* 用户常量 |
||||
*/ |
||||
public interface UserConstant { |
||||
|
||||
/** |
||||
* 用户登录态键 |
||||
*/ |
||||
String USER_LOGIN_STATE = "user_login"; |
||||
|
||||
// region 权限
|
||||
|
||||
/** |
||||
* 默认角色 |
||||
*/ |
||||
String DEFAULT_ROLE = "user"; |
||||
|
||||
/** |
||||
* 管理员角色 |
||||
*/ |
||||
String ADMIN_ROLE = "admin"; |
||||
|
||||
/** |
||||
* 被封号 |
||||
*/ |
||||
String BAN_ROLE = "ban"; |
||||
|
||||
// endregion
|
||||
} |
@ -0,0 +1,106 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.constant.FileConstant; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.model.dto.file.UploadFileRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.enums.FileUploadBizEnum; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import cn.hutool.core.io.FileUtil; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import cc.bnblogs.springbootinit.manager.CosManager; |
||||
|
||||
import java.io.File; |
||||
import java.util.Arrays; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.lang3.RandomStringUtils; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestPart; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
import org.springframework.web.multipart.MultipartFile; |
||||
|
||||
/** |
||||
* 文件接口 |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/file") |
||||
@Slf4j |
||||
public class FileController { |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
@Resource |
||||
private CosManager cosManager; |
||||
|
||||
/** |
||||
* 文件上传 |
||||
* |
||||
* @param multipartFile |
||||
* @param uploadFileRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/upload") |
||||
public BaseResponse<String> uploadFile(@RequestPart("file") MultipartFile multipartFile, |
||||
UploadFileRequest uploadFileRequest, HttpServletRequest request) { |
||||
String biz = uploadFileRequest.getBiz(); |
||||
FileUploadBizEnum fileUploadBizEnum = FileUploadBizEnum.getEnumByValue(biz); |
||||
if (fileUploadBizEnum == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
validFile(multipartFile, fileUploadBizEnum); |
||||
User loginUser = userService.getLoginUser(request); |
||||
// 文件目录:根据业务、用户来划分
|
||||
String uuid = RandomStringUtils.randomAlphanumeric(8); |
||||
String filename = uuid + "-" + multipartFile.getOriginalFilename(); |
||||
String filepath = String.format("/%s/%s/%s", fileUploadBizEnum.getValue(), loginUser.getId(), filename); |
||||
File file = null; |
||||
try { |
||||
// 上传文件
|
||||
file = File.createTempFile(filepath, null); |
||||
multipartFile.transferTo(file); |
||||
cosManager.putObject(filepath, file); |
||||
// 返回可访问地址
|
||||
return ResultUtils.success(FileConstant.COS_HOST + filepath); |
||||
} catch (Exception e) { |
||||
log.error("file upload error, filepath = " + filepath, e); |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "上传失败"); |
||||
} finally { |
||||
if (file != null) { |
||||
// 删除临时文件
|
||||
boolean delete = file.delete(); |
||||
if (!delete) { |
||||
log.error("file delete error, filepath = {}", filepath); |
||||
} |
||||
} |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 校验文件 |
||||
* |
||||
* @param multipartFile |
||||
* @param fileUploadBizEnum 业务类型 |
||||
*/ |
||||
private void validFile(MultipartFile multipartFile, FileUploadBizEnum fileUploadBizEnum) { |
||||
// 文件大小
|
||||
long fileSize = multipartFile.getSize(); |
||||
// 文件后缀
|
||||
String fileSuffix = FileUtil.getSuffix(multipartFile.getOriginalFilename()); |
||||
final long ONE_M = 1024 * 1024L; |
||||
if (FileUploadBizEnum.USER_AVATAR.equals(fileUploadBizEnum)) { |
||||
if (fileSize > ONE_M) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件大小不能超过 1M"); |
||||
} |
||||
if (!Arrays.asList("jpeg", "jpg", "svg", "png", "webp").contains(fileSuffix)) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "文件类型错误"); |
||||
} |
||||
} |
||||
} |
||||
} |
@ -0,0 +1,247 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.annotation.AuthCheck; |
||||
import cc.bnblogs.springbootinit.common.DeleteRequest; |
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.constant.UserConstant; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.exception.ThrowUtils; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostAddRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEditRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostUpdateRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.vo.PostVO; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.google.gson.Gson; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
|
||||
import java.util.List; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* 帖子接口 |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/post") |
||||
@Slf4j |
||||
public class PostController { |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
private final static Gson GSON = new Gson(); |
||||
|
||||
// region 增删改查
|
||||
|
||||
/** |
||||
* 创建 |
||||
* |
||||
* @param postAddRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/add") |
||||
public BaseResponse<Long> addPost(@RequestBody PostAddRequest postAddRequest, HttpServletRequest request) { |
||||
if (postAddRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
Post post = new Post(); |
||||
BeanUtils.copyProperties(postAddRequest, post); |
||||
List<String> tags = postAddRequest.getTags(); |
||||
if (tags != null) { |
||||
post.setTags(GSON.toJson(tags)); |
||||
} |
||||
postService.validPost(post, true); |
||||
User loginUser = userService.getLoginUser(request); |
||||
post.setUserId(loginUser.getId()); |
||||
post.setFavourNum(0); |
||||
post.setThumbNum(0); |
||||
boolean result = postService.save(post); |
||||
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); |
||||
long newPostId = post.getId(); |
||||
return ResultUtils.success(newPostId); |
||||
} |
||||
|
||||
/** |
||||
* 删除 |
||||
* |
||||
* @param deleteRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/delete") |
||||
public BaseResponse<Boolean> deletePost(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) { |
||||
if (deleteRequest == null || deleteRequest.getId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User user = userService.getLoginUser(request); |
||||
long id = deleteRequest.getId(); |
||||
// 判断是否存在
|
||||
Post oldPost = postService.getById(id); |
||||
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR); |
||||
// 仅本人或管理员可删除
|
||||
if (!oldPost.getUserId().equals(user.getId()) && !userService.isAdmin(request)) { |
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); |
||||
} |
||||
boolean b = postService.removeById(id); |
||||
return ResultUtils.success(b); |
||||
} |
||||
|
||||
/** |
||||
* 更新(仅管理员) |
||||
* |
||||
* @param postUpdateRequest |
||||
* @return |
||||
*/ |
||||
@PostMapping("/update") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<Boolean> updatePost(@RequestBody PostUpdateRequest postUpdateRequest) { |
||||
if (postUpdateRequest == null || postUpdateRequest.getId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
Post post = new Post(); |
||||
BeanUtils.copyProperties(postUpdateRequest, post); |
||||
List<String> tags = postUpdateRequest.getTags(); |
||||
if (tags != null) { |
||||
post.setTags(GSON.toJson(tags)); |
||||
} |
||||
// 参数校验
|
||||
postService.validPost(post, false); |
||||
long id = postUpdateRequest.getId(); |
||||
// 判断是否存在
|
||||
Post oldPost = postService.getById(id); |
||||
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR); |
||||
boolean result = postService.updateById(post); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
/** |
||||
* 根据 id 获取 |
||||
* |
||||
* @param id |
||||
* @return |
||||
*/ |
||||
@GetMapping("/get/vo") |
||||
public BaseResponse<PostVO> getPostVOById(long id, HttpServletRequest request) { |
||||
if (id <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
Post post = postService.getById(id); |
||||
if (post == null) { |
||||
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); |
||||
} |
||||
return ResultUtils.success(postService.getPostVO(post, request)); |
||||
} |
||||
|
||||
/** |
||||
* 分页获取列表(封装类) |
||||
* |
||||
* @param postQueryRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/list/page/vo") |
||||
public BaseResponse<Page<PostVO>> listPostVOByPage(@RequestBody PostQueryRequest postQueryRequest, |
||||
HttpServletRequest request) { |
||||
long current = postQueryRequest.getCurrent(); |
||||
long size = postQueryRequest.getPageSize(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); |
||||
Page<Post> postPage = postService.page(new Page<>(current, size), |
||||
postService.getQueryWrapper(postQueryRequest)); |
||||
return ResultUtils.success(postService.getPostVOPage(postPage, request)); |
||||
} |
||||
|
||||
/** |
||||
* 分页获取当前用户创建的资源列表 |
||||
* |
||||
* @param postQueryRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/my/list/page/vo") |
||||
public BaseResponse<Page<PostVO>> listMyPostVOByPage(@RequestBody PostQueryRequest postQueryRequest, |
||||
HttpServletRequest request) { |
||||
if (postQueryRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User loginUser = userService.getLoginUser(request); |
||||
postQueryRequest.setUserId(loginUser.getId()); |
||||
long current = postQueryRequest.getCurrent(); |
||||
long size = postQueryRequest.getPageSize(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); |
||||
Page<Post> postPage = postService.page(new Page<>(current, size), |
||||
postService.getQueryWrapper(postQueryRequest)); |
||||
return ResultUtils.success(postService.getPostVOPage(postPage, request)); |
||||
} |
||||
|
||||
// endregion
|
||||
|
||||
/** |
||||
* 分页搜索(从 ES 查询,封装类) |
||||
* |
||||
* @param postQueryRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/search/page/vo") |
||||
public BaseResponse<Page<PostVO>> searchPostVOByPage(@RequestBody PostQueryRequest postQueryRequest, |
||||
HttpServletRequest request) { |
||||
long size = postQueryRequest.getPageSize(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); |
||||
Page<Post> postPage = postService.searchFromEs(postQueryRequest); |
||||
return ResultUtils.success(postService.getPostVOPage(postPage, request)); |
||||
} |
||||
|
||||
/** |
||||
* 编辑(用户) |
||||
* |
||||
* @param postEditRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/edit") |
||||
public BaseResponse<Boolean> editPost(@RequestBody PostEditRequest postEditRequest, HttpServletRequest request) { |
||||
if (postEditRequest == null || postEditRequest.getId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
Post post = new Post(); |
||||
BeanUtils.copyProperties(postEditRequest, post); |
||||
List<String> tags = postEditRequest.getTags(); |
||||
if (tags != null) { |
||||
post.setTags(GSON.toJson(tags)); |
||||
} |
||||
// 参数校验
|
||||
postService.validPost(post, false); |
||||
User loginUser = userService.getLoginUser(request); |
||||
long id = postEditRequest.getId(); |
||||
// 判断是否存在
|
||||
Post oldPost = postService.getById(id); |
||||
ThrowUtils.throwIf(oldPost == null, ErrorCode.NOT_FOUND_ERROR); |
||||
// 仅本人或管理员可编辑
|
||||
if (!oldPost.getUserId().equals(loginUser.getId()) && !userService.isAdmin(loginUser)) { |
||||
throw new BusinessException(ErrorCode.NO_AUTH_ERROR); |
||||
} |
||||
boolean result = postService.updateById(post); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,108 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.exception.ThrowUtils; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.postfavour.PostFavourAddRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.postfavour.PostFavourQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.vo.PostVO; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import cc.bnblogs.springbootinit.service.PostFavourService; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
|
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* 帖子收藏接口 |
||||
*> |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/post_favour") |
||||
@Slf4j |
||||
public class PostFavourController { |
||||
|
||||
@Resource |
||||
private PostFavourService postFavourService; |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
/** |
||||
* 收藏 / 取消收藏 |
||||
* |
||||
* @param postFavourAddRequest |
||||
* @param request |
||||
* @return resultNum 收藏变化数 |
||||
*/ |
||||
@PostMapping("/") |
||||
public BaseResponse<Integer> doPostFavour(@RequestBody PostFavourAddRequest postFavourAddRequest, |
||||
HttpServletRequest request) { |
||||
if (postFavourAddRequest == null || postFavourAddRequest.getPostId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
// 登录才能操作
|
||||
final User loginUser = userService.getLoginUser(request); |
||||
long postId = postFavourAddRequest.getPostId(); |
||||
int result = postFavourService.doPostFavour(postId, loginUser); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
/** |
||||
* 获取我收藏的帖子列表 |
||||
* |
||||
* @param postQueryRequest |
||||
* @param request |
||||
*/ |
||||
@PostMapping("/my/list/page") |
||||
public BaseResponse<Page<PostVO>> listMyFavourPostByPage(@RequestBody PostQueryRequest postQueryRequest, |
||||
HttpServletRequest request) { |
||||
if (postQueryRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User loginUser = userService.getLoginUser(request); |
||||
long current = postQueryRequest.getCurrent(); |
||||
long size = postQueryRequest.getPageSize(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); |
||||
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size), |
||||
postService.getQueryWrapper(postQueryRequest), loginUser.getId()); |
||||
return ResultUtils.success(postService.getPostVOPage(postPage, request)); |
||||
} |
||||
|
||||
/** |
||||
* 获取用户收藏的帖子列表 |
||||
* |
||||
* @param postFavourQueryRequest |
||||
* @param request |
||||
*/ |
||||
@PostMapping("/list/page") |
||||
public BaseResponse<Page<PostVO>> listFavourPostByPage(@RequestBody PostFavourQueryRequest postFavourQueryRequest, |
||||
HttpServletRequest request) { |
||||
if (postFavourQueryRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
long current = postFavourQueryRequest.getCurrent(); |
||||
long size = postFavourQueryRequest.getPageSize(); |
||||
Long userId = postFavourQueryRequest.getUserId(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20 || userId == null, ErrorCode.PARAMS_ERROR); |
||||
Page<Post> postPage = postFavourService.listFavourPostByPage(new Page<>(current, size), |
||||
postService.getQueryWrapper(postFavourQueryRequest.getPostQueryRequest()), userId); |
||||
return ResultUtils.success(postService.getPostVOPage(postPage, request)); |
||||
} |
||||
} |
@ -0,0 +1,53 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.model.dto.postthumb.PostThumbAddRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.service.PostThumbService; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* 帖子点赞接口> |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/post_thumb") |
||||
@Slf4j |
||||
public class PostThumbController { |
||||
|
||||
@Resource |
||||
private PostThumbService postThumbService; |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
/** |
||||
* 点赞 / 取消点赞 |
||||
* |
||||
* @param postThumbAddRequest |
||||
* @param request |
||||
* @return resultNum 本次点赞变化数 |
||||
*/ |
||||
@PostMapping("/") |
||||
public BaseResponse<Integer> doThumb(@RequestBody PostThumbAddRequest postThumbAddRequest, |
||||
HttpServletRequest request) { |
||||
if (postThumbAddRequest == null || postThumbAddRequest.getPostId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
// 登录才能点赞
|
||||
final User loginUser = userService.getLoginUser(request); |
||||
long postId = postThumbAddRequest.getPostId(); |
||||
int result = postThumbService.doPostThumb(postId, loginUser); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,311 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.common.DeleteRequest; |
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.vo.LoginUserVO; |
||||
import cc.bnblogs.springbootinit.model.vo.UserVO; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import cc.bnblogs.springbootinit.annotation.AuthCheck; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import cc.bnblogs.springbootinit.config.WxOpenConfig; |
||||
import cc.bnblogs.springbootinit.constant.UserConstant; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.exception.ThrowUtils; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserAddRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserLoginRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserRegisterRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserUpdateMyRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserUpdateRequest; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
import java.util.List; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo; |
||||
import me.chanjar.weixin.common.bean.oauth2.WxOAuth2AccessToken; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestBody; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RequestParam; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* 用户接口 |
||||
*> |
||||
*/ |
||||
@RestController |
||||
@RequestMapping("/user") |
||||
@Slf4j |
||||
public class UserController { |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
@Resource |
||||
private WxOpenConfig wxOpenConfig; |
||||
|
||||
// region 登录相关
|
||||
|
||||
|
||||
|
||||
/** |
||||
* 用户注册 |
||||
* |
||||
* @param userRegisterRequest |
||||
* @return |
||||
*/ |
||||
@PostMapping("/register") |
||||
public BaseResponse<Long> userRegister(@RequestBody UserRegisterRequest userRegisterRequest) { |
||||
if (userRegisterRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
String userAccount = userRegisterRequest.getUserAccount(); |
||||
String userPassword = userRegisterRequest.getUserPassword(); |
||||
String checkPassword = userRegisterRequest.getCheckPassword(); |
||||
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) { |
||||
return null; |
||||
} |
||||
long result = userService.userRegister(userAccount, userPassword, checkPassword); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
/** |
||||
* 用户登录 |
||||
* |
||||
* @param userLoginRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/login") |
||||
public BaseResponse<LoginUserVO> userLogin(@RequestBody UserLoginRequest userLoginRequest, HttpServletRequest request) { |
||||
if (userLoginRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
String userAccount = userLoginRequest.getUserAccount(); |
||||
String userPassword = userLoginRequest.getUserPassword(); |
||||
if (StringUtils.isAnyBlank(userAccount, userPassword)) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
LoginUserVO loginUserVO = userService.userLogin(userAccount, userPassword, request); |
||||
return ResultUtils.success(loginUserVO); |
||||
} |
||||
|
||||
/** |
||||
* 用户登录(微信开放平台) |
||||
*/ |
||||
@GetMapping("/login/wx_open") |
||||
public BaseResponse<LoginUserVO> userLoginByWxOpen(HttpServletRequest request, HttpServletResponse response, |
||||
@RequestParam("code") String code) { |
||||
WxOAuth2AccessToken accessToken; |
||||
try { |
||||
WxMpService wxService = wxOpenConfig.getWxMpService(); |
||||
accessToken = wxService.getOAuth2Service().getAccessToken(code); |
||||
WxOAuth2UserInfo userInfo = wxService.getOAuth2Service().getUserInfo(accessToken, code); |
||||
String unionId = userInfo.getUnionId(); |
||||
String mpOpenId = userInfo.getOpenid(); |
||||
if (StringUtils.isAnyBlank(unionId, mpOpenId)) { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "登录失败,系统错误"); |
||||
} |
||||
return ResultUtils.success(userService.userLoginByMpOpen(userInfo, request)); |
||||
} catch (Exception e) { |
||||
log.error("userLoginByWxOpen error", e); |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "登录失败,系统错误"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 用户注销 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/logout") |
||||
public BaseResponse<Boolean> userLogout(HttpServletRequest request) { |
||||
if (request == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
boolean result = userService.userLogout(request); |
||||
return ResultUtils.success(result); |
||||
} |
||||
|
||||
/** |
||||
* 获取当前登录用户 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@GetMapping("/get/login") |
||||
public BaseResponse<LoginUserVO> getLoginUser(HttpServletRequest request) { |
||||
User user = userService.getLoginUser(request); |
||||
return ResultUtils.success(userService.getLoginUserVO(user)); |
||||
} |
||||
|
||||
// endregion
|
||||
|
||||
// region 增删改查
|
||||
|
||||
/** |
||||
* 创建用户 |
||||
* |
||||
* @param userAddRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/add") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<Long> addUser(@RequestBody UserAddRequest userAddRequest, HttpServletRequest request) { |
||||
if (userAddRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User user = new User(); |
||||
BeanUtils.copyProperties(userAddRequest, user); |
||||
boolean result = userService.save(user); |
||||
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); |
||||
return ResultUtils.success(user.getId()); |
||||
} |
||||
|
||||
/** |
||||
* 删除用户 |
||||
* |
||||
* @param deleteRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/delete") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<Boolean> deleteUser(@RequestBody DeleteRequest deleteRequest, HttpServletRequest request) { |
||||
if (deleteRequest == null || deleteRequest.getId() <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
boolean b = userService.removeById(deleteRequest.getId()); |
||||
return ResultUtils.success(b); |
||||
} |
||||
|
||||
/** |
||||
* 更新用户 |
||||
* |
||||
* @param userUpdateRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/update") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<Boolean> updateUser(@RequestBody UserUpdateRequest userUpdateRequest, |
||||
HttpServletRequest request) { |
||||
if (userUpdateRequest == null || userUpdateRequest.getId() == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User user = new User(); |
||||
BeanUtils.copyProperties(userUpdateRequest, user); |
||||
boolean result = userService.updateById(user); |
||||
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); |
||||
return ResultUtils.success(true); |
||||
} |
||||
|
||||
/** |
||||
* 根据 id 获取用户(仅管理员) |
||||
* |
||||
* @param id |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@GetMapping("/get") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<User> getUserById(long id, HttpServletRequest request) { |
||||
if (id <= 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User user = userService.getById(id); |
||||
ThrowUtils.throwIf(user == null, ErrorCode.NOT_FOUND_ERROR); |
||||
return ResultUtils.success(user); |
||||
} |
||||
|
||||
/** |
||||
* 根据 id 获取包装类 |
||||
* |
||||
* @param id |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@GetMapping("/get/vo") |
||||
public BaseResponse<UserVO> getUserVOById(long id, HttpServletRequest request) { |
||||
BaseResponse<User> response = getUserById(id, request); |
||||
User user = response.getData(); |
||||
return ResultUtils.success(userService.getUserVO(user)); |
||||
} |
||||
|
||||
/** |
||||
* 分页获取用户列表(仅管理员) |
||||
* |
||||
* @param userQueryRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/list/page") |
||||
@AuthCheck(mustRole = UserConstant.ADMIN_ROLE) |
||||
public BaseResponse<Page<User>> listUserByPage(@RequestBody UserQueryRequest userQueryRequest, |
||||
HttpServletRequest request) { |
||||
long current = userQueryRequest.getCurrent(); |
||||
long size = userQueryRequest.getPageSize(); |
||||
Page<User> userPage = userService.page(new Page<>(current, size), |
||||
userService.getQueryWrapper(userQueryRequest)); |
||||
return ResultUtils.success(userPage); |
||||
} |
||||
|
||||
/** |
||||
* 分页获取用户封装列表 |
||||
* |
||||
* @param userQueryRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/list/page/vo") |
||||
public BaseResponse<Page<UserVO>> listUserVOByPage(@RequestBody UserQueryRequest userQueryRequest, |
||||
HttpServletRequest request) { |
||||
if (userQueryRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
long current = userQueryRequest.getCurrent(); |
||||
long size = userQueryRequest.getPageSize(); |
||||
// 限制爬虫
|
||||
ThrowUtils.throwIf(size > 20, ErrorCode.PARAMS_ERROR); |
||||
Page<User> userPage = userService.page(new Page<>(current, size), |
||||
userService.getQueryWrapper(userQueryRequest)); |
||||
Page<UserVO> userVOPage = new Page<>(current, size, userPage.getTotal()); |
||||
List<UserVO> userVO = userService.getUserVO(userPage.getRecords()); |
||||
userVOPage.setRecords(userVO); |
||||
return ResultUtils.success(userVOPage); |
||||
} |
||||
|
||||
// endregion
|
||||
|
||||
/** |
||||
* 更新个人信息 |
||||
* |
||||
* @param userUpdateMyRequest |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@PostMapping("/update/my") |
||||
public BaseResponse<Boolean> updateMyUser(@RequestBody UserUpdateMyRequest userUpdateMyRequest, |
||||
HttpServletRequest request) { |
||||
if (userUpdateMyRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
User loginUser = userService.getLoginUser(request); |
||||
User user = new User(); |
||||
BeanUtils.copyProperties(userUpdateMyRequest, user); |
||||
user.setId(loginUser.getId()); |
||||
boolean result = userService.updateById(user); |
||||
ThrowUtils.throwIf(!result, ErrorCode.OPERATION_ERROR); |
||||
return ResultUtils.success(true); |
||||
} |
||||
} |
@ -0,0 +1,134 @@ |
||||
package cc.bnblogs.springbootinit.controller; |
||||
|
||||
import cc.bnblogs.springbootinit.wxmp.WxMpConstant; |
||||
|
||||
import java.io.IOException; |
||||
import java.util.Arrays; |
||||
import java.util.Collections; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import javax.servlet.http.HttpServletResponse; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import me.chanjar.weixin.common.api.WxConsts.MenuButtonType; |
||||
import me.chanjar.weixin.common.bean.menu.WxMenu; |
||||
import me.chanjar.weixin.common.bean.menu.WxMenuButton; |
||||
import me.chanjar.weixin.common.error.WxErrorException; |
||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.springframework.web.bind.annotation.GetMapping; |
||||
import org.springframework.web.bind.annotation.PostMapping; |
||||
import org.springframework.web.bind.annotation.RequestMapping; |
||||
import org.springframework.web.bind.annotation.RestController; |
||||
|
||||
/** |
||||
* 微信公众号相关接口 |
||||
*> |
||||
**/ |
||||
@RestController |
||||
@RequestMapping("/") |
||||
@Slf4j |
||||
public class WxMpController { |
||||
|
||||
@Resource |
||||
private WxMpService wxMpService; |
||||
|
||||
@Resource |
||||
private WxMpMessageRouter router; |
||||
|
||||
@PostMapping("/") |
||||
public void receiveMessage(HttpServletRequest request, HttpServletResponse response) |
||||
throws IOException { |
||||
response.setContentType("text/html;charset=utf-8"); |
||||
response.setStatus(HttpServletResponse.SC_OK); |
||||
// 校验消息签名,判断是否为公众平台发的消息
|
||||
String signature = request.getParameter("signature"); |
||||
String nonce = request.getParameter("nonce"); |
||||
String timestamp = request.getParameter("timestamp"); |
||||
if (!wxMpService.checkSignature(timestamp, nonce, signature)) { |
||||
response.getWriter().println("非法请求"); |
||||
} |
||||
// 加密类型
|
||||
String encryptType = StringUtils.isBlank(request.getParameter("encrypt_type")) ? "raw" |
||||
: request.getParameter("encrypt_type"); |
||||
// 明文消息
|
||||
if ("raw".equals(encryptType)) { |
||||
return; |
||||
} |
||||
// aes 加密消息
|
||||
if ("aes".equals(encryptType)) { |
||||
// 解密消息
|
||||
String msgSignature = request.getParameter("msg_signature"); |
||||
WxMpXmlMessage inMessage = WxMpXmlMessage |
||||
.fromEncryptedXml(request.getInputStream(), wxMpService.getWxMpConfigStorage(), timestamp, |
||||
nonce, |
||||
msgSignature); |
||||
log.info("message content = {}", inMessage.getContent()); |
||||
// 路由消息并处理
|
||||
WxMpXmlOutMessage outMessage = router.route(inMessage); |
||||
if (outMessage == null) { |
||||
response.getWriter().write(""); |
||||
} else { |
||||
response.getWriter().write(outMessage.toEncryptedXml(wxMpService.getWxMpConfigStorage())); |
||||
} |
||||
return; |
||||
} |
||||
response.getWriter().println("不可识别的加密类型"); |
||||
} |
||||
|
||||
@GetMapping("/") |
||||
public String check(String timestamp, String nonce, String signature, String echostr) { |
||||
log.info("check"); |
||||
if (wxMpService.checkSignature(timestamp, nonce, signature)) { |
||||
return echostr; |
||||
} else { |
||||
return ""; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 设置公众号菜单 |
||||
* |
||||
* @return |
||||
* @throws WxErrorException |
||||
*/ |
||||
@GetMapping("/setMenu") |
||||
public String setMenu() throws WxErrorException { |
||||
log.info("setMenu"); |
||||
WxMenu wxMenu = new WxMenu(); |
||||
// 菜单一
|
||||
WxMenuButton wxMenuButton1 = new WxMenuButton(); |
||||
wxMenuButton1.setType(MenuButtonType.VIEW); |
||||
wxMenuButton1.setName("主菜单一"); |
||||
// 子菜单
|
||||
WxMenuButton wxMenuButton1SubButton1 = new WxMenuButton(); |
||||
wxMenuButton1SubButton1.setType(MenuButtonType.VIEW); |
||||
wxMenuButton1SubButton1.setName("跳转页面"); |
||||
wxMenuButton1SubButton1.setUrl( |
||||
"https://yupi.icu"); |
||||
wxMenuButton1.setSubButtons(Collections.singletonList(wxMenuButton1SubButton1)); |
||||
|
||||
// 菜单二
|
||||
WxMenuButton wxMenuButton2 = new WxMenuButton(); |
||||
wxMenuButton2.setType(MenuButtonType.CLICK); |
||||
wxMenuButton2.setName("点击事件"); |
||||
wxMenuButton2.setKey(WxMpConstant.CLICK_MENU_KEY); |
||||
|
||||
// 菜单三
|
||||
WxMenuButton wxMenuButton3 = new WxMenuButton(); |
||||
wxMenuButton3.setType(MenuButtonType.VIEW); |
||||
wxMenuButton3.setName("主菜单三"); |
||||
WxMenuButton wxMenuButton3SubButton1 = new WxMenuButton(); |
||||
wxMenuButton3SubButton1.setType(MenuButtonType.VIEW); |
||||
wxMenuButton3SubButton1.setName("编程学习"); |
||||
wxMenuButton3SubButton1.setUrl("https://yupi.icu"); |
||||
wxMenuButton3.setSubButtons(Collections.singletonList(wxMenuButton3SubButton1)); |
||||
|
||||
// 设置主菜单
|
||||
wxMenu.setButtons(Arrays.asList(wxMenuButton1, wxMenuButton2, wxMenuButton3)); |
||||
wxMpService.getMenuService().menuCreate(wxMenu); |
||||
return "ok"; |
||||
} |
||||
} |
@ -0,0 +1,14 @@ |
||||
package cc.bnblogs.springbootinit.esdao; |
||||
|
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEsDTO; |
||||
|
||||
import java.util.List; |
||||
import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; |
||||
|
||||
/** |
||||
* 帖子 ES 操作 |
||||
*/ |
||||
public interface PostEsDao extends ElasticsearchRepository<PostEsDTO, Long> { |
||||
|
||||
List<PostEsDTO> findByUserId(Long userId); |
||||
} |
@ -0,0 +1,33 @@ |
||||
package cc.bnblogs.springbootinit.exception; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
|
||||
/** |
||||
* 自定义异常类 |
||||
*/ |
||||
public class BusinessException extends RuntimeException { |
||||
|
||||
/** |
||||
* 错误码 |
||||
*/ |
||||
private final int code; |
||||
|
||||
public BusinessException(int code, String message) { |
||||
super(message); |
||||
this.code = code; |
||||
} |
||||
|
||||
public BusinessException(ErrorCode errorCode) { |
||||
super(errorCode.getMessage()); |
||||
this.code = errorCode.getCode(); |
||||
} |
||||
|
||||
public BusinessException(ErrorCode errorCode, String message) { |
||||
super(message); |
||||
this.code = errorCode.getCode(); |
||||
} |
||||
|
||||
public int getCode() { |
||||
return code; |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cc.bnblogs.springbootinit.exception; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.common.ResultUtils; |
||||
import cc.bnblogs.springbootinit.common.BaseResponse; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.springframework.web.bind.annotation.ExceptionHandler; |
||||
import org.springframework.web.bind.annotation.RestControllerAdvice; |
||||
|
||||
/** |
||||
* 全局异常处理器 |
||||
*/ |
||||
@RestControllerAdvice |
||||
@Slf4j |
||||
public class GlobalExceptionHandler { |
||||
|
||||
@ExceptionHandler(BusinessException.class) |
||||
public BaseResponse<?> businessExceptionHandler(BusinessException e) { |
||||
log.error("BusinessException", e); |
||||
return ResultUtils.error(e.getCode(), e.getMessage()); |
||||
} |
||||
|
||||
@ExceptionHandler(RuntimeException.class) |
||||
public BaseResponse<?> runtimeExceptionHandler(RuntimeException e) { |
||||
log.error("RuntimeException", e); |
||||
return ResultUtils.error(ErrorCode.SYSTEM_ERROR, "系统错误"); |
||||
} |
||||
} |
@ -0,0 +1,42 @@ |
||||
package cc.bnblogs.springbootinit.exception; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
|
||||
/** |
||||
* 抛异常工具类 |
||||
*/ |
||||
public class ThrowUtils { |
||||
|
||||
/** |
||||
* 条件成立则抛异常 |
||||
* |
||||
* @param condition |
||||
* @param runtimeException |
||||
*/ |
||||
public static void throwIf(boolean condition, RuntimeException runtimeException) { |
||||
if (condition) { |
||||
throw runtimeException; |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 条件成立则抛异常 |
||||
* |
||||
* @param condition |
||||
* @param errorCode |
||||
*/ |
||||
public static void throwIf(boolean condition, ErrorCode errorCode) { |
||||
throwIf(condition, new BusinessException(errorCode)); |
||||
} |
||||
|
||||
/** |
||||
* 条件成立则抛异常 |
||||
* |
||||
* @param condition |
||||
* @param errorCode |
||||
* @param message |
||||
*/ |
||||
public static void throwIf(boolean condition, ErrorCode errorCode, String message) { |
||||
throwIf(condition, new BusinessException(errorCode, message)); |
||||
} |
||||
} |
@ -0,0 +1,55 @@ |
||||
package cc.bnblogs.springbootinit.job.cycle; |
||||
|
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEsDTO; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.esdao.PostEsDao; |
||||
import cc.bnblogs.springbootinit.mapper.PostMapper; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import javax.annotation.Resource; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
import org.springframework.scheduling.annotation.Scheduled; |
||||
|
||||
/** |
||||
* 增量同步帖子到 es |
||||
*/ |
||||
// todo 取消注释开启任务
|
||||
//@Component
|
||||
@Slf4j |
||||
public class IncSyncPostToEs { |
||||
|
||||
@Resource |
||||
private PostMapper postMapper; |
||||
|
||||
@Resource |
||||
private PostEsDao postEsDao; |
||||
|
||||
/** |
||||
* 每分钟执行一次 |
||||
*/ |
||||
@Scheduled(fixedRate = 60 * 1000) |
||||
public void run() { |
||||
// 查询近 5 分钟内的数据
|
||||
Date fiveMinutesAgoDate = new Date(new Date().getTime() - 5 * 60 * 1000L); |
||||
List<Post> postList = postMapper.listPostWithDelete(fiveMinutesAgoDate); |
||||
if (CollectionUtils.isEmpty(postList)) { |
||||
log.info("no inc post"); |
||||
return; |
||||
} |
||||
List<PostEsDTO> postEsDTOList = postList.stream() |
||||
.map(PostEsDTO::objToDto) |
||||
.collect(Collectors.toList()); |
||||
final int pageSize = 500; |
||||
int total = postEsDTOList.size(); |
||||
log.info("IncSyncPostToEs start, total {}", total); |
||||
for (int i = 0; i < total; i += pageSize) { |
||||
int end = Math.min(i + pageSize, total); |
||||
log.info("sync from {} to {}", i, end); |
||||
postEsDao.saveAll(postEsDTOList.subList(i, end)); |
||||
} |
||||
log.info("IncSyncPostToEs end, total {}", total); |
||||
} |
||||
} |
@ -0,0 +1,45 @@ |
||||
package cc.bnblogs.springbootinit.job.once; |
||||
|
||||
import cc.bnblogs.springbootinit.esdao.PostEsDao; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEsDTO; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import javax.annotation.Resource; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
import org.springframework.boot.CommandLineRunner; |
||||
|
||||
/** |
||||
* 全量同步帖子到 es |
||||
*/ |
||||
// todo 取消注释开启任务
|
||||
//@Component
|
||||
@Slf4j |
||||
public class FullSyncPostToEs implements CommandLineRunner { |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
@Resource |
||||
private PostEsDao postEsDao; |
||||
|
||||
@Override |
||||
public void run(String... args) { |
||||
List<Post> postList = postService.list(); |
||||
if (CollectionUtils.isEmpty(postList)) { |
||||
return; |
||||
} |
||||
List<PostEsDTO> postEsDTOList = postList.stream().map(PostEsDTO::objToDto).collect(Collectors.toList()); |
||||
final int pageSize = 500; |
||||
int total = postEsDTOList.size(); |
||||
log.info("FullSyncPostToEs start, total {}", total); |
||||
for (int i = 0; i < total; i += pageSize) { |
||||
int end = Math.min(i + pageSize, total); |
||||
log.info("sync from {} to {}", i, end); |
||||
postEsDao.saveAll(postEsDTOList.subList(i, end)); |
||||
} |
||||
log.info("FullSyncPostToEs end, total {}", total); |
||||
} |
||||
} |
@ -0,0 +1,50 @@ |
||||
package cc.bnblogs.springbootinit.manager; |
||||
|
||||
import cc.bnblogs.springbootinit.config.CosClientConfig; |
||||
import com.qcloud.cos.COSClient; |
||||
import com.qcloud.cos.model.PutObjectRequest; |
||||
import com.qcloud.cos.model.PutObjectResult; |
||||
|
||||
import java.io.File; |
||||
import javax.annotation.Resource; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* Cos 对象存储操作 |
||||
* |
||||
*/ |
||||
@Component |
||||
public class CosManager { |
||||
|
||||
@Resource |
||||
private CosClientConfig cosClientConfig; |
||||
|
||||
@Resource |
||||
private COSClient cosClient; |
||||
|
||||
/** |
||||
* 上传对象 |
||||
* |
||||
* @param key 唯一键 |
||||
* @param localFilePath 本地文件路径 |
||||
* @return |
||||
*/ |
||||
public PutObjectResult putObject(String key, String localFilePath) { |
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key, |
||||
new File(localFilePath)); |
||||
return cosClient.putObject(putObjectRequest); |
||||
} |
||||
|
||||
/** |
||||
* 上传对象 |
||||
* |
||||
* @param key 唯一键 |
||||
* @param file 文件 |
||||
* @return |
||||
*/ |
||||
public PutObjectResult putObject(String key, File file) { |
||||
PutObjectRequest putObjectRequest = new PutObjectRequest(cosClientConfig.getBucket(), key, |
||||
file); |
||||
return cosClient.putObject(putObjectRequest); |
||||
} |
||||
} |
@ -0,0 +1,32 @@ |
||||
package cc.bnblogs.springbootinit.mapper; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.PostFavour; |
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper; |
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
import com.baomidou.mybatisplus.core.toolkit.Constants; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import org.apache.ibatis.annotations.Param; |
||||
|
||||
/** |
||||
* 帖子收藏数据库操作 |
||||
*/ |
||||
public interface PostFavourMapper extends BaseMapper<PostFavour> { |
||||
|
||||
/** |
||||
* 分页查询收藏帖子列表 |
||||
* |
||||
* @param page |
||||
* @param queryWrapper |
||||
* @param favourUserId |
||||
* @return |
||||
*/ |
||||
Page<Post> listFavourPostByPage(IPage<Post> page, @Param(Constants.WRAPPER) Wrapper<Post> queryWrapper, |
||||
long favourUserId); |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,23 @@ |
||||
package cc.bnblogs.springbootinit.mapper; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
||||
import java.util.Date; |
||||
import java.util.List; |
||||
|
||||
/** |
||||
* 帖子数据库操作 |
||||
*/ |
||||
public interface PostMapper extends BaseMapper<Post> { |
||||
|
||||
/** |
||||
* 查询帖子列表(包括已被删除的数据) |
||||
*/ |
||||
List<Post> listPostWithDelete(Date minUpdateTime); |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,15 @@ |
||||
package cc.bnblogs.springbootinit.mapper; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.PostThumb; |
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
||||
/** |
||||
* 帖子点赞数据库操作 |
||||
*/ |
||||
public interface PostThumbMapper extends BaseMapper<PostThumb> { |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,15 @@ |
||||
package cc.bnblogs.springbootinit.mapper; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import com.baomidou.mybatisplus.core.mapper.BaseMapper; |
||||
|
||||
/** |
||||
* 用户数据库操作 |
||||
*/ |
||||
public interface UserMapper extends BaseMapper<User> { |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.file; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 文件上传请求 |
||||
*/ |
||||
@Data |
||||
public class UploadFileRequest implements Serializable { |
||||
|
||||
/** |
||||
* 业务 |
||||
*/ |
||||
private String biz; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,29 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.post; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 创建请求 |
||||
*/ |
||||
@Data |
||||
public class PostAddRequest implements Serializable { |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tags; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,34 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.post; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 编辑请求 |
||||
*/ |
||||
@Data |
||||
public class PostEditRequest implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tags; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,123 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.post; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import com.google.common.reflect.TypeToken; |
||||
import com.google.gson.Gson; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.data.annotation.Id; |
||||
import org.springframework.data.elasticsearch.annotations.Field; |
||||
import org.springframework.data.elasticsearch.annotations.FieldType; |
||||
|
||||
/** |
||||
* 帖子 ES 包装类 |
||||
**/ |
||||
// todo 取消注释开启 ES(须先配置 ES)
|
||||
//@Document(indexName = "post")
|
||||
@Data |
||||
public class PostEsDTO implements Serializable { |
||||
|
||||
private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'"; |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
@Id |
||||
private Long id; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tags; |
||||
|
||||
/** |
||||
* 点赞数 |
||||
*/ |
||||
private Integer thumbNum; |
||||
|
||||
/** |
||||
* 收藏数 |
||||
*/ |
||||
private Integer favourNum; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN) |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN) |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 是否删除 |
||||
*/ |
||||
private Integer isDelete; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
|
||||
private static final Gson GSON = new Gson(); |
||||
|
||||
/** |
||||
* 对象转包装类 |
||||
* |
||||
* @param post |
||||
* @return |
||||
*/ |
||||
public static PostEsDTO objToDto(Post post) { |
||||
if (post == null) { |
||||
return null; |
||||
} |
||||
PostEsDTO postEsDTO = new PostEsDTO(); |
||||
BeanUtils.copyProperties(post, postEsDTO); |
||||
String tagsStr = post.getTags(); |
||||
if (StringUtils.isNotBlank(tagsStr)) { |
||||
postEsDTO.setTags(GSON.fromJson(tagsStr, new TypeToken<List<String>>() { |
||||
}.getType())); |
||||
} |
||||
return postEsDTO; |
||||
} |
||||
|
||||
/** |
||||
* 包装类转对象 |
||||
* |
||||
* @param postEsDTO |
||||
* @return |
||||
*/ |
||||
public static Post dtoToObj(PostEsDTO postEsDTO) { |
||||
if (postEsDTO == null) { |
||||
return null; |
||||
} |
||||
Post post = new Post(); |
||||
BeanUtils.copyProperties(postEsDTO, post); |
||||
List<String> tagList = postEsDTO.getTags(); |
||||
if (CollectionUtils.isNotEmpty(tagList)) { |
||||
post.setTags(GSON.toJson(tagList)); |
||||
} |
||||
return post; |
||||
} |
||||
} |
@ -0,0 +1,63 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.post; |
||||
|
||||
import cc.bnblogs.springbootinit.common.PageRequest; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
|
||||
/** |
||||
* 查询请求 |
||||
*/ |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@Data |
||||
public class PostQueryRequest extends PageRequest implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long notId; |
||||
|
||||
/** |
||||
* 搜索词 |
||||
*/ |
||||
private String searchText; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tags; |
||||
|
||||
/** |
||||
* 至少有一个标签 |
||||
*/ |
||||
private List<String> orTags; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 收藏用户 id |
||||
*/ |
||||
private Long favourUserId; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,34 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.post; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 更新请求 |
||||
*/ |
||||
@Data |
||||
public class PostUpdateRequest implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tags; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.postfavour; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 帖子收藏 / 取消收藏请求 |
||||
*/ |
||||
@Data |
||||
public class PostFavourAddRequest implements Serializable { |
||||
|
||||
/** |
||||
* 帖子 id |
||||
*/ |
||||
private Long postId; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,27 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.postfavour; |
||||
|
||||
import cc.bnblogs.springbootinit.common.PageRequest; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
|
||||
/** |
||||
* 帖子收藏查询请求 |
||||
*/ |
||||
@Data |
||||
@EqualsAndHashCode(callSuper = true) |
||||
public class PostFavourQueryRequest extends PageRequest implements Serializable { |
||||
|
||||
/** |
||||
* 帖子查询请求 |
||||
*/ |
||||
private PostQueryRequest postQueryRequest; |
||||
|
||||
/** |
||||
* 用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.postthumb; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 帖子点赞请求 |
||||
*/ |
||||
@Data |
||||
public class PostThumbAddRequest implements Serializable { |
||||
|
||||
/** |
||||
* 帖子 id |
||||
*/ |
||||
private Long postId; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,33 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户创建请求 |
||||
*/ |
||||
@Data |
||||
public class UserAddRequest implements Serializable { |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 账号 |
||||
*/ |
||||
private String userAccount; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 用户角色: user, admin |
||||
*/ |
||||
private String userRole; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,18 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户登录请求 |
||||
> |
||||
*/ |
||||
@Data |
||||
public class UserLoginRequest implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 3191241716373120793L; |
||||
|
||||
private String userAccount; |
||||
|
||||
private String userPassword; |
||||
} |
@ -0,0 +1,46 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import cc.bnblogs.springbootinit.common.PageRequest; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
import lombok.EqualsAndHashCode; |
||||
|
||||
/** |
||||
* 用户查询请求 |
||||
*/ |
||||
@EqualsAndHashCode(callSuper = true) |
||||
@Data |
||||
public class UserQueryRequest extends PageRequest implements Serializable { |
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 开放平台id |
||||
*/ |
||||
private String unionId; |
||||
|
||||
/** |
||||
* 公众号openId |
||||
*/ |
||||
private String mpOpenId; |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
/** |
||||
* 用户角色:user/admin/ban |
||||
*/ |
||||
private String userRole; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,19 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户注册请求体 |
||||
*/ |
||||
@Data |
||||
public class UserRegisterRequest implements Serializable { |
||||
|
||||
private static final long serialVersionUID = 3191241716373120793L; |
||||
|
||||
private String userAccount; |
||||
|
||||
private String userPassword; |
||||
|
||||
private String checkPassword; |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户更新个人信息请求 |
||||
*/ |
||||
@Data |
||||
public class UserUpdateMyRequest implements Serializable { |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,37 @@ |
||||
package cc.bnblogs.springbootinit.model.dto.user; |
||||
|
||||
import java.io.Serializable; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户更新请求 |
||||
*/ |
||||
@Data |
||||
public class UserUpdateRequest implements Serializable { |
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
/** |
||||
* 用户角色:user/admin/ban |
||||
*/ |
||||
private String userRole; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,73 @@ |
||||
package cc.bnblogs.springbootinit.model.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 帖子 |
||||
*/ |
||||
@TableName(value = "post") |
||||
@Data |
||||
public class Post implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
@TableId(type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 标签列表 json |
||||
*/ |
||||
private String tags; |
||||
|
||||
/** |
||||
* 点赞数 |
||||
*/ |
||||
private Integer thumbNum; |
||||
|
||||
/** |
||||
* 收藏数 |
||||
*/ |
||||
private Integer favourNum; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 是否删除 |
||||
*/ |
||||
@TableLogic |
||||
private Integer isDelete; |
||||
|
||||
@TableField(exist = false) |
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,46 @@ |
||||
package cc.bnblogs.springbootinit.model.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 帖子收藏 |
||||
**/ |
||||
@TableName(value = "post_favour") |
||||
@Data |
||||
public class PostFavour implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
@TableId(type = IdType.AUTO) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 帖子 id |
||||
*/ |
||||
private Long postId; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
@TableField(exist = false) |
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,46 @@ |
||||
package cc.bnblogs.springbootinit.model.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 帖子点赞 |
||||
*/ |
||||
@TableName(value = "post_thumb") |
||||
@Data |
||||
public class PostThumb implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
@TableId(type = IdType.AUTO) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 帖子 id |
||||
*/ |
||||
private Long postId; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
@TableField(exist = false) |
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,83 @@ |
||||
package cc.bnblogs.springbootinit.model.entity; |
||||
|
||||
import com.baomidou.mybatisplus.annotation.IdType; |
||||
import com.baomidou.mybatisplus.annotation.TableField; |
||||
import com.baomidou.mybatisplus.annotation.TableId; |
||||
import com.baomidou.mybatisplus.annotation.TableLogic; |
||||
import com.baomidou.mybatisplus.annotation.TableName; |
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户 |
||||
*/ |
||||
@TableName(value = "user") |
||||
@Data |
||||
public class User implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
@TableId(type = IdType.ASSIGN_ID) |
||||
private Long id; |
||||
|
||||
/** |
||||
* 用户账号 |
||||
*/ |
||||
private String userAccount; |
||||
|
||||
/** |
||||
* 用户密码 |
||||
*/ |
||||
private String userPassword; |
||||
|
||||
/** |
||||
* 开放平台id |
||||
*/ |
||||
private String unionId; |
||||
|
||||
/** |
||||
* 公众号openId |
||||
*/ |
||||
private String mpOpenId; |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 用户简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
/** |
||||
* 用户角色:user/admin/ban |
||||
*/ |
||||
private String userRole; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 是否删除 |
||||
*/ |
||||
@TableLogic |
||||
private Integer isDelete; |
||||
|
||||
@TableField(exist = false) |
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,58 @@ |
||||
package cc.bnblogs.springbootinit.model.enums; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
|
||||
/** |
||||
* 文件上传业务类型枚举 |
||||
*/ |
||||
public enum FileUploadBizEnum { |
||||
|
||||
USER_AVATAR("用户头像", "user_avatar"); |
||||
|
||||
private final String text; |
||||
|
||||
private final String value; |
||||
|
||||
FileUploadBizEnum(String text, String value) { |
||||
this.text = text; |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* 获取值列表 |
||||
* |
||||
* @return |
||||
*/ |
||||
public static List<String> getValues() { |
||||
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList()); |
||||
} |
||||
|
||||
/** |
||||
* 根据 value 获取枚举 |
||||
* |
||||
* @param value |
||||
* @return |
||||
*/ |
||||
public static FileUploadBizEnum getEnumByValue(String value) { |
||||
if (ObjectUtils.isEmpty(value)) { |
||||
return null; |
||||
} |
||||
for (FileUploadBizEnum anEnum : FileUploadBizEnum.values()) { |
||||
if (anEnum.value.equals(value)) { |
||||
return anEnum; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public String getValue() { |
||||
return value; |
||||
} |
||||
|
||||
public String getText() { |
||||
return text; |
||||
} |
||||
} |
@ -0,0 +1,60 @@ |
||||
package cc.bnblogs.springbootinit.model.enums; |
||||
|
||||
import java.util.Arrays; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
|
||||
/** |
||||
* 用户角色枚举 |
||||
*/ |
||||
public enum UserRoleEnum { |
||||
|
||||
USER("用户", "user"), |
||||
ADMIN("管理员", "admin"), |
||||
BAN("被封号", "ban"); |
||||
|
||||
private final String text; |
||||
|
||||
private final String value; |
||||
|
||||
UserRoleEnum(String text, String value) { |
||||
this.text = text; |
||||
this.value = value; |
||||
} |
||||
|
||||
/** |
||||
* 获取值列表 |
||||
* |
||||
* @return |
||||
*/ |
||||
public static List<String> getValues() { |
||||
return Arrays.stream(values()).map(item -> item.value).collect(Collectors.toList()); |
||||
} |
||||
|
||||
/** |
||||
* 根据 value 获取枚举 |
||||
* |
||||
* @param value |
||||
* @return |
||||
*/ |
||||
public static UserRoleEnum getEnumByValue(String value) { |
||||
if (ObjectUtils.isEmpty(value)) { |
||||
return null; |
||||
} |
||||
for (UserRoleEnum anEnum : UserRoleEnum.values()) { |
||||
if (anEnum.value.equals(value)) { |
||||
return anEnum; |
||||
} |
||||
} |
||||
return null; |
||||
} |
||||
|
||||
public String getValue() { |
||||
return value; |
||||
} |
||||
|
||||
public String getText() { |
||||
return text; |
||||
} |
||||
} |
@ -0,0 +1,49 @@ |
||||
package cc.bnblogs.springbootinit.model.vo; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 已登录用户视图(脱敏) |
||||
**/ |
||||
@Data |
||||
public class LoginUserVO implements Serializable { |
||||
|
||||
/** |
||||
* 用户 id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 用户简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
/** |
||||
* 用户角色:user/admin/ban |
||||
*/ |
||||
private String userRole; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,116 @@ |
||||
package cc.bnblogs.springbootinit.model.vo; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import com.google.gson.Gson; |
||||
import com.google.gson.reflect.TypeToken; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import lombok.Data; |
||||
import org.springframework.beans.BeanUtils; |
||||
|
||||
/** |
||||
* 帖子视图 |
||||
*/ |
||||
@Data |
||||
public class PostVO implements Serializable { |
||||
|
||||
private final static Gson GSON = new Gson(); |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 标题 |
||||
*/ |
||||
private String title; |
||||
|
||||
/** |
||||
* 内容 |
||||
*/ |
||||
private String content; |
||||
|
||||
/** |
||||
* 点赞数 |
||||
*/ |
||||
private Integer thumbNum; |
||||
|
||||
/** |
||||
* 收藏数 |
||||
*/ |
||||
private Integer favourNum; |
||||
|
||||
/** |
||||
* 创建用户 id |
||||
*/ |
||||
private Long userId; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
/** |
||||
* 更新时间 |
||||
*/ |
||||
private Date updateTime; |
||||
|
||||
/** |
||||
* 标签列表 |
||||
*/ |
||||
private List<String> tagList; |
||||
|
||||
/** |
||||
* 创建人信息 |
||||
*/ |
||||
private UserVO user; |
||||
|
||||
/** |
||||
* 是否已点赞 |
||||
*/ |
||||
private Boolean hasThumb; |
||||
|
||||
/** |
||||
* 是否已收藏 |
||||
*/ |
||||
private Boolean hasFavour; |
||||
|
||||
/** |
||||
* 包装类转对象 |
||||
* |
||||
* @param postVO |
||||
* @return |
||||
*/ |
||||
public static Post voToObj(PostVO postVO) { |
||||
if (postVO == null) { |
||||
return null; |
||||
} |
||||
Post post = new Post(); |
||||
BeanUtils.copyProperties(postVO, post); |
||||
List<String> tagList = postVO.getTagList(); |
||||
if (tagList != null) { |
||||
post.setTags(GSON.toJson(tagList)); |
||||
} |
||||
return post; |
||||
} |
||||
|
||||
/** |
||||
* 对象转包装类 |
||||
* |
||||
* @param post |
||||
* @return |
||||
*/ |
||||
public static PostVO objToVo(Post post) { |
||||
if (post == null) { |
||||
return null; |
||||
} |
||||
PostVO postVO = new PostVO(); |
||||
BeanUtils.copyProperties(post, postVO); |
||||
postVO.setTagList(GSON.fromJson(post.getTags(), new TypeToken<List<String>>() { |
||||
}.getType())); |
||||
return postVO; |
||||
} |
||||
} |
@ -0,0 +1,45 @@ |
||||
package cc.bnblogs.springbootinit.model.vo; |
||||
|
||||
import java.io.Serializable; |
||||
import java.util.Date; |
||||
import lombok.Data; |
||||
|
||||
/** |
||||
* 用户视图(脱敏) |
||||
* 页面显示所需要的数据 |
||||
*/ |
||||
@Data |
||||
public class UserVO implements Serializable { |
||||
|
||||
/** |
||||
* id |
||||
*/ |
||||
private Long id; |
||||
|
||||
/** |
||||
* 用户昵称 |
||||
*/ |
||||
private String userName; |
||||
|
||||
/** |
||||
* 用户头像 |
||||
*/ |
||||
private String userAvatar; |
||||
|
||||
/** |
||||
* 用户简介 |
||||
*/ |
||||
private String userProfile; |
||||
|
||||
/** |
||||
* 用户角色:user/admin/ban |
||||
*/ |
||||
private String userRole; |
||||
|
||||
/** |
||||
* 创建时间 |
||||
*/ |
||||
private Date createTime; |
||||
|
||||
private static final long serialVersionUID = 1L; |
||||
} |
@ -0,0 +1,44 @@ |
||||
package cc.bnblogs.springbootinit.service; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.PostFavour; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper; |
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
/** |
||||
* 帖子收藏服务 |
||||
*/ |
||||
public interface PostFavourService extends IService<PostFavour> { |
||||
|
||||
/** |
||||
* 帖子收藏 |
||||
* |
||||
* @param postId |
||||
* @param loginUser |
||||
* @return |
||||
*/ |
||||
int doPostFavour(long postId, User loginUser); |
||||
|
||||
/** |
||||
* 分页获取用户收藏的帖子列表 |
||||
* |
||||
* @param page |
||||
* @param queryWrapper |
||||
* @param favourUserId |
||||
* @return |
||||
*/ |
||||
Page<Post> listFavourPostByPage(IPage<Post> page, Wrapper<Post> queryWrapper, |
||||
long favourUserId); |
||||
|
||||
/** |
||||
* 帖子收藏(内部服务) |
||||
* |
||||
* @param userId |
||||
* @param postId |
||||
* @return |
||||
*/ |
||||
int doPostFavourInner(long userId, long postId); |
||||
} |
@ -0,0 +1,58 @@ |
||||
package cc.bnblogs.springbootinit.service; |
||||
|
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.vo.PostVO; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* 帖子服务 |
||||
*/ |
||||
public interface PostService extends IService<Post> { |
||||
|
||||
/** |
||||
* 校验 |
||||
* |
||||
* @param post |
||||
* @param add |
||||
*/ |
||||
void validPost(Post post, boolean add); |
||||
|
||||
/** |
||||
* 获取查询条件 |
||||
* |
||||
* @param postQueryRequest |
||||
* @return |
||||
*/ |
||||
QueryWrapper<Post> getQueryWrapper(PostQueryRequest postQueryRequest); |
||||
|
||||
/** |
||||
* 从 ES 查询 |
||||
* |
||||
* @param postQueryRequest |
||||
* @return |
||||
*/ |
||||
Page<Post> searchFromEs(PostQueryRequest postQueryRequest); |
||||
|
||||
/** |
||||
* 获取帖子封装 |
||||
* |
||||
* @param post |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
PostVO getPostVO(Post post, HttpServletRequest request); |
||||
|
||||
/** |
||||
* 分页获取帖子封装 |
||||
* |
||||
* @param postPage |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
Page<PostVO> getPostVOPage(Page<Post> postPage, HttpServletRequest request); |
||||
} |
@ -0,0 +1,29 @@ |
||||
package cc.bnblogs.springbootinit.service; |
||||
|
||||
import cc.bnblogs.springbootinit.model.entity.PostThumb; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
/** |
||||
* 帖子点赞服务 |
||||
*/ |
||||
public interface PostThumbService extends IService<PostThumb> { |
||||
|
||||
/** |
||||
* 点赞 |
||||
* |
||||
* @param postId |
||||
* @param loginUser |
||||
* @return |
||||
*/ |
||||
int doPostThumb(long postId, User loginUser); |
||||
|
||||
/** |
||||
* 帖子点赞(内部服务) |
||||
* |
||||
* @param userId |
||||
* @param postId |
||||
* @return |
||||
*/ |
||||
int doPostThumbInner(long userId, long postId); |
||||
} |
@ -0,0 +1,119 @@ |
||||
package cc.bnblogs.springbootinit.service; |
||||
|
||||
import cc.bnblogs.springbootinit.model.dto.user.UserQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.vo.LoginUserVO; |
||||
import cc.bnblogs.springbootinit.model.vo.UserVO; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.IService; |
||||
|
||||
import java.util.List; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo; |
||||
|
||||
/** |
||||
* 用户服务 |
||||
*/ |
||||
public interface UserService extends IService<User> { |
||||
|
||||
/** |
||||
* 用户注册 |
||||
* |
||||
* @param userAccount 用户账户 |
||||
* @param userPassword 用户密码 |
||||
* @param checkPassword 校验密码 |
||||
* @return 新用户 id |
||||
*/ |
||||
long userRegister(String userAccount, String userPassword, String checkPassword); |
||||
|
||||
/** |
||||
* 用户登录 |
||||
* |
||||
* @param userAccount 用户账户 |
||||
* @param userPassword 用户密码 |
||||
* @param request |
||||
* @return 脱敏后的用户信息 |
||||
*/ |
||||
LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request); |
||||
|
||||
/** |
||||
* 用户登录(微信开放平台) |
||||
* |
||||
* @param wxOAuth2UserInfo 从微信获取的用户信息 |
||||
* @param request |
||||
* @return 脱敏后的用户信息 |
||||
*/ |
||||
LoginUserVO userLoginByMpOpen(WxOAuth2UserInfo wxOAuth2UserInfo, HttpServletRequest request); |
||||
|
||||
/** |
||||
* 获取当前登录用户 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
User getLoginUser(HttpServletRequest request); |
||||
|
||||
/** |
||||
* 获取当前登录用户(允许未登录) |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
User getLoginUserPermitNull(HttpServletRequest request); |
||||
|
||||
/** |
||||
* 是否为管理员 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
boolean isAdmin(HttpServletRequest request); |
||||
|
||||
/** |
||||
* 是否为管理员 |
||||
* |
||||
* @param user |
||||
* @return |
||||
*/ |
||||
boolean isAdmin(User user); |
||||
|
||||
/** |
||||
* 用户注销 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
boolean userLogout(HttpServletRequest request); |
||||
|
||||
/** |
||||
* 获取脱敏的已登录用户信息 |
||||
* |
||||
* @return |
||||
*/ |
||||
LoginUserVO getLoginUserVO(User user); |
||||
|
||||
/** |
||||
* 获取脱敏的用户信息 |
||||
* |
||||
* @param user |
||||
* @return |
||||
*/ |
||||
UserVO getUserVO(User user); |
||||
|
||||
/** |
||||
* 获取脱敏的用户信息 |
||||
* |
||||
* @param userList |
||||
* @return |
||||
*/ |
||||
List<UserVO> getUserVO(List<User> userList); |
||||
|
||||
/** |
||||
* 获取查询条件 |
||||
* |
||||
* @param userQueryRequest |
||||
* @return |
||||
*/ |
||||
QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest); |
||||
|
||||
} |
@ -0,0 +1,113 @@ |
||||
package cc.bnblogs.springbootinit.service.impl; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.PostFavour; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import com.baomidou.mybatisplus.core.conditions.Wrapper; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.core.metadata.IPage; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.mapper.PostFavourMapper; |
||||
import cc.bnblogs.springbootinit.service.PostFavourService; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
import javax.annotation.Resource; |
||||
import org.springframework.aop.framework.AopContext; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.transaction.annotation.Transactional; |
||||
|
||||
/** |
||||
* 帖子收藏服务实现 |
||||
*/ |
||||
@Service |
||||
public class PostFavourServiceImpl extends ServiceImpl<PostFavourMapper, PostFavour> |
||||
implements PostFavourService { |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
/** |
||||
* 帖子收藏 |
||||
* |
||||
* @param postId |
||||
* @param loginUser |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public int doPostFavour(long postId, User loginUser) { |
||||
// 判断是否存在
|
||||
Post post = postService.getById(postId); |
||||
if (post == null) { |
||||
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); |
||||
} |
||||
// 是否已帖子收藏
|
||||
long userId = loginUser.getId(); |
||||
// 每个用户串行帖子收藏
|
||||
// 锁必须要包裹住事务方法
|
||||
PostFavourService postFavourService = (PostFavourService) AopContext.currentProxy(); |
||||
synchronized (String.valueOf(userId).intern()) { |
||||
return postFavourService.doPostFavourInner(userId, postId); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public Page<Post> listFavourPostByPage(IPage<Post> page, Wrapper<Post> queryWrapper, long favourUserId) { |
||||
if (favourUserId <= 0) { |
||||
return new Page<>(); |
||||
} |
||||
return baseMapper.listFavourPostByPage(page, queryWrapper, favourUserId); |
||||
} |
||||
|
||||
/** |
||||
* 封装了事务的方法 |
||||
* |
||||
* @param userId |
||||
* @param postId |
||||
* @return |
||||
*/ |
||||
@Override |
||||
@Transactional(rollbackFor = Exception.class) |
||||
public int doPostFavourInner(long userId, long postId) { |
||||
PostFavour postFavour = new PostFavour(); |
||||
postFavour.setUserId(userId); |
||||
postFavour.setPostId(postId); |
||||
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>(postFavour); |
||||
PostFavour oldPostFavour = this.getOne(postFavourQueryWrapper); |
||||
boolean result; |
||||
// 已收藏
|
||||
if (oldPostFavour != null) { |
||||
result = this.remove(postFavourQueryWrapper); |
||||
if (result) { |
||||
// 帖子收藏数 - 1
|
||||
result = postService.update() |
||||
.eq("id", postId) |
||||
.gt("favourNum", 0) |
||||
.setSql("favourNum = favourNum - 1") |
||||
.update(); |
||||
return result ? -1 : 0; |
||||
} else { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR); |
||||
} |
||||
} else { |
||||
// 未帖子收藏
|
||||
result = this.save(postFavour); |
||||
if (result) { |
||||
// 帖子收藏数 + 1
|
||||
result = postService.update() |
||||
.eq("id", postId) |
||||
.setSql("favourNum = favourNum + 1") |
||||
.update(); |
||||
return result ? 1 : 0; |
||||
} else { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,314 @@ |
||||
package cc.bnblogs.springbootinit.service.impl; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEsDTO; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.PostFavour; |
||||
import cc.bnblogs.springbootinit.model.entity.PostThumb; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.vo.PostVO; |
||||
import cc.bnblogs.springbootinit.model.vo.UserVO; |
||||
import cc.bnblogs.springbootinit.utils.SqlUtils; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import com.google.gson.Gson; |
||||
import cc.bnblogs.springbootinit.constant.CommonConstant; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.exception.ThrowUtils; |
||||
import cc.bnblogs.springbootinit.mapper.PostFavourMapper; |
||||
import cc.bnblogs.springbootinit.mapper.PostMapper; |
||||
import cc.bnblogs.springbootinit.mapper.PostThumbMapper; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.HashMap; |
||||
import java.util.List; |
||||
import java.util.Map; |
||||
import java.util.Set; |
||||
import java.util.stream.Collectors; |
||||
import javax.annotation.Resource; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import org.apache.commons.collections4.CollectionUtils; |
||||
import org.apache.commons.lang3.ObjectUtils; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.elasticsearch.index.query.BoolQueryBuilder; |
||||
import org.elasticsearch.index.query.QueryBuilders; |
||||
import org.elasticsearch.search.sort.SortBuilder; |
||||
import org.elasticsearch.search.sort.SortBuilders; |
||||
import org.elasticsearch.search.sort.SortOrder; |
||||
import org.springframework.data.domain.PageRequest; |
||||
import org.springframework.data.elasticsearch.core.ElasticsearchRestTemplate; |
||||
import org.springframework.data.elasticsearch.core.SearchHit; |
||||
import org.springframework.data.elasticsearch.core.SearchHits; |
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQuery; |
||||
import org.springframework.data.elasticsearch.core.query.NativeSearchQueryBuilder; |
||||
import org.springframework.stereotype.Service; |
||||
|
||||
/** |
||||
* 帖子服务实现 |
||||
*/ |
||||
@Service |
||||
@Slf4j |
||||
public class PostServiceImpl extends ServiceImpl<PostMapper, Post> implements PostService { |
||||
|
||||
private final static Gson GSON = new Gson(); |
||||
|
||||
@Resource |
||||
private UserService userService; |
||||
|
||||
@Resource |
||||
private PostThumbMapper postThumbMapper; |
||||
|
||||
@Resource |
||||
private PostFavourMapper postFavourMapper; |
||||
|
||||
@Resource |
||||
private ElasticsearchRestTemplate elasticsearchRestTemplate; |
||||
|
||||
@Override |
||||
public void validPost(Post post, boolean add) { |
||||
if (post == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR); |
||||
} |
||||
String title = post.getTitle(); |
||||
String content = post.getContent(); |
||||
String tags = post.getTags(); |
||||
// 创建时,参数不能为空
|
||||
if (add) { |
||||
ThrowUtils.throwIf(StringUtils.isAnyBlank(title, content, tags), ErrorCode.PARAMS_ERROR); |
||||
} |
||||
// 有参数则校验
|
||||
if (StringUtils.isNotBlank(title) && title.length() > 80) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "标题过长"); |
||||
} |
||||
if (StringUtils.isNotBlank(content) && content.length() > 8192) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "内容过长"); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取查询包装类 |
||||
* |
||||
* @param postQueryRequest |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public QueryWrapper<Post> getQueryWrapper(PostQueryRequest postQueryRequest) { |
||||
QueryWrapper<Post> queryWrapper = new QueryWrapper<>(); |
||||
if (postQueryRequest == null) { |
||||
return queryWrapper; |
||||
} |
||||
String searchText = postQueryRequest.getSearchText(); |
||||
String sortField = postQueryRequest.getSortField(); |
||||
String sortOrder = postQueryRequest.getSortOrder(); |
||||
Long id = postQueryRequest.getId(); |
||||
String title = postQueryRequest.getTitle(); |
||||
String content = postQueryRequest.getContent(); |
||||
List<String> tagList = postQueryRequest.getTags(); |
||||
Long userId = postQueryRequest.getUserId(); |
||||
Long notId = postQueryRequest.getNotId(); |
||||
// 拼接查询条件
|
||||
if (StringUtils.isNotBlank(searchText)) { |
||||
queryWrapper.like("title", searchText).or().like("content", searchText); |
||||
} |
||||
queryWrapper.like(StringUtils.isNotBlank(title), "title", title); |
||||
queryWrapper.like(StringUtils.isNotBlank(content), "content", content); |
||||
if (CollectionUtils.isNotEmpty(tagList)) { |
||||
for (String tag : tagList) { |
||||
queryWrapper.like("tags", "\"" + tag + "\""); |
||||
} |
||||
} |
||||
queryWrapper.ne(ObjectUtils.isNotEmpty(notId), "id", notId); |
||||
queryWrapper.eq(ObjectUtils.isNotEmpty(id), "id", id); |
||||
queryWrapper.eq(ObjectUtils.isNotEmpty(userId), "userId", userId); |
||||
queryWrapper.eq("isDelete", false); |
||||
queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), |
||||
sortField); |
||||
return queryWrapper; |
||||
} |
||||
|
||||
@Override |
||||
public Page<Post> searchFromEs(PostQueryRequest postQueryRequest) { |
||||
Long id = postQueryRequest.getId(); |
||||
Long notId = postQueryRequest.getNotId(); |
||||
String searchText = postQueryRequest.getSearchText(); |
||||
String title = postQueryRequest.getTitle(); |
||||
String content = postQueryRequest.getContent(); |
||||
List<String> tagList = postQueryRequest.getTags(); |
||||
List<String> orTagList = postQueryRequest.getOrTags(); |
||||
Long userId = postQueryRequest.getUserId(); |
||||
// es 起始页为 0
|
||||
long current = postQueryRequest.getCurrent() - 1; |
||||
long pageSize = postQueryRequest.getPageSize(); |
||||
String sortField = postQueryRequest.getSortField(); |
||||
String sortOrder = postQueryRequest.getSortOrder(); |
||||
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery(); |
||||
// 过滤
|
||||
boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0)); |
||||
if (id != null) { |
||||
boolQueryBuilder.filter(QueryBuilders.termQuery("id", id)); |
||||
} |
||||
if (notId != null) { |
||||
boolQueryBuilder.mustNot(QueryBuilders.termQuery("id", notId)); |
||||
} |
||||
if (userId != null) { |
||||
boolQueryBuilder.filter(QueryBuilders.termQuery("userId", userId)); |
||||
} |
||||
// 必须包含所有标签
|
||||
if (CollectionUtils.isNotEmpty(tagList)) { |
||||
for (String tag : tagList) { |
||||
boolQueryBuilder.filter(QueryBuilders.termQuery("tags", tag)); |
||||
} |
||||
} |
||||
// 包含任何一个标签即可
|
||||
if (CollectionUtils.isNotEmpty(orTagList)) { |
||||
BoolQueryBuilder orTagBoolQueryBuilder = QueryBuilders.boolQuery(); |
||||
for (String tag : orTagList) { |
||||
orTagBoolQueryBuilder.should(QueryBuilders.termQuery("tags", tag)); |
||||
} |
||||
orTagBoolQueryBuilder.minimumShouldMatch(1); |
||||
boolQueryBuilder.filter(orTagBoolQueryBuilder); |
||||
} |
||||
// 按关键词检索
|
||||
if (StringUtils.isNotBlank(searchText)) { |
||||
boolQueryBuilder.should(QueryBuilders.matchQuery("title", searchText)); |
||||
boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText)); |
||||
boolQueryBuilder.should(QueryBuilders.matchQuery("content", searchText)); |
||||
boolQueryBuilder.minimumShouldMatch(1); |
||||
} |
||||
// 按标题检索
|
||||
if (StringUtils.isNotBlank(title)) { |
||||
boolQueryBuilder.should(QueryBuilders.matchQuery("title", title)); |
||||
boolQueryBuilder.minimumShouldMatch(1); |
||||
} |
||||
// 按内容检索
|
||||
if (StringUtils.isNotBlank(content)) { |
||||
boolQueryBuilder.should(QueryBuilders.matchQuery("content", content)); |
||||
boolQueryBuilder.minimumShouldMatch(1); |
||||
} |
||||
// 排序
|
||||
SortBuilder<?> sortBuilder = SortBuilders.scoreSort(); |
||||
if (StringUtils.isNotBlank(sortField)) { |
||||
sortBuilder = SortBuilders.fieldSort(sortField); |
||||
sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC); |
||||
} |
||||
// 分页
|
||||
PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize); |
||||
// 构造查询
|
||||
NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder) |
||||
.withPageable(pageRequest).withSorts(sortBuilder).build(); |
||||
SearchHits<PostEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, PostEsDTO.class); |
||||
Page<Post> page = new Page<>(); |
||||
page.setTotal(searchHits.getTotalHits()); |
||||
List<Post> resourceList = new ArrayList<>(); |
||||
// 查出结果后,从 db 获取最新动态数据(比如点赞数)
|
||||
if (searchHits.hasSearchHits()) { |
||||
List<SearchHit<PostEsDTO>> searchHitList = searchHits.getSearchHits(); |
||||
List<Long> postIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId()) |
||||
.collect(Collectors.toList()); |
||||
List<Post> postList = baseMapper.selectBatchIds(postIdList); |
||||
if (postList != null) { |
||||
Map<Long, List<Post>> idPostMap = postList.stream().collect(Collectors.groupingBy(Post::getId)); |
||||
postIdList.forEach(postId -> { |
||||
if (idPostMap.containsKey(postId)) { |
||||
resourceList.add(idPostMap.get(postId).get(0)); |
||||
} else { |
||||
// 从 es 清空 db 已物理删除的数据
|
||||
String delete = elasticsearchRestTemplate.delete(String.valueOf(postId), PostEsDTO.class); |
||||
log.info("delete post {}", delete); |
||||
} |
||||
}); |
||||
} |
||||
} |
||||
page.setRecords(resourceList); |
||||
return page; |
||||
} |
||||
|
||||
@Override |
||||
public PostVO getPostVO(Post post, HttpServletRequest request) { |
||||
PostVO postVO = PostVO.objToVo(post); |
||||
long postId = post.getId(); |
||||
// 1. 关联查询用户信息
|
||||
Long userId = post.getUserId(); |
||||
User user = null; |
||||
if (userId != null && userId > 0) { |
||||
user = userService.getById(userId); |
||||
} |
||||
UserVO userVO = userService.getUserVO(user); |
||||
postVO.setUser(userVO); |
||||
// 2. 已登录,获取用户点赞、收藏状态
|
||||
User loginUser = userService.getLoginUserPermitNull(request); |
||||
if (loginUser != null) { |
||||
// 获取点赞
|
||||
QueryWrapper<PostThumb> postThumbQueryWrapper = new QueryWrapper<>(); |
||||
postThumbQueryWrapper.in("postId", postId); |
||||
postThumbQueryWrapper.eq("userId", loginUser.getId()); |
||||
PostThumb postThumb = postThumbMapper.selectOne(postThumbQueryWrapper); |
||||
postVO.setHasThumb(postThumb != null); |
||||
// 获取收藏
|
||||
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>(); |
||||
postFavourQueryWrapper.in("postId", postId); |
||||
postFavourQueryWrapper.eq("userId", loginUser.getId()); |
||||
PostFavour postFavour = postFavourMapper.selectOne(postFavourQueryWrapper); |
||||
postVO.setHasFavour(postFavour != null); |
||||
} |
||||
return postVO; |
||||
} |
||||
|
||||
@Override |
||||
public Page<PostVO> getPostVOPage(Page<Post> postPage, HttpServletRequest request) { |
||||
List<Post> postList = postPage.getRecords(); |
||||
Page<PostVO> postVOPage = new Page<>(postPage.getCurrent(), postPage.getSize(), postPage.getTotal()); |
||||
if (CollectionUtils.isEmpty(postList)) { |
||||
return postVOPage; |
||||
} |
||||
// 1. 关联查询用户信息
|
||||
Set<Long> userIdSet = postList.stream().map(Post::getUserId).collect(Collectors.toSet()); |
||||
Map<Long, List<User>> userIdUserListMap = userService.listByIds(userIdSet).stream() |
||||
.collect(Collectors.groupingBy(User::getId)); |
||||
// 2. 已登录,获取用户点赞、收藏状态
|
||||
Map<Long, Boolean> postIdHasThumbMap = new HashMap<>(); |
||||
Map<Long, Boolean> postIdHasFavourMap = new HashMap<>(); |
||||
User loginUser = userService.getLoginUserPermitNull(request); |
||||
if (loginUser != null) { |
||||
Set<Long> postIdSet = postList.stream().map(Post::getId).collect(Collectors.toSet()); |
||||
loginUser = userService.getLoginUser(request); |
||||
// 获取点赞
|
||||
QueryWrapper<PostThumb> postThumbQueryWrapper = new QueryWrapper<>(); |
||||
postThumbQueryWrapper.in("postId", postIdSet); |
||||
postThumbQueryWrapper.eq("userId", loginUser.getId()); |
||||
List<PostThumb> postPostThumbList = postThumbMapper.selectList(postThumbQueryWrapper); |
||||
postPostThumbList.forEach(postPostThumb -> postIdHasThumbMap.put(postPostThumb.getPostId(), true)); |
||||
// 获取收藏
|
||||
QueryWrapper<PostFavour> postFavourQueryWrapper = new QueryWrapper<>(); |
||||
postFavourQueryWrapper.in("postId", postIdSet); |
||||
postFavourQueryWrapper.eq("userId", loginUser.getId()); |
||||
List<PostFavour> postFavourList = postFavourMapper.selectList(postFavourQueryWrapper); |
||||
postFavourList.forEach(postFavour -> postIdHasFavourMap.put(postFavour.getPostId(), true)); |
||||
} |
||||
// 填充信息
|
||||
List<PostVO> postVOList = postList.stream().map(post -> { |
||||
PostVO postVO = PostVO.objToVo(post); |
||||
Long userId = post.getUserId(); |
||||
User user = null; |
||||
if (userIdUserListMap.containsKey(userId)) { |
||||
user = userIdUserListMap.get(userId).get(0); |
||||
} |
||||
postVO.setUser(userService.getUserVO(user)); |
||||
postVO.setHasThumb(postIdHasThumbMap.getOrDefault(post.getId(), false)); |
||||
postVO.setHasFavour(postIdHasFavourMap.getOrDefault(post.getId(), false)); |
||||
return postVO; |
||||
}).collect(Collectors.toList()); |
||||
postVOPage.setRecords(postVOList); |
||||
return postVOPage; |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,102 @@ |
||||
package cc.bnblogs.springbootinit.service.impl; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.model.entity.PostThumb; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.mapper.PostThumbMapper; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
import cc.bnblogs.springbootinit.service.PostThumbService; |
||||
import javax.annotation.Resource; |
||||
import org.springframework.aop.framework.AopContext; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.transaction.annotation.Transactional; |
||||
|
||||
/** |
||||
* 帖子点赞服务实现 |
||||
*/ |
||||
@Service |
||||
public class PostThumbServiceImpl extends ServiceImpl<PostThumbMapper, PostThumb> |
||||
implements PostThumbService { |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
/** |
||||
* 点赞 |
||||
* |
||||
* @param postId |
||||
* @param loginUser |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public int doPostThumb(long postId, User loginUser) { |
||||
// 判断实体是否存在,根据类别获取实体
|
||||
Post post = postService.getById(postId); |
||||
if (post == null) { |
||||
throw new BusinessException(ErrorCode.NOT_FOUND_ERROR); |
||||
} |
||||
// 是否已点赞
|
||||
long userId = loginUser.getId(); |
||||
// 每个用户串行点赞
|
||||
// 锁必须要包裹住事务方法
|
||||
PostThumbService postThumbService = (PostThumbService) AopContext.currentProxy(); |
||||
synchronized (String.valueOf(userId).intern()) { |
||||
return postThumbService.doPostThumbInner(userId, postId); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 封装了事务的方法 |
||||
* |
||||
* @param userId |
||||
* @param postId |
||||
* @return |
||||
*/ |
||||
@Override |
||||
@Transactional(rollbackFor = Exception.class) |
||||
public int doPostThumbInner(long userId, long postId) { |
||||
PostThumb postThumb = new PostThumb(); |
||||
postThumb.setUserId(userId); |
||||
postThumb.setPostId(postId); |
||||
QueryWrapper<PostThumb> thumbQueryWrapper = new QueryWrapper<>(postThumb); |
||||
PostThumb oldPostThumb = this.getOne(thumbQueryWrapper); |
||||
boolean result; |
||||
// 已点赞
|
||||
if (oldPostThumb != null) { |
||||
result = this.remove(thumbQueryWrapper); |
||||
if (result) { |
||||
// 点赞数 - 1
|
||||
result = postService.update() |
||||
.eq("id", postId) |
||||
.gt("thumbNum", 0) |
||||
.setSql("thumbNum = thumbNum - 1") |
||||
.update(); |
||||
return result ? -1 : 0; |
||||
} else { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR); |
||||
} |
||||
} else { |
||||
// 未点赞
|
||||
result = this.save(postThumb); |
||||
if (result) { |
||||
// 点赞数 + 1
|
||||
result = postService.update() |
||||
.eq("id", postId) |
||||
.setSql("thumbNum = thumbNum + 1") |
||||
.update(); |
||||
return result ? 1 : 0; |
||||
} else { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR); |
||||
} |
||||
} |
||||
} |
||||
|
||||
} |
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,270 @@ |
||||
package cc.bnblogs.springbootinit.service.impl; |
||||
|
||||
import static cc.bnblogs.springbootinit.constant.UserConstant.USER_LOGIN_STATE; |
||||
|
||||
import cc.bnblogs.springbootinit.common.ErrorCode; |
||||
import cc.bnblogs.springbootinit.model.dto.user.UserQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.User; |
||||
import cc.bnblogs.springbootinit.model.enums.UserRoleEnum; |
||||
import cc.bnblogs.springbootinit.model.vo.LoginUserVO; |
||||
import cc.bnblogs.springbootinit.model.vo.UserVO; |
||||
import cc.bnblogs.springbootinit.utils.SqlUtils; |
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; |
||||
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; |
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; |
||||
import cc.bnblogs.springbootinit.constant.CommonConstant; |
||||
import cc.bnblogs.springbootinit.exception.BusinessException; |
||||
import cc.bnblogs.springbootinit.mapper.UserMapper; |
||||
import cc.bnblogs.springbootinit.service.UserService; |
||||
|
||||
import java.util.ArrayList; |
||||
import java.util.List; |
||||
import java.util.stream.Collectors; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
import lombok.extern.slf4j.Slf4j; |
||||
import me.chanjar.weixin.common.bean.WxOAuth2UserInfo; |
||||
import org.apache.commons.lang3.StringUtils; |
||||
import org.springframework.beans.BeanUtils; |
||||
import org.springframework.stereotype.Service; |
||||
import org.springframework.util.DigestUtils; |
||||
|
||||
/** |
||||
* 用户服务实现 |
||||
*/ |
||||
@Service |
||||
@Slf4j |
||||
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService { |
||||
|
||||
/** |
||||
* 盐值,混淆密码 |
||||
*/ |
||||
private static final String SALT = "demo"; |
||||
|
||||
@Override |
||||
public long userRegister(String userAccount, String userPassword, String checkPassword) { |
||||
// 1. 校验
|
||||
if (StringUtils.isAnyBlank(userAccount, userPassword, checkPassword)) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空"); |
||||
} |
||||
if (userAccount.length() < 4) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户账号过短"); |
||||
} |
||||
if (userPassword.length() < 8 || checkPassword.length() < 8) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户密码过短"); |
||||
} |
||||
// 密码和校验密码相同
|
||||
if (!userPassword.equals(checkPassword)) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "两次输入的密码不一致"); |
||||
} |
||||
synchronized (userAccount.intern()) { |
||||
// 账户不能重复
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); |
||||
queryWrapper.eq("userAccount", userAccount); |
||||
long count = this.baseMapper.selectCount(queryWrapper); |
||||
if (count > 0) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号重复"); |
||||
} |
||||
// 2. 加密
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes()); |
||||
// 3. 插入数据
|
||||
User user = new User(); |
||||
user.setUserAccount(userAccount); |
||||
user.setUserPassword(encryptPassword); |
||||
boolean saveResult = this.save(user); |
||||
if (!saveResult) { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "注册失败,数据库错误"); |
||||
} |
||||
return user.getId(); |
||||
} |
||||
} |
||||
|
||||
@Override |
||||
public LoginUserVO userLogin(String userAccount, String userPassword, HttpServletRequest request) { |
||||
// 1. 校验
|
||||
if (StringUtils.isAnyBlank(userAccount, userPassword)) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "参数为空"); |
||||
} |
||||
if (userAccount.length() < 4) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "账号错误"); |
||||
} |
||||
if (userPassword.length() < 8) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "密码错误"); |
||||
} |
||||
// 2. 加密
|
||||
String encryptPassword = DigestUtils.md5DigestAsHex((SALT + userPassword).getBytes()); |
||||
// 查询用户是否存在
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); |
||||
queryWrapper.eq("userAccount", userAccount); |
||||
queryWrapper.eq("userPassword", encryptPassword); |
||||
User user = this.baseMapper.selectOne(queryWrapper); |
||||
// 用户不存在
|
||||
if (user == null) { |
||||
log.info("user login failed, userAccount cannot match userPassword"); |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "用户不存在或密码错误"); |
||||
} |
||||
// 3. 记录用户的登录态
|
||||
request.getSession().setAttribute(USER_LOGIN_STATE, user); |
||||
return this.getLoginUserVO(user); |
||||
} |
||||
|
||||
@Override |
||||
public LoginUserVO userLoginByMpOpen(WxOAuth2UserInfo wxOAuth2UserInfo, HttpServletRequest request) { |
||||
String unionId = wxOAuth2UserInfo.getUnionId(); |
||||
String mpOpenId = wxOAuth2UserInfo.getOpenid(); |
||||
// 单机锁
|
||||
synchronized (unionId.intern()) { |
||||
// 查询用户是否已存在
|
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); |
||||
queryWrapper.eq("unionId", unionId); |
||||
User user = this.getOne(queryWrapper); |
||||
// 被封号,禁止登录
|
||||
if (user != null && UserRoleEnum.BAN.getValue().equals(user.getUserRole())) { |
||||
throw new BusinessException(ErrorCode.FORBIDDEN_ERROR, "该用户已被封,禁止登录"); |
||||
} |
||||
// 用户不存在则创建
|
||||
if (user == null) { |
||||
user = new User(); |
||||
user.setUnionId(unionId); |
||||
user.setMpOpenId(mpOpenId); |
||||
user.setUserAvatar(wxOAuth2UserInfo.getHeadImgUrl()); |
||||
user.setUserName(wxOAuth2UserInfo.getNickname()); |
||||
boolean result = this.save(user); |
||||
if (!result) { |
||||
throw new BusinessException(ErrorCode.SYSTEM_ERROR, "登录失败"); |
||||
} |
||||
} |
||||
// 记录用户的登录态
|
||||
request.getSession().setAttribute(USER_LOGIN_STATE, user); |
||||
return getLoginUserVO(user); |
||||
} |
||||
} |
||||
|
||||
/** |
||||
* 获取当前登录用户 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public User getLoginUser(HttpServletRequest request) { |
||||
// 先判断是否已登录
|
||||
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE); |
||||
User currentUser = (User) userObj; |
||||
if (currentUser == null || currentUser.getId() == null) { |
||||
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); |
||||
} |
||||
// 从数据库查询(追求性能的话可以注释,直接走缓存)
|
||||
long userId = currentUser.getId(); |
||||
currentUser = this.getById(userId); |
||||
if (currentUser == null) { |
||||
throw new BusinessException(ErrorCode.NOT_LOGIN_ERROR); |
||||
} |
||||
return currentUser; |
||||
} |
||||
|
||||
/** |
||||
* 获取当前登录用户(允许未登录) |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public User getLoginUserPermitNull(HttpServletRequest request) { |
||||
// 先判断是否已登录
|
||||
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE); |
||||
User currentUser = (User) userObj; |
||||
if (currentUser == null || currentUser.getId() == null) { |
||||
return null; |
||||
} |
||||
// 从数据库查询(追求性能的话可以注释,直接走缓存)
|
||||
long userId = currentUser.getId(); |
||||
return this.getById(userId); |
||||
} |
||||
|
||||
/** |
||||
* 是否为管理员 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
@Override |
||||
public boolean isAdmin(HttpServletRequest request) { |
||||
// 仅管理员可查询
|
||||
Object userObj = request.getSession().getAttribute(USER_LOGIN_STATE); |
||||
User user = (User) userObj; |
||||
return isAdmin(user); |
||||
} |
||||
|
||||
@Override |
||||
public boolean isAdmin(User user) { |
||||
return user != null && UserRoleEnum.ADMIN.getValue().equals(user.getUserRole()); |
||||
} |
||||
|
||||
/** |
||||
* 用户注销 |
||||
* |
||||
* @param request |
||||
*/ |
||||
@Override |
||||
public boolean userLogout(HttpServletRequest request) { |
||||
if (request.getSession().getAttribute(USER_LOGIN_STATE) == null) { |
||||
throw new BusinessException(ErrorCode.OPERATION_ERROR, "未登录"); |
||||
} |
||||
// 移除登录态
|
||||
request.getSession().removeAttribute(USER_LOGIN_STATE); |
||||
return true; |
||||
} |
||||
|
||||
@Override |
||||
public LoginUserVO getLoginUserVO(User user) { |
||||
if (user == null) { |
||||
return null; |
||||
} |
||||
LoginUserVO loginUserVO = new LoginUserVO(); |
||||
BeanUtils.copyProperties(user, loginUserVO); |
||||
return loginUserVO; |
||||
} |
||||
|
||||
@Override |
||||
public UserVO getUserVO(User user) { |
||||
if (user == null) { |
||||
return null; |
||||
} |
||||
UserVO userVO = new UserVO(); |
||||
BeanUtils.copyProperties(user, userVO); |
||||
return userVO; |
||||
} |
||||
|
||||
@Override |
||||
public List<UserVO> getUserVO(List<User> userList) { |
||||
if (CollectionUtils.isEmpty(userList)) { |
||||
return new ArrayList<>(); |
||||
} |
||||
return userList.stream().map(this::getUserVO).collect(Collectors.toList()); |
||||
} |
||||
|
||||
@Override |
||||
public QueryWrapper<User> getQueryWrapper(UserQueryRequest userQueryRequest) { |
||||
if (userQueryRequest == null) { |
||||
throw new BusinessException(ErrorCode.PARAMS_ERROR, "请求参数为空"); |
||||
} |
||||
Long id = userQueryRequest.getId(); |
||||
String unionId = userQueryRequest.getUnionId(); |
||||
String mpOpenId = userQueryRequest.getMpOpenId(); |
||||
String userName = userQueryRequest.getUserName(); |
||||
String userProfile = userQueryRequest.getUserProfile(); |
||||
String userRole = userQueryRequest.getUserRole(); |
||||
String sortField = userQueryRequest.getSortField(); |
||||
String sortOrder = userQueryRequest.getSortOrder(); |
||||
QueryWrapper<User> queryWrapper = new QueryWrapper<>(); |
||||
queryWrapper.eq(id != null, "id", id); |
||||
queryWrapper.eq(StringUtils.isNotBlank(unionId), "unionId", unionId); |
||||
queryWrapper.eq(StringUtils.isNotBlank(mpOpenId), "mpOpenId", mpOpenId); |
||||
queryWrapper.eq(StringUtils.isNotBlank(userRole), "userRole", userRole); |
||||
queryWrapper.like(StringUtils.isNotBlank(userProfile), "userProfile", userProfile); |
||||
queryWrapper.like(StringUtils.isNotBlank(userName), "userName", userName); |
||||
queryWrapper.orderBy(SqlUtils.validSortField(sortField), sortOrder.equals(CommonConstant.SORT_ORDER_ASC), |
||||
sortField); |
||||
return queryWrapper; |
||||
} |
||||
} |
@ -0,0 +1,52 @@ |
||||
package cc.bnblogs.springbootinit.utils; |
||||
|
||||
import java.net.InetAddress; |
||||
import javax.servlet.http.HttpServletRequest; |
||||
|
||||
/** |
||||
* 网络工具类 |
||||
*/ |
||||
public class NetUtils { |
||||
|
||||
/** |
||||
* 获取客户端 IP 地址 |
||||
* |
||||
* @param request |
||||
* @return |
||||
*/ |
||||
public static String getIpAddress(HttpServletRequest request) { |
||||
String ip = request.getHeader("x-forwarded-for"); |
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
ip = request.getHeader("Proxy-Client-IP"); |
||||
} |
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
ip = request.getHeader("WL-Proxy-Client-IP"); |
||||
} |
||||
if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { |
||||
ip = request.getRemoteAddr(); |
||||
if (ip.equals("127.0.0.1")) { |
||||
// 根据网卡取本机配置的 IP
|
||||
InetAddress inet = null; |
||||
try { |
||||
inet = InetAddress.getLocalHost(); |
||||
} catch (Exception e) { |
||||
e.printStackTrace(); |
||||
} |
||||
if (inet != null) { |
||||
ip = inet.getHostAddress(); |
||||
} |
||||
} |
||||
} |
||||
// 多个代理的情况,第一个IP为客户端真实IP,多个IP按照','分割
|
||||
if (ip != null && ip.length() > 15) { |
||||
if (ip.indexOf(",") > 0) { |
||||
ip = ip.substring(0, ip.indexOf(",")); |
||||
} |
||||
} |
||||
if (ip == null) { |
||||
return "127.0.0.1"; |
||||
} |
||||
return ip; |
||||
} |
||||
|
||||
} |
@ -0,0 +1,54 @@ |
||||
package cc.bnblogs.springbootinit.utils; |
||||
|
||||
import org.jetbrains.annotations.NotNull; |
||||
import org.springframework.beans.BeansException; |
||||
import org.springframework.context.ApplicationContext; |
||||
import org.springframework.context.ApplicationContextAware; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* Spring 上下文获取工具 |
||||
*/ |
||||
@Component |
||||
public class SpringContextUtils implements ApplicationContextAware { |
||||
|
||||
private static ApplicationContext applicationContext; |
||||
|
||||
@Override |
||||
public void setApplicationContext(@NotNull ApplicationContext applicationContext) throws BeansException { |
||||
SpringContextUtils.applicationContext = applicationContext; |
||||
} |
||||
|
||||
/** |
||||
* 通过名称获取 Bean |
||||
* |
||||
* @param beanName |
||||
* @return |
||||
*/ |
||||
public static Object getBean(String beanName) { |
||||
return applicationContext.getBean(beanName); |
||||
} |
||||
|
||||
/** |
||||
* 通过 class 获取 Bean |
||||
* |
||||
* @param beanClass |
||||
* @param <T> |
||||
* @return |
||||
*/ |
||||
public static <T> T getBean(Class<T> beanClass) { |
||||
return applicationContext.getBean(beanClass); |
||||
} |
||||
|
||||
/** |
||||
* 通过名称和类型获取 Bean |
||||
* |
||||
* @param beanName |
||||
* @param beanClass |
||||
* @param <T> |
||||
* @return |
||||
*/ |
||||
public static <T> T getBean(String beanName, Class<T> beanClass) { |
||||
return applicationContext.getBean(beanName, beanClass); |
||||
} |
||||
} |
@ -0,0 +1,22 @@ |
||||
package cc.bnblogs.springbootinit.utils; |
||||
|
||||
import org.apache.commons.lang3.StringUtils; |
||||
|
||||
/** |
||||
* SQL 工具 |
||||
*/ |
||||
public class SqlUtils { |
||||
|
||||
/** |
||||
* 校验排序字段是否合法(防止 SQL 注入) |
||||
* |
||||
* @param sortField |
||||
* @return |
||||
*/ |
||||
public static boolean validSortField(String sortField) { |
||||
if (StringUtils.isBlank(sortField)) { |
||||
return false; |
||||
} |
||||
return !StringUtils.containsAny(sortField, "=", "(", ")", " "); |
||||
} |
||||
} |
@ -0,0 +1,13 @@ |
||||
package cc.bnblogs.springbootinit.wxmp; |
||||
|
||||
/** |
||||
* 微信公众号相关常量 |
||||
**/ |
||||
public class WxMpConstant { |
||||
|
||||
/** |
||||
* 点击菜单 key |
||||
*/ |
||||
public static final String CLICK_MENU_KEY = "CLICK_MENU_KEY"; |
||||
|
||||
} |
@ -0,0 +1,58 @@ |
||||
package cc.bnblogs.springbootinit.wxmp; |
||||
|
||||
import cc.bnblogs.springbootinit.wxmp.handler.EventHandler; |
||||
import cc.bnblogs.springbootinit.wxmp.handler.MessageHandler; |
||||
import cc.bnblogs.springbootinit.wxmp.handler.SubscribeHandler; |
||||
import javax.annotation.Resource; |
||||
import me.chanjar.weixin.common.api.WxConsts.EventType; |
||||
import me.chanjar.weixin.common.api.WxConsts.XmlMsgType; |
||||
import me.chanjar.weixin.mp.api.WxMpMessageRouter; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import org.springframework.context.annotation.Bean; |
||||
import org.springframework.context.annotation.Configuration; |
||||
|
||||
/** |
||||
* 微信公众号路由 |
||||
*/ |
||||
@Configuration |
||||
public class WxMpMsgRouter { |
||||
|
||||
@Resource |
||||
private WxMpService wxMpService; |
||||
|
||||
@Resource |
||||
private EventHandler eventHandler; |
||||
|
||||
@Resource |
||||
private MessageHandler messageHandler; |
||||
|
||||
@Resource |
||||
private SubscribeHandler subscribeHandler; |
||||
|
||||
@Bean |
||||
public WxMpMessageRouter getWxMsgRouter() { |
||||
WxMpMessageRouter router = new WxMpMessageRouter(wxMpService); |
||||
// 消息
|
||||
router.rule() |
||||
.async(false) |
||||
.msgType(XmlMsgType.TEXT) |
||||
.handler(messageHandler) |
||||
.end(); |
||||
// 关注
|
||||
router.rule() |
||||
.async(false) |
||||
.msgType(XmlMsgType.EVENT) |
||||
.event(EventType.SUBSCRIBE) |
||||
.handler(subscribeHandler) |
||||
.end(); |
||||
// 点击按钮
|
||||
router.rule() |
||||
.async(false) |
||||
.msgType(XmlMsgType.EVENT) |
||||
.event(EventType.CLICK) |
||||
.eventKey(WxMpConstant.CLICK_MENU_KEY) |
||||
.handler(eventHandler) |
||||
.end(); |
||||
return router; |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cc.bnblogs.springbootinit.wxmp.handler; |
||||
|
||||
import java.util.Map; |
||||
import me.chanjar.weixin.common.error.WxErrorException; |
||||
import me.chanjar.weixin.common.session.WxSessionManager; |
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* 事件处理器 |
||||
**/ |
||||
@Component |
||||
public class EventHandler implements WxMpMessageHandler { |
||||
|
||||
@Override |
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, WxMpService wxMpService, |
||||
WxSessionManager wxSessionManager) throws WxErrorException { |
||||
final String content = "您点击了菜单"; |
||||
// 调用接口,返回验证码
|
||||
return WxMpXmlOutMessage.TEXT().content(content) |
||||
.fromUser(wxMpXmlMessage.getToUser()) |
||||
.toUser(wxMpXmlMessage.getFromUser()) |
||||
.build(); |
||||
} |
||||
} |
@ -0,0 +1,27 @@ |
||||
package cc.bnblogs.springbootinit.wxmp.handler; |
||||
|
||||
import java.util.Map; |
||||
import me.chanjar.weixin.common.error.WxErrorException; |
||||
import me.chanjar.weixin.common.session.WxSessionManager; |
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* 消息处理器 |
||||
**/ |
||||
@Component |
||||
public class MessageHandler implements WxMpMessageHandler { |
||||
|
||||
@Override |
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, |
||||
WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { |
||||
String content = "我是复读机:" + wxMpXmlMessage.getContent(); |
||||
return WxMpXmlOutMessage.TEXT().content(content) |
||||
.fromUser(wxMpXmlMessage.getToUser()) |
||||
.toUser(wxMpXmlMessage.getFromUser()) |
||||
.build(); |
||||
} |
||||
} |
@ -0,0 +1,28 @@ |
||||
package cc.bnblogs.springbootinit.wxmp.handler; |
||||
|
||||
import java.util.Map; |
||||
import me.chanjar.weixin.common.error.WxErrorException; |
||||
import me.chanjar.weixin.common.session.WxSessionManager; |
||||
import me.chanjar.weixin.mp.api.WxMpMessageHandler; |
||||
import me.chanjar.weixin.mp.api.WxMpService; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlMessage; |
||||
import me.chanjar.weixin.mp.bean.message.WxMpXmlOutMessage; |
||||
import org.springframework.stereotype.Component; |
||||
|
||||
/** |
||||
* 关注处理器 |
||||
**/ |
||||
@Component |
||||
public class SubscribeHandler implements WxMpMessageHandler { |
||||
|
||||
@Override |
||||
public WxMpXmlOutMessage handle(WxMpXmlMessage wxMpXmlMessage, Map<String, Object> map, |
||||
WxMpService wxMpService, WxSessionManager wxSessionManager) throws WxErrorException { |
||||
final String content = "感谢关注"; |
||||
// 调用接口,返回验证码
|
||||
return WxMpXmlOutMessage.TEXT().content(content) |
||||
.fromUser(wxMpXmlMessage.getToUser()) |
||||
.toUser(wxMpXmlMessage.getFromUser()) |
||||
.build(); |
||||
} |
||||
} |
@ -0,0 +1,34 @@ |
||||
{ |
||||
"properties": [ |
||||
{ |
||||
"name": "cos.client.accessKey", |
||||
"type": "java.lang.String", |
||||
"description": "Description for cos.client.accessKey." |
||||
}, |
||||
{ |
||||
"name": "cos.client.secretKey", |
||||
"type": "java.lang.String", |
||||
"description": "Description for cos.client.secretKey." |
||||
}, |
||||
{ |
||||
"name": "cos.client.region", |
||||
"type": "java.lang.String", |
||||
"description": "Description for cos.client.region." |
||||
}, |
||||
{ |
||||
"name": "cos.client.bucket", |
||||
"type": "java.lang.String", |
||||
"description": "Description for cos.client.bucket." |
||||
}, |
||||
{ |
||||
"name": "wx.open.appId", |
||||
"type": "java.lang.String", |
||||
"description": "Description for wx.open.appId." |
||||
}, |
||||
{ |
||||
"name": "wx.open.appSecret", |
||||
"type": "java.lang.String", |
||||
"description": "Description for wx.open.appSecret." |
||||
} |
||||
] |
||||
} |
@ -0,0 +1,29 @@ |
||||
# 线上配置文件 |
||||
server: |
||||
port: 8101 |
||||
spring: |
||||
# 数据库配置 |
||||
# todo 需替换配置 |
||||
datasource: |
||||
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
url: jdbc:mysql://127.0.0.1:3306/my_db |
||||
username: root |
||||
password: 123456 |
||||
# Redis 配置 |
||||
# todo 需替换配置 |
||||
redis: |
||||
database: 1 |
||||
host: 127.0.0.1 |
||||
port: 6379 |
||||
timeout: 5000 |
||||
password: 123456 |
||||
# Elasticsearch 配置 |
||||
# todo 需替换配置 |
||||
elasticsearch: |
||||
uris: http://127.0.0.1:9200 |
||||
username: elastic |
||||
password: 123456 |
||||
mybatis-plus: |
||||
configuration: |
||||
# 生产环境关闭日志 |
||||
log-impl: '' |
@ -0,0 +1,25 @@ |
||||
# 测试配置文件 |
||||
server: |
||||
port: 8101 |
||||
spring: |
||||
# 数据库配置 |
||||
# todo 需替换配置 |
||||
datasource: |
||||
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
url: jdbc:mysql://localhost:3306/my_db |
||||
username: root |
||||
password: 123456 |
||||
# Redis 配置 |
||||
# todo 需替换配置 |
||||
redis: |
||||
database: 1 |
||||
host: localhost |
||||
port: 6379 |
||||
timeout: 5000 |
||||
password: 123456 |
||||
# Elasticsearch 配置 |
||||
# todo 需替换配置 |
||||
elasticsearch: |
||||
uris: http://localhost:9200 |
||||
username: root |
||||
password: 123456 |
@ -0,0 +1,91 @@ |
||||
# 公共配置文件 |
||||
spring: |
||||
application: |
||||
name: springboot-init |
||||
# 默认 dev 环境 |
||||
profiles: |
||||
active: dev |
||||
# 支持 swagger3 |
||||
# http://localhost:8101/api/doc.html |
||||
mvc: |
||||
pathmatch: |
||||
matching-strategy: ant_path_matcher |
||||
# session 配置 |
||||
session: |
||||
# todo 取消注释开启分布式 session(须先配置 Redis) |
||||
# store-type: redis |
||||
# 30 天过期 |
||||
timeout: 2592000 |
||||
# 数据库配置 |
||||
# todo 需替换配置 |
||||
datasource: |
||||
driver-class-name: com.mysql.cj.jdbc.Driver |
||||
url: jdbc:mysql://127.0.0.1:3306/my_db |
||||
username: root |
||||
password: 123456 |
||||
# Redis 配置 |
||||
# todo 需替换配置,然后取消注释 |
||||
redis: |
||||
database: 1 |
||||
host: 192.168.153.131 |
||||
port: 6379 |
||||
timeout: 5000 |
||||
password: admin |
||||
# Elasticsearch 配置 |
||||
# todo 需替换配置,然后取消注释 |
||||
elasticsearch: |
||||
uris: http://127.0.0.1:9200 |
||||
username: root |
||||
password: 123456 |
||||
# 文件上传 |
||||
servlet: |
||||
multipart: |
||||
# 大小限制 |
||||
max-file-size: 10MB |
||||
server: |
||||
address: 127.0.0.1 |
||||
port: 8101 |
||||
servlet: |
||||
context-path: /api |
||||
# cookie 30 天过期 |
||||
session: |
||||
cookie: |
||||
max-age: 2592000 |
||||
mybatis-plus: |
||||
configuration: |
||||
map-underscore-to-camel-case: false |
||||
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl |
||||
global-config: |
||||
db-config: |
||||
logic-delete-field: isDelete # 全局逻辑删除的实体字段名 |
||||
logic-delete-value: 1 # 逻辑已删除值(默认为 1) |
||||
logic-not-delete-value: 0 # 逻辑未删除值(默认为 0) |
||||
# 微信相关 |
||||
wx: |
||||
# 微信公众平台 |
||||
# todo 需替换配置 |
||||
mp: |
||||
token: xxx |
||||
aesKey: xxx |
||||
appId: xxx |
||||
secret: xxx |
||||
config-storage: |
||||
http-client-type: HttpClient |
||||
key-prefix: wx |
||||
redis: |
||||
host: 127.0.0.1 |
||||
port: 6379 |
||||
type: Memory |
||||
# 微信开放平台 |
||||
# todo 需替换配置 |
||||
open: |
||||
appId: xxx |
||||
appSecret: xxx |
||||
# 对象存储 |
||||
# todo 需替换配置 |
||||
cos: |
||||
client: |
||||
accessKey: xxx |
||||
secretKey: xxx |
||||
region: xxx |
||||
bucket: xxx |
@ -0,0 +1 @@ |
||||
Spring-init |
@ -0,0 +1,27 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE mapper |
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="cc.bnblogs.springbootinit.mapper.PostFavourMapper"> |
||||
|
||||
<resultMap id="BaseResultMap" type="cc.bnblogs.springbootinit.model.entity.PostFavour"> |
||||
<id property="id" column="id" jdbcType="BIGINT"/> |
||||
<result property="postId" column="postId" jdbcType="BIGINT"/> |
||||
<result property="userId" column="userId" jdbcType="BIGINT"/> |
||||
<result property="createTime" column="createTime" jdbcType="TIMESTAMP"/> |
||||
<result property="updateTime" column="updateTime" jdbcType="TIMESTAMP"/> |
||||
</resultMap> |
||||
|
||||
<sql id="Base_Column_List"> |
||||
id,postId,userId, |
||||
createTime,updateTime |
||||
</sql> |
||||
|
||||
<select id="listFavourPostByPage" |
||||
resultType="cc.bnblogs.springbootinit.model.entity.Post"> |
||||
select p.* |
||||
from post p |
||||
join (select postId from post_favour where userId = #{favourUserId}) pf |
||||
on p.id = pf.postId ${ew.customSqlSegment} |
||||
</select> |
||||
</mapper> |
@ -0,0 +1,31 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE mapper |
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="cc.bnblogs.springbootinit.mapper.PostMapper"> |
||||
|
||||
<resultMap id="BaseResultMap" type="cc.bnblogs.springbootinit.model.entity.Post"> |
||||
<id property="id" column="id" jdbcType="BIGINT"/> |
||||
<result property="title" column="title" jdbcType="VARCHAR"/> |
||||
<result property="content" column="content" jdbcType="VARCHAR"/> |
||||
<result property="tags" column="tags" jdbcType="VARCHAR"/> |
||||
<result property="thumbNum" column="thumbNum" jdbcType="BIGINT"/> |
||||
<result property="favourNum" column="favourNum" jdbcType="BIGINT"/> |
||||
<result property="userId" column="userId" jdbcType="BIGINT"/> |
||||
<result property="createTime" column="createTime" jdbcType="TIMESTAMP"/> |
||||
<result property="updateTime" column="updateTime" jdbcType="TIMESTAMP"/> |
||||
<result property="isDelete" column="isDelete" jdbcType="TINYINT"/> |
||||
</resultMap> |
||||
|
||||
<sql id="Base_Column_List"> |
||||
id,title,content,tags, |
||||
thumbNum,favourNum,userId, |
||||
createTime,updateTime,isDelete |
||||
</sql> |
||||
|
||||
<select id="listPostWithDelete" resultType="cc.bnblogs.springbootinit.model.entity.Post"> |
||||
select * |
||||
from post |
||||
where updateTime >= #{minUpdateTime} |
||||
</select> |
||||
</mapper> |
@ -0,0 +1,19 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
<!DOCTYPE mapper |
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="cc.bnblogs.springbootinit.mapper.PostThumbMapper"> |
||||
|
||||
<resultMap id="BaseResultMap" type="cc.bnblogs.springbootinit.model.entity.PostThumb"> |
||||
<id property="id" column="id" jdbcType="BIGINT"/> |
||||
<result property="postId" column="postId" jdbcType="BIGINT"/> |
||||
<result property="userId" column="userId" jdbcType="BIGINT"/> |
||||
<result property="createTime" column="createTime" jdbcType="TIMESTAMP"/> |
||||
<result property="updateTime" column="updateTime" jdbcType="TIMESTAMP"/> |
||||
</resultMap> |
||||
|
||||
<sql id="Base_Column_List"> |
||||
id,postId, |
||||
userId,createTime,updateTime |
||||
</sql> |
||||
</mapper> |
@ -0,0 +1,25 @@ |
||||
<?xml version="1.0" encoding="UTF-8"?> |
||||
|
||||
<!DOCTYPE mapper |
||||
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" |
||||
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"> |
||||
<mapper namespace="cc.bnblogs.springbootinit.mapper.UserMapper"> |
||||
<resultMap id="BaseResultMap" type="cc.bnblogs.springbootinit.model.entity.User"> |
||||
<id property="id" column="id" jdbcType="BIGINT"/> |
||||
<result property="unionId" column="unionId" jdbcType="VARCHAR"/> |
||||
<result property="mpOpenId" column="mpOpenId" jdbcType="VARCHAR"/> |
||||
<result property="userName" column="userName" jdbcType="VARCHAR"/> |
||||
<result property="userAvatar" column="userAvatar" jdbcType="VARCHAR"/> |
||||
<result property="userProfile" column="userProfile" jdbcType="VARCHAR"/> |
||||
<result property="userRole" column="userRole" jdbcType="VARCHAR"/> |
||||
<result property="createTime" column="createTime" jdbcType="TIMESTAMP"/> |
||||
<result property="updateTime" column="updateTime" jdbcType="TIMESTAMP"/> |
||||
<result property="isDelete" column="isDelete" jdbcType="TINYINT"/> |
||||
</resultMap> |
||||
|
||||
<sql id="Base_Column_List"> |
||||
id,unionId,mpOpenId, |
||||
userName,userAvatar,userProfile, |
||||
userRole,createTime,updateTime,isDelete |
||||
</sql> |
||||
</mapper> |
Binary file not shown.
Binary file not shown.
@ -0,0 +1,22 @@ |
||||
package cc.bnblogs.springbootinit; |
||||
|
||||
import cc.bnblogs.springbootinit.config.WxOpenConfig; |
||||
import javax.annotation.Resource; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
|
||||
/** |
||||
* 主类测试 |
||||
*/ |
||||
@SpringBootTest |
||||
class MainApplicationTests { |
||||
|
||||
@Resource |
||||
private WxOpenConfig wxOpenConfig; |
||||
|
||||
@Test |
||||
void contextLoads() { |
||||
System.out.println(wxOpenConfig); |
||||
} |
||||
|
||||
} |
@ -0,0 +1,80 @@ |
||||
package cc.bnblogs.springbootinit.esdao; |
||||
|
||||
import cc.bnblogs.springbootinit.model.dto.post.PostEsDTO; |
||||
import cc.bnblogs.springbootinit.model.dto.post.PostQueryRequest; |
||||
import cc.bnblogs.springbootinit.model.entity.Post; |
||||
import cc.bnblogs.springbootinit.service.PostService; |
||||
import java.util.Arrays; |
||||
import java.util.Date; |
||||
import java.util.List; |
||||
import java.util.Optional; |
||||
import javax.annotation.Resource; |
||||
import org.junit.jupiter.api.Test; |
||||
import org.springframework.boot.test.context.SpringBootTest; |
||||
import org.springframework.data.domain.Page; |
||||
import org.springframework.data.domain.PageRequest; |
||||
import org.springframework.data.domain.Sort; |
||||
|
||||
/** |
||||
* 帖子 ES 操作测试 |
||||
*/ |
||||
@SpringBootTest |
||||
public class PostEsDaoTest { |
||||
|
||||
@Resource |
||||
private PostEsDao postEsDao; |
||||
|
||||
@Resource |
||||
private PostService postService; |
||||
|
||||
@Test |
||||
void test() { |
||||
PostQueryRequest postQueryRequest = new PostQueryRequest(); |
||||
com.baomidou.mybatisplus.extension.plugins.pagination.Page<Post> page = |
||||
postService.searchFromEs(postQueryRequest); |
||||
System.out.println(page); |
||||
} |
||||
|
||||
@Test |
||||
void testSelect() { |
||||
System.out.println(postEsDao.count()); |
||||
Page<PostEsDTO> PostPage = postEsDao.findAll( |
||||
PageRequest.of(0, 5, Sort.by("createTime"))); |
||||
List<PostEsDTO> postList = PostPage.getContent(); |
||||
System.out.println(postList); |
||||
} |
||||
|
||||
@Test |
||||
void testAdd() { |
||||
PostEsDTO postEsDTO = new PostEsDTO(); |
||||
postEsDTO.setId(1L); |
||||
postEsDTO.setTitle("test"); |
||||
postEsDTO.setContent("test"); |
||||
postEsDTO.setTags(Arrays.asList("java", "python")); |
||||
postEsDTO.setThumbNum(1); |
||||
postEsDTO.setFavourNum(1); |
||||
postEsDTO.setUserId(1L); |
||||
postEsDTO.setCreateTime(new Date()); |
||||
postEsDTO.setUpdateTime(new Date()); |
||||
postEsDTO.setIsDelete(0); |
||||
postEsDao.save(postEsDTO); |
||||
System.out.println(postEsDTO.getId()); |
||||
} |
||||
|
||||
@Test |
||||
void testFindById() { |
||||
Optional<PostEsDTO> postEsDTO = postEsDao.findById(1L); |
||||
System.out.println(postEsDTO); |
||||
} |
||||
|
||||
@Test |
||||
void testCount() { |
||||
System.out.println(postEsDao.count()); |
||||
} |
||||
|
||||
@Test |
||||
void testFindByCategory() { |
||||
List<PostEsDTO> postEsDaoTestList = postEsDao.findByUserId(1L); |
||||
System.out.println(postEsDaoTestList); |
||||
} |
||||
} |
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in new issue