├── .gitignore ├── Back-end ├── .gitignore ├── HELP.md ├── mvnw ├── mvnw.cmd ├── package-lock.json ├── package.json ├── pom.xml ├── src │ ├── main │ │ ├── java │ │ │ └── com │ │ │ │ └── bookshop │ │ │ │ └── backend │ │ │ │ ├── BackEndApplication.java │ │ │ │ ├── CorsConfig.java │ │ │ │ ├── SqlResult.java │ │ │ │ ├── callback │ │ │ │ └── UserServiceCallback.java │ │ │ │ ├── controller │ │ │ │ ├── BookController.java │ │ │ │ ├── CartController.java │ │ │ │ ├── ChartController.java │ │ │ │ ├── NewsController.java │ │ │ │ ├── ReviewController.java │ │ │ │ ├── SearchController.java │ │ │ │ ├── TestController.java │ │ │ │ └── UserController.java │ │ │ │ ├── data │ │ │ │ ├── Book.java │ │ │ │ ├── Cart.java │ │ │ │ ├── CartItemPack.java │ │ │ │ ├── Chart.java │ │ │ │ ├── ChartType.java │ │ │ │ ├── News.java │ │ │ │ ├── Review.java │ │ │ │ ├── ReviewChart.java │ │ │ │ └── User.java │ │ │ │ ├── jsonconvert │ │ │ │ ├── JacksonConfig.java │ │ │ │ └── JsonResult.java │ │ │ │ ├── mapper │ │ │ │ ├── BookMapper.java │ │ │ │ ├── CartMapper.java │ │ │ │ ├── ChartMapper.java │ │ │ │ ├── ChartTypeMapper.java │ │ │ │ ├── NewsMapper.java │ │ │ │ ├── ReviewChartMapper.java │ │ │ │ ├── ReviewMapper.java │ │ │ │ └── UserMapper.java │ │ │ │ └── utility │ │ │ │ ├── BookUtil.java │ │ │ │ └── RequestUtil.java │ │ └── resources │ │ │ └── application.properties │ └── test │ │ └── java │ │ └── com │ │ └── bookshop │ │ └── backend │ │ └── BackEndApplicationTests.java ├── tsconfig.json ├── types.d.ts ├── webpack.config.js └── webpack.generated.js ├── Database └── bookshop_system.sql ├── Front-end ├── .gitignore ├── README.md ├── babel.config.js ├── jsconfig.json ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ └── index.html ├── src │ ├── App.vue │ ├── assets │ │ └── Logo.png │ ├── components │ │ ├── AccountCenterPage.vue │ │ ├── AccountInfoPage.vue │ │ ├── AccountPanel.vue │ │ ├── AddressPage.vue │ │ ├── BriefCard.vue │ │ ├── BriefChartBoard.vue │ │ ├── ChartBoard.vue │ │ ├── ChartPage.vue │ │ ├── CollectionPage.vue │ │ ├── DetailedChartBoard.vue │ │ ├── FrontPage.vue │ │ ├── HottestSellersPage.vue │ │ ├── InfoCard.vue │ │ ├── LatestBookPage.vue │ │ ├── LoginDialog.vue │ │ ├── MainFrame.vue │ │ ├── MemberPage.vue │ │ ├── MenuBar.vue │ │ ├── OrderPage.vue │ │ ├── PageHeader.vue │ │ ├── PasswordChangingPage.vue │ │ ├── ProductPage.vue │ │ ├── RankChangeIcon.vue │ │ ├── RegisterDialog.vue │ │ ├── ReviewChartPage.vue │ │ ├── ReviewPage.vue │ │ ├── RightSideBoard.vue │ │ ├── SearchPanel.vue │ │ ├── SearchResultPage.vue │ │ ├── SideBoard.vue │ │ ├── TabPage.vue │ │ ├── TopBar.vue │ │ └── WindowHeader.vue │ ├── main.js │ ├── router.js │ └── store.js └── vue.config.js └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | Back-end/.mvn/ 3 | 4 | Back-end/frontend/ 5 | -------------------------------------------------------------------------------- /Back-end/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules 2 | target/ 3 | !.mvn/wrapper/maven-wrapper.jar 4 | !**/src/main/**/target/ 5 | !**/src/test/**/target/ 6 | 7 | ### STS ### 8 | .apt_generated 9 | .classpath 10 | .factorypath 11 | .project 12 | .settings 13 | .springBeans 14 | .sts4-cache 15 | 16 | ### IntelliJ IDEA ### 17 | .idea 18 | *.iws 19 | *.iml 20 | *.ipr 21 | 22 | ### NetBeans ### 23 | /nbproject/private/ 24 | /nbbuild/ 25 | /dist/ 26 | /nbdist/ 27 | /.nb-gradle/ 28 | build/ 29 | !**/src/main/**/build/ 30 | !**/src/test/**/build/ 31 | 32 | ### VS Code ### 33 | .vscode/ 34 | -------------------------------------------------------------------------------- /Back-end/HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | 5 | For further reference, please consider the following sections: 6 | 7 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 8 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/html/) 9 | * [Create an OCI image](https://docs.spring.io/spring-boot/docs/2.6.4/maven-plugin/reference/html/#build-image) 10 | * [Jersey](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#boot-features-jersey) 11 | * [Spring Web](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#boot-features-developing-web-applications) 12 | * [Spring Configuration Processor](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#configuration-metadata-annotation-processor) 13 | * [JDBC API](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#boot-features-sql) 14 | * [Spring HATEOAS](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#boot-features-spring-hateoas) 15 | * [Vaadin](https://vaadin.com/spring) 16 | * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#using-boot-devtools) 17 | * [Rest Repositories](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#howto-use-exposing-spring-data-repositories-rest-endpoint) 18 | * [Spring Web Services](https://docs.spring.io/spring-boot/docs/2.6.4/reference/htmlsingle/#boot-features-webservices) 19 | 20 | ### Guides 21 | 22 | The following guides illustrate how to use some features concretely: 23 | 24 | * [Accessing data with MySQL](https://spring.io/guides/gs/accessing-data-mysql/) 25 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 26 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 27 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 28 | * [Accessing Relational Data using JDBC with Spring](https://spring.io/guides/gs/relational-data-access/) 29 | * [Managing Transactions](https://spring.io/guides/gs/managing-transactions/) 30 | * [Building a Hypermedia-Driven RESTful Web Service](https://spring.io/guides/gs/rest-hateoas/) 31 | * [Creating CRUD UI with Vaadin](https://spring.io/guides/gs/crud-with-vaadin/) 32 | * [Accessing JPA Data with REST](https://spring.io/guides/gs/accessing-data-rest/) 33 | * [Accessing Neo4j Data with REST](https://spring.io/guides/gs/accessing-neo4j-data-rest/) 34 | * [Accessing MongoDB Data with REST](https://spring.io/guides/gs/accessing-mongodb-data-rest/) 35 | * [Producing a SOAP web service](https://spring.io/guides/gs/producing-web-service/) 36 | 37 | -------------------------------------------------------------------------------- /Back-end/mvnw: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # ---------------------------------------------------------------------------- 3 | # Licensed to the Apache Software Foundation (ASF) under one 4 | # or more contributor license agreements. See the NOTICE file 5 | # distributed with this work for additional information 6 | # regarding copyright ownership. The ASF licenses this file 7 | # to you under the Apache License, Version 2.0 (the 8 | # "License"); you may not use this file except in compliance 9 | # with the License. You may obtain a copy of the License at 10 | # 11 | # https://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, 14 | # software distributed under the License is distributed on an 15 | # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 16 | # KIND, either express or implied. See the License for the 17 | # specific language governing permissions and limitations 18 | # under the License. 19 | # ---------------------------------------------------------------------------- 20 | 21 | # ---------------------------------------------------------------------------- 22 | # Maven Start Up Batch script 23 | # 24 | # Required ENV vars: 25 | # ------------------ 26 | # JAVA_HOME - location of a JDK home dir 27 | # 28 | # Optional ENV vars 29 | # ----------------- 30 | # M2_HOME - location of maven2's installed home dir 31 | # MAVEN_OPTS - parameters passed to the Java VM when running Maven 32 | # e.g. to debug Maven itself, use 33 | # set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 34 | # MAVEN_SKIP_RC - flag to disable loading of mavenrc files 35 | # ---------------------------------------------------------------------------- 36 | 37 | if [ -z "$MAVEN_SKIP_RC" ] ; then 38 | 39 | if [ -f /usr/local/etc/mavenrc ] ; then 40 | . /usr/local/etc/mavenrc 41 | fi 42 | 43 | if [ -f /etc/mavenrc ] ; then 44 | . /etc/mavenrc 45 | fi 46 | 47 | if [ -f "$HOME/.mavenrc" ] ; then 48 | . "$HOME/.mavenrc" 49 | fi 50 | 51 | fi 52 | 53 | # OS specific support. $var _must_ be set to either true or false. 54 | cygwin=false; 55 | darwin=false; 56 | mingw=false 57 | case "`uname`" in 58 | CYGWIN*) cygwin=true ;; 59 | MINGW*) mingw=true;; 60 | Darwin*) darwin=true 61 | # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home 62 | # See https://developer.apple.com/library/mac/qa/qa1170/_index.html 63 | if [ -z "$JAVA_HOME" ]; then 64 | if [ -x "/usr/libexec/java_home" ]; then 65 | export JAVA_HOME="`/usr/libexec/java_home`" 66 | else 67 | export JAVA_HOME="/Library/Java/Home" 68 | fi 69 | fi 70 | ;; 71 | esac 72 | 73 | if [ -z "$JAVA_HOME" ] ; then 74 | if [ -r /etc/gentoo-release ] ; then 75 | JAVA_HOME=`java-config --jre-home` 76 | fi 77 | fi 78 | 79 | if [ -z "$M2_HOME" ] ; then 80 | ## resolve links - $0 may be a link to maven's home 81 | PRG="$0" 82 | 83 | # need this for relative symlinks 84 | while [ -h "$PRG" ] ; do 85 | ls=`ls -ld "$PRG"` 86 | link=`expr "$ls" : '.*-> \(.*\)$'` 87 | if expr "$link" : '/.*' > /dev/null; then 88 | PRG="$link" 89 | else 90 | PRG="`dirname "$PRG"`/$link" 91 | fi 92 | done 93 | 94 | saveddir=`pwd` 95 | 96 | M2_HOME=`dirname "$PRG"`/.. 97 | 98 | # make it fully qualified 99 | M2_HOME=`cd "$M2_HOME" && pwd` 100 | 101 | cd "$saveddir" 102 | # echo Using m2 at $M2_HOME 103 | fi 104 | 105 | # For Cygwin, ensure paths are in UNIX format before anything is touched 106 | if $cygwin ; then 107 | [ -n "$M2_HOME" ] && 108 | M2_HOME=`cygpath --unix "$M2_HOME"` 109 | [ -n "$JAVA_HOME" ] && 110 | JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 111 | [ -n "$CLASSPATH" ] && 112 | CLASSPATH=`cygpath --path --unix "$CLASSPATH"` 113 | fi 114 | 115 | # For Mingw, ensure paths are in UNIX format before anything is touched 116 | if $mingw ; then 117 | [ -n "$M2_HOME" ] && 118 | M2_HOME="`(cd "$M2_HOME"; pwd)`" 119 | [ -n "$JAVA_HOME" ] && 120 | JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" 121 | fi 122 | 123 | if [ -z "$JAVA_HOME" ]; then 124 | javaExecutable="`which javac`" 125 | if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then 126 | # readlink(1) is not available as standard on Solaris 10. 127 | readLink=`which readlink` 128 | if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then 129 | if $darwin ; then 130 | javaHome="`dirname \"$javaExecutable\"`" 131 | javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" 132 | else 133 | javaExecutable="`readlink -f \"$javaExecutable\"`" 134 | fi 135 | javaHome="`dirname \"$javaExecutable\"`" 136 | javaHome=`expr "$javaHome" : '\(.*\)/bin'` 137 | JAVA_HOME="$javaHome" 138 | export JAVA_HOME 139 | fi 140 | fi 141 | fi 142 | 143 | if [ -z "$JAVACMD" ] ; then 144 | if [ -n "$JAVA_HOME" ] ; then 145 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 146 | # IBM's JDK on AIX uses strange locations for the executables 147 | JAVACMD="$JAVA_HOME/jre/sh/java" 148 | else 149 | JAVACMD="$JAVA_HOME/bin/java" 150 | fi 151 | else 152 | JAVACMD="`\\unset -f command; \\command -v java`" 153 | fi 154 | fi 155 | 156 | if [ ! -x "$JAVACMD" ] ; then 157 | echo "Error: JAVA_HOME is not defined correctly." >&2 158 | echo " We cannot execute $JAVACMD" >&2 159 | exit 1 160 | fi 161 | 162 | if [ -z "$JAVA_HOME" ] ; then 163 | echo "Warning: JAVA_HOME environment variable is not set." 164 | fi 165 | 166 | CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher 167 | 168 | # traverses directory structure from process work directory to filesystem root 169 | # first directory with .mvn subdirectory is considered project base directory 170 | find_maven_basedir() { 171 | 172 | if [ -z "$1" ] 173 | then 174 | echo "Path not specified to find_maven_basedir" 175 | return 1 176 | fi 177 | 178 | basedir="$1" 179 | wdir="$1" 180 | while [ "$wdir" != '/' ] ; do 181 | if [ -d "$wdir"/.mvn ] ; then 182 | basedir=$wdir 183 | break 184 | fi 185 | # workaround for JBEAP-8937 (on Solaris 10/Sparc) 186 | if [ -d "${wdir}" ]; then 187 | wdir=`cd "$wdir/.."; pwd` 188 | fi 189 | # end of workaround 190 | done 191 | echo "${basedir}" 192 | } 193 | 194 | # concatenates all lines of a file 195 | concat_lines() { 196 | if [ -f "$1" ]; then 197 | echo "$(tr -s '\n' ' ' < "$1")" 198 | fi 199 | } 200 | 201 | BASE_DIR=`find_maven_basedir "$(pwd)"` 202 | if [ -z "$BASE_DIR" ]; then 203 | exit 1; 204 | fi 205 | 206 | ########################################################################################## 207 | # Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 208 | # This allows using the maven wrapper in projects that prohibit checking in binary data. 209 | ########################################################################################## 210 | if [ -r "$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" ]; then 211 | if [ "$MVNW_VERBOSE" = true ]; then 212 | echo "Found .mvn/wrapper/maven-wrapper.jar" 213 | fi 214 | else 215 | if [ "$MVNW_VERBOSE" = true ]; then 216 | echo "Couldn't find .mvn/wrapper/maven-wrapper.jar, downloading it ..." 217 | fi 218 | if [ -n "$MVNW_REPOURL" ]; then 219 | jarUrl="$MVNW_REPOURL/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 220 | else 221 | jarUrl="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 222 | fi 223 | while IFS="=" read key value; do 224 | case "$key" in (wrapperUrl) jarUrl="$value"; break ;; 225 | esac 226 | done < "$BASE_DIR/.mvn/wrapper/maven-wrapper.properties" 227 | if [ "$MVNW_VERBOSE" = true ]; then 228 | echo "Downloading from: $jarUrl" 229 | fi 230 | wrapperJarPath="$BASE_DIR/.mvn/wrapper/maven-wrapper.jar" 231 | if $cygwin; then 232 | wrapperJarPath=`cygpath --path --windows "$wrapperJarPath"` 233 | fi 234 | 235 | if command -v wget > /dev/null; then 236 | if [ "$MVNW_VERBOSE" = true ]; then 237 | echo "Found wget ... using wget" 238 | fi 239 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 240 | wget "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 241 | else 242 | wget --http-user=$MVNW_USERNAME --http-password=$MVNW_PASSWORD "$jarUrl" -O "$wrapperJarPath" || rm -f "$wrapperJarPath" 243 | fi 244 | elif command -v curl > /dev/null; then 245 | if [ "$MVNW_VERBOSE" = true ]; then 246 | echo "Found curl ... using curl" 247 | fi 248 | if [ -z "$MVNW_USERNAME" ] || [ -z "$MVNW_PASSWORD" ]; then 249 | curl -o "$wrapperJarPath" "$jarUrl" -f 250 | else 251 | curl --user $MVNW_USERNAME:$MVNW_PASSWORD -o "$wrapperJarPath" "$jarUrl" -f 252 | fi 253 | 254 | else 255 | if [ "$MVNW_VERBOSE" = true ]; then 256 | echo "Falling back to using Java to download" 257 | fi 258 | javaClass="$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.java" 259 | # For Cygwin, switch paths to Windows format before running javac 260 | if $cygwin; then 261 | javaClass=`cygpath --path --windows "$javaClass"` 262 | fi 263 | if [ -e "$javaClass" ]; then 264 | if [ ! -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 265 | if [ "$MVNW_VERBOSE" = true ]; then 266 | echo " - Compiling MavenWrapperDownloader.java ..." 267 | fi 268 | # Compiling the Java class 269 | ("$JAVA_HOME/bin/javac" "$javaClass") 270 | fi 271 | if [ -e "$BASE_DIR/.mvn/wrapper/MavenWrapperDownloader.class" ]; then 272 | # Running the downloader 273 | if [ "$MVNW_VERBOSE" = true ]; then 274 | echo " - Running MavenWrapperDownloader.java ..." 275 | fi 276 | ("$JAVA_HOME/bin/java" -cp .mvn/wrapper MavenWrapperDownloader "$MAVEN_PROJECTBASEDIR") 277 | fi 278 | fi 279 | fi 280 | fi 281 | ########################################################################################## 282 | # End of extension 283 | ########################################################################################## 284 | 285 | export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} 286 | if [ "$MVNW_VERBOSE" = true ]; then 287 | echo $MAVEN_PROJECTBASEDIR 288 | fi 289 | MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" 290 | 291 | # For Cygwin, switch paths to Windows format before running java 292 | if $cygwin; then 293 | [ -n "$M2_HOME" ] && 294 | M2_HOME=`cygpath --path --windows "$M2_HOME"` 295 | [ -n "$JAVA_HOME" ] && 296 | JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` 297 | [ -n "$CLASSPATH" ] && 298 | CLASSPATH=`cygpath --path --windows "$CLASSPATH"` 299 | [ -n "$MAVEN_PROJECTBASEDIR" ] && 300 | MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` 301 | fi 302 | 303 | # Provide a "standardized" way to retrieve the CLI args that will 304 | # work with both Windows and non-Windows executions. 305 | MAVEN_CMD_LINE_ARGS="$MAVEN_CONFIG $@" 306 | export MAVEN_CMD_LINE_ARGS 307 | 308 | WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 309 | 310 | exec "$JAVACMD" \ 311 | $MAVEN_OPTS \ 312 | $MAVEN_DEBUG_OPTS \ 313 | -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ 314 | "-Dmaven.home=${M2_HOME}" \ 315 | "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ 316 | ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" 317 | -------------------------------------------------------------------------------- /Back-end/mvnw.cmd: -------------------------------------------------------------------------------- 1 | @REM ---------------------------------------------------------------------------- 2 | @REM Licensed to the Apache Software Foundation (ASF) under one 3 | @REM or more contributor license agreements. See the NOTICE file 4 | @REM distributed with this work for additional information 5 | @REM regarding copyright ownership. The ASF licenses this file 6 | @REM to you under the Apache License, Version 2.0 (the 7 | @REM "License"); you may not use this file except in compliance 8 | @REM with the License. You may obtain a copy of the License at 9 | @REM 10 | @REM https://www.apache.org/licenses/LICENSE-2.0 11 | @REM 12 | @REM Unless required by applicable law or agreed to in writing, 13 | @REM software distributed under the License is distributed on an 14 | @REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 15 | @REM KIND, either express or implied. See the License for the 16 | @REM specific language governing permissions and limitations 17 | @REM under the License. 18 | @REM ---------------------------------------------------------------------------- 19 | 20 | @REM ---------------------------------------------------------------------------- 21 | @REM Maven Start Up Batch script 22 | @REM 23 | @REM Required ENV vars: 24 | @REM JAVA_HOME - location of a JDK home dir 25 | @REM 26 | @REM Optional ENV vars 27 | @REM M2_HOME - location of maven2's installed home dir 28 | @REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands 29 | @REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a keystroke before ending 30 | @REM MAVEN_OPTS - parameters passed to the Java VM when running Maven 31 | @REM e.g. to debug Maven itself, use 32 | @REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 33 | @REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files 34 | @REM ---------------------------------------------------------------------------- 35 | 36 | @REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' 37 | @echo off 38 | @REM set title of command window 39 | title %0 40 | @REM enable echoing by setting MAVEN_BATCH_ECHO to 'on' 41 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 42 | 43 | @REM set %HOME% to equivalent of $HOME 44 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 45 | 46 | @REM Execute a user defined script before this one 47 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 48 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 49 | if exist "%USERPROFILE%\mavenrc_pre.bat" call "%USERPROFILE%\mavenrc_pre.bat" %* 50 | if exist "%USERPROFILE%\mavenrc_pre.cmd" call "%USERPROFILE%\mavenrc_pre.cmd" %* 51 | :skipRcPre 52 | 53 | @setlocal 54 | 55 | set ERROR_CODE=0 56 | 57 | @REM To isolate internal variables from possible post scripts, we use another setlocal 58 | @setlocal 59 | 60 | @REM ==== START VALIDATION ==== 61 | if not "%JAVA_HOME%" == "" goto OkJHome 62 | 63 | echo. 64 | echo Error: JAVA_HOME not found in your environment. >&2 65 | echo Please set the JAVA_HOME variable in your environment to match the >&2 66 | echo location of your Java installation. >&2 67 | echo. 68 | goto error 69 | 70 | :OkJHome 71 | if exist "%JAVA_HOME%\bin\java.exe" goto init 72 | 73 | echo. 74 | echo Error: JAVA_HOME is set to an invalid directory. >&2 75 | echo JAVA_HOME = "%JAVA_HOME%" >&2 76 | echo Please set the JAVA_HOME variable in your environment to match the >&2 77 | echo location of your Java installation. >&2 78 | echo. 79 | goto error 80 | 81 | @REM ==== END VALIDATION ==== 82 | 83 | :init 84 | 85 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 86 | @REM Fallback to current working directory if not found. 87 | 88 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 89 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 90 | 91 | set EXEC_DIR=%CD% 92 | set WDIR=%EXEC_DIR% 93 | :findBaseDir 94 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 95 | cd .. 96 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 97 | set WDIR=%CD% 98 | goto findBaseDir 99 | 100 | :baseDirFound 101 | set MAVEN_PROJECTBASEDIR=%WDIR% 102 | cd "%EXEC_DIR%" 103 | goto endDetectBaseDir 104 | 105 | :baseDirNotFound 106 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 107 | cd "%EXEC_DIR%" 108 | 109 | :endDetectBaseDir 110 | 111 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 112 | 113 | @setlocal EnableExtensions EnableDelayedExpansion 114 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 115 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 116 | 117 | :endReadAdditionalConfig 118 | 119 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 120 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 121 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 122 | 123 | set DOWNLOAD_URL="https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 124 | 125 | FOR /F "usebackq tokens=1,2 delims==" %%A IN ("%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.properties") DO ( 126 | IF "%%A"=="wrapperUrl" SET DOWNLOAD_URL=%%B 127 | ) 128 | 129 | @REM Extension to allow automatically downloading the maven-wrapper.jar from Maven-central 130 | @REM This allows using the maven wrapper in projects that prohibit checking in binary data. 131 | if exist %WRAPPER_JAR% ( 132 | if "%MVNW_VERBOSE%" == "true" ( 133 | echo Found %WRAPPER_JAR% 134 | ) 135 | ) else ( 136 | if not "%MVNW_REPOURL%" == "" ( 137 | SET DOWNLOAD_URL="%MVNW_REPOURL%/org/apache/maven/wrapper/maven-wrapper/3.1.0/maven-wrapper-3.1.0.jar" 138 | ) 139 | if "%MVNW_VERBOSE%" == "true" ( 140 | echo Couldn't find %WRAPPER_JAR%, downloading it ... 141 | echo Downloading from: %DOWNLOAD_URL% 142 | ) 143 | 144 | powershell -Command "&{"^ 145 | "$webclient = new-object System.Net.WebClient;"^ 146 | "if (-not ([string]::IsNullOrEmpty('%MVNW_USERNAME%') -and [string]::IsNullOrEmpty('%MVNW_PASSWORD%'))) {"^ 147 | "$webclient.Credentials = new-object System.Net.NetworkCredential('%MVNW_USERNAME%', '%MVNW_PASSWORD%');"^ 148 | "}"^ 149 | "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12; $webclient.DownloadFile('%DOWNLOAD_URL%', '%WRAPPER_JAR%')"^ 150 | "}" 151 | if "%MVNW_VERBOSE%" == "true" ( 152 | echo Finished downloading %WRAPPER_JAR% 153 | ) 154 | ) 155 | @REM End of extension 156 | 157 | @REM Provide a "standardized" way to retrieve the CLI args that will 158 | @REM work with both Windows and non-Windows executions. 159 | set MAVEN_CMD_LINE_ARGS=%* 160 | 161 | %MAVEN_JAVA_EXE% ^ 162 | %JVM_CONFIG_MAVEN_PROPS% ^ 163 | %MAVEN_OPTS% ^ 164 | %MAVEN_DEBUG_OPTS% ^ 165 | -classpath %WRAPPER_JAR% ^ 166 | "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" ^ 167 | %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 168 | if ERRORLEVEL 1 goto error 169 | goto end 170 | 171 | :error 172 | set ERROR_CODE=1 173 | 174 | :end 175 | @endlocal & set ERROR_CODE=%ERROR_CODE% 176 | 177 | if not "%MAVEN_SKIP_RC%"=="" goto skipRcPost 178 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 179 | if exist "%USERPROFILE%\mavenrc_post.bat" call "%USERPROFILE%\mavenrc_post.bat" 180 | if exist "%USERPROFILE%\mavenrc_post.cmd" call "%USERPROFILE%\mavenrc_post.cmd" 181 | :skipRcPost 182 | 183 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 184 | if "%MAVEN_BATCH_PAUSE%"=="on" pause 185 | 186 | if "%MAVEN_TERMINATE_CMD%"=="on" exit %ERROR_CODE% 187 | 188 | cmd /C exit /B %ERROR_CODE% 189 | -------------------------------------------------------------------------------- /Back-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "no-name", 3 | "license": "UNLICENSED", 4 | "dependencies": { 5 | "@polymer/iron-icon": "3.0.1", 6 | "@polymer/iron-iconset-svg": "3.0.1", 7 | "@polymer/iron-list": "3.1.0", 8 | "@polymer/iron-meta": "3.0.1", 9 | "@polymer/iron-resizable-behavior": "3.0.1", 10 | "@polymer/polymer": "3.4.1", 11 | "@vaadin/accordion": "23.0.1", 12 | "@vaadin/app-layout": "23.0.1", 13 | "@vaadin/avatar": "23.0.1", 14 | "@vaadin/avatar-group": "23.0.1", 15 | "@vaadin/board": "23.0.1", 16 | "@vaadin/bundles": "23.0.1", 17 | "@vaadin/button": "23.0.1", 18 | "@vaadin/charts": "23.0.1", 19 | "@vaadin/checkbox": "23.0.1", 20 | "@vaadin/checkbox-group": "23.0.1", 21 | "@vaadin/combo-box": "23.0.1", 22 | "@vaadin/common-frontend": "0.0.17", 23 | "@vaadin/component-base": "23.0.1", 24 | "@vaadin/confirm-dialog": "23.0.1", 25 | "@vaadin/context-menu": "23.0.1", 26 | "@vaadin/cookie-consent": "23.0.1", 27 | "@vaadin/crud": "23.0.1", 28 | "@vaadin/custom-field": "23.0.1", 29 | "@vaadin/date-picker": "23.0.1", 30 | "@vaadin/date-time-picker": "23.0.1", 31 | "@vaadin/details": "23.0.1", 32 | "@vaadin/dialog": "23.0.1", 33 | "@vaadin/email-field": "23.0.1", 34 | "@vaadin/field-base": "23.0.1", 35 | "@vaadin/field-highlighter": "23.0.1", 36 | "@vaadin/flow-frontend": "./target/flow-frontend", 37 | "@vaadin/form-layout": "23.0.1", 38 | "@vaadin/grid": "23.0.1", 39 | "@vaadin/grid-pro": "23.0.1", 40 | "@vaadin/horizontal-layout": "23.0.1", 41 | "@vaadin/icon": "23.0.1", 42 | "@vaadin/icons": "23.0.1", 43 | "@vaadin/input-container": "23.0.1", 44 | "@vaadin/integer-field": "23.0.1", 45 | "@vaadin/item": "23.0.1", 46 | "@vaadin/list-box": "23.0.1", 47 | "@vaadin/login": "23.0.1", 48 | "@vaadin/map": "23.0.1", 49 | "@vaadin/menu-bar": "23.0.1", 50 | "@vaadin/message-input": "23.0.1", 51 | "@vaadin/message-list": "23.0.1", 52 | "@vaadin/notification": "23.0.1", 53 | "@vaadin/number-field": "23.0.1", 54 | "@vaadin/password-field": "23.0.1", 55 | "@vaadin/polymer-legacy-adapter": "23.0.1", 56 | "@vaadin/progress-bar": "23.0.1", 57 | "@vaadin/radio-group": "23.0.1", 58 | "@vaadin/rich-text-editor": "23.0.1", 59 | "@vaadin/router": "1.7.4", 60 | "@vaadin/scroller": "23.0.1", 61 | "@vaadin/select": "23.0.1", 62 | "@vaadin/split-layout": "23.0.1", 63 | "@vaadin/tabs": "23.0.1", 64 | "@vaadin/text-area": "23.0.1", 65 | "@vaadin/text-field": "23.0.1", 66 | "@vaadin/time-picker": "23.0.1", 67 | "@vaadin/upload": "23.0.1", 68 | "@vaadin/vaadin-accordion": "23.0.1", 69 | "@vaadin/vaadin-app-layout": "23.0.1", 70 | "@vaadin/vaadin-avatar": "23.0.1", 71 | "@vaadin/vaadin-board": "23.0.1", 72 | "@vaadin/vaadin-button": "23.0.1", 73 | "@vaadin/vaadin-charts": "23.0.1", 74 | "@vaadin/vaadin-checkbox": "23.0.1", 75 | "@vaadin/vaadin-combo-box": "23.0.1", 76 | "@vaadin/vaadin-confirm-dialog": "23.0.1", 77 | "@vaadin/vaadin-context-menu": "23.0.1", 78 | "@vaadin/vaadin-cookie-consent": "23.0.1", 79 | "@vaadin/vaadin-crud": "23.0.1", 80 | "@vaadin/vaadin-custom-field": "23.0.1", 81 | "@vaadin/vaadin-date-picker": "23.0.1", 82 | "@vaadin/vaadin-date-time-picker": "23.0.1", 83 | "@vaadin/vaadin-details": "23.0.1", 84 | "@vaadin/vaadin-development-mode-detector": "2.0.5", 85 | "@vaadin/vaadin-dialog": "23.0.1", 86 | "@vaadin/vaadin-form-layout": "23.0.1", 87 | "@vaadin/vaadin-grid": "23.0.1", 88 | "@vaadin/vaadin-grid-pro": "23.0.1", 89 | "@vaadin/vaadin-icon": "23.0.1", 90 | "@vaadin/vaadin-icons": "23.0.1", 91 | "@vaadin/vaadin-item": "23.0.1", 92 | "@vaadin/vaadin-list-box": "23.0.1", 93 | "@vaadin/vaadin-list-mixin": "23.0.1", 94 | "@vaadin/vaadin-login": "23.0.1", 95 | "@vaadin/vaadin-lumo-styles": "23.0.1", 96 | "@vaadin/vaadin-material-styles": "23.0.1", 97 | "@vaadin/vaadin-menu-bar": "23.0.1", 98 | "@vaadin/vaadin-messages": "23.0.1", 99 | "@vaadin/vaadin-notification": "23.0.1", 100 | "@vaadin/vaadin-ordered-layout": "23.0.1", 101 | "@vaadin/vaadin-overlay": "23.0.1", 102 | "@vaadin/vaadin-progress-bar": "23.0.1", 103 | "@vaadin/vaadin-radio-button": "23.0.1", 104 | "@vaadin/vaadin-rich-text-editor": "23.0.1", 105 | "@vaadin/vaadin-select": "23.0.1", 106 | "@vaadin/vaadin-split-layout": "23.0.1", 107 | "@vaadin/vaadin-tabs": "23.0.1", 108 | "@vaadin/vaadin-template-renderer": "23.0.1", 109 | "@vaadin/vaadin-text-field": "23.0.1", 110 | "@vaadin/vaadin-themable-mixin": "23.0.1", 111 | "@vaadin/vaadin-time-picker": "23.0.1", 112 | "@vaadin/vaadin-upload": "23.0.1", 113 | "@vaadin/vaadin-usage-statistics": "2.1.2", 114 | "@vaadin/vaadin-virtual-list": "23.0.1", 115 | "@vaadin/vertical-layout": "23.0.1", 116 | "@vaadin/virtual-list": "23.0.1", 117 | "@webcomponents/shadycss": "1.9.6", 118 | "construct-style-sheets-polyfill": "3.0.4", 119 | "date-fns": "2.23.0", 120 | "lit": "2.1.4" 121 | }, 122 | "devDependencies": { 123 | "@vaadin/application-theme-plugin": "./target/plugins/application-theme-plugin", 124 | "@vaadin/build-status-plugin": "./target/plugins/build-status-plugin", 125 | "@vaadin/theme-live-reload-plugin": "./target/plugins/theme-live-reload-plugin", 126 | "@vaadin/theme-loader": "./target/plugins/theme-loader", 127 | "chokidar": "^3.5.0", 128 | "compression-webpack-plugin": "4.0.1", 129 | "css-loader": "5.2.7", 130 | "esbuild-loader": "2.15.1", 131 | "extra-watch-webpack-plugin": "1.0.3", 132 | "extract-loader": "5.1.0", 133 | "file-loader": "6.2.0", 134 | "fork-ts-checker-webpack-plugin": "6.2.1", 135 | "glob": "7.1.6", 136 | "html-webpack-plugin": "4.5.1", 137 | "lit-css-loader": "0.1.0", 138 | "loader-utils": "2.0.0", 139 | "typescript": "4.5.3", 140 | "webpack": "4.46.0", 141 | "webpack-cli": "4.9.2", 142 | "webpack-dev-server": "4.7.4", 143 | "webpack-merge": "4.2.2", 144 | "workbox-core": "6.5.0", 145 | "workbox-precaching": "6.5.0", 146 | "workbox-webpack-plugin": "6.5.0" 147 | }, 148 | "vaadin": { 149 | "dependencies": { 150 | "@polymer/iron-icon": "3.0.1", 151 | "@polymer/iron-iconset-svg": "3.0.1", 152 | "@polymer/iron-list": "3.1.0", 153 | "@polymer/iron-meta": "3.0.1", 154 | "@polymer/iron-resizable-behavior": "3.0.1", 155 | "@polymer/polymer": "3.4.1", 156 | "@vaadin/accordion": "23.0.1", 157 | "@vaadin/app-layout": "23.0.1", 158 | "@vaadin/avatar": "23.0.1", 159 | "@vaadin/avatar-group": "23.0.1", 160 | "@vaadin/board": "23.0.1", 161 | "@vaadin/bundles": "23.0.1", 162 | "@vaadin/button": "23.0.1", 163 | "@vaadin/charts": "23.0.1", 164 | "@vaadin/checkbox": "23.0.1", 165 | "@vaadin/checkbox-group": "23.0.1", 166 | "@vaadin/combo-box": "23.0.1", 167 | "@vaadin/common-frontend": "0.0.17", 168 | "@vaadin/component-base": "23.0.1", 169 | "@vaadin/confirm-dialog": "23.0.1", 170 | "@vaadin/context-menu": "23.0.1", 171 | "@vaadin/cookie-consent": "23.0.1", 172 | "@vaadin/crud": "23.0.1", 173 | "@vaadin/custom-field": "23.0.1", 174 | "@vaadin/date-picker": "23.0.1", 175 | "@vaadin/date-time-picker": "23.0.1", 176 | "@vaadin/details": "23.0.1", 177 | "@vaadin/dialog": "23.0.1", 178 | "@vaadin/email-field": "23.0.1", 179 | "@vaadin/field-base": "23.0.1", 180 | "@vaadin/field-highlighter": "23.0.1", 181 | "@vaadin/form-layout": "23.0.1", 182 | "@vaadin/grid": "23.0.1", 183 | "@vaadin/grid-pro": "23.0.1", 184 | "@vaadin/horizontal-layout": "23.0.1", 185 | "@vaadin/icon": "23.0.1", 186 | "@vaadin/icons": "23.0.1", 187 | "@vaadin/input-container": "23.0.1", 188 | "@vaadin/integer-field": "23.0.1", 189 | "@vaadin/item": "23.0.1", 190 | "@vaadin/list-box": "23.0.1", 191 | "@vaadin/login": "23.0.1", 192 | "@vaadin/map": "23.0.1", 193 | "@vaadin/menu-bar": "23.0.1", 194 | "@vaadin/message-input": "23.0.1", 195 | "@vaadin/message-list": "23.0.1", 196 | "@vaadin/notification": "23.0.1", 197 | "@vaadin/number-field": "23.0.1", 198 | "@vaadin/password-field": "23.0.1", 199 | "@vaadin/polymer-legacy-adapter": "23.0.1", 200 | "@vaadin/progress-bar": "23.0.1", 201 | "@vaadin/radio-group": "23.0.1", 202 | "@vaadin/rich-text-editor": "23.0.1", 203 | "@vaadin/router": "1.7.4", 204 | "@vaadin/scroller": "23.0.1", 205 | "@vaadin/select": "23.0.1", 206 | "@vaadin/split-layout": "23.0.1", 207 | "@vaadin/tabs": "23.0.1", 208 | "@vaadin/text-area": "23.0.1", 209 | "@vaadin/text-field": "23.0.1", 210 | "@vaadin/time-picker": "23.0.1", 211 | "@vaadin/upload": "23.0.1", 212 | "@vaadin/vaadin-accordion": "23.0.1", 213 | "@vaadin/vaadin-app-layout": "23.0.1", 214 | "@vaadin/vaadin-avatar": "23.0.1", 215 | "@vaadin/vaadin-board": "23.0.1", 216 | "@vaadin/vaadin-button": "23.0.1", 217 | "@vaadin/vaadin-charts": "23.0.1", 218 | "@vaadin/vaadin-checkbox": "23.0.1", 219 | "@vaadin/vaadin-combo-box": "23.0.1", 220 | "@vaadin/vaadin-confirm-dialog": "23.0.1", 221 | "@vaadin/vaadin-context-menu": "23.0.1", 222 | "@vaadin/vaadin-cookie-consent": "23.0.1", 223 | "@vaadin/vaadin-crud": "23.0.1", 224 | "@vaadin/vaadin-custom-field": "23.0.1", 225 | "@vaadin/vaadin-date-picker": "23.0.1", 226 | "@vaadin/vaadin-date-time-picker": "23.0.1", 227 | "@vaadin/vaadin-details": "23.0.1", 228 | "@vaadin/vaadin-development-mode-detector": "2.0.5", 229 | "@vaadin/vaadin-dialog": "23.0.1", 230 | "@vaadin/vaadin-form-layout": "23.0.1", 231 | "@vaadin/vaadin-grid": "23.0.1", 232 | "@vaadin/vaadin-grid-pro": "23.0.1", 233 | "@vaadin/vaadin-icon": "23.0.1", 234 | "@vaadin/vaadin-icons": "23.0.1", 235 | "@vaadin/vaadin-item": "23.0.1", 236 | "@vaadin/vaadin-list-box": "23.0.1", 237 | "@vaadin/vaadin-list-mixin": "23.0.1", 238 | "@vaadin/vaadin-login": "23.0.1", 239 | "@vaadin/vaadin-lumo-styles": "23.0.1", 240 | "@vaadin/vaadin-material-styles": "23.0.1", 241 | "@vaadin/vaadin-menu-bar": "23.0.1", 242 | "@vaadin/vaadin-messages": "23.0.1", 243 | "@vaadin/vaadin-notification": "23.0.1", 244 | "@vaadin/vaadin-ordered-layout": "23.0.1", 245 | "@vaadin/vaadin-overlay": "23.0.1", 246 | "@vaadin/vaadin-progress-bar": "23.0.1", 247 | "@vaadin/vaadin-radio-button": "23.0.1", 248 | "@vaadin/vaadin-rich-text-editor": "23.0.1", 249 | "@vaadin/vaadin-select": "23.0.1", 250 | "@vaadin/vaadin-split-layout": "23.0.1", 251 | "@vaadin/vaadin-tabs": "23.0.1", 252 | "@vaadin/vaadin-template-renderer": "23.0.1", 253 | "@vaadin/vaadin-text-field": "23.0.1", 254 | "@vaadin/vaadin-themable-mixin": "23.0.1", 255 | "@vaadin/vaadin-time-picker": "23.0.1", 256 | "@vaadin/vaadin-upload": "23.0.1", 257 | "@vaadin/vaadin-usage-statistics": "2.1.2", 258 | "@vaadin/vaadin-virtual-list": "23.0.1", 259 | "@vaadin/vertical-layout": "23.0.1", 260 | "@vaadin/virtual-list": "23.0.1", 261 | "@webcomponents/shadycss": "1.9.6", 262 | "construct-style-sheets-polyfill": "3.0.4", 263 | "date-fns": "2.23.0", 264 | "lit": "2.1.4" 265 | }, 266 | "devDependencies": { 267 | "chokidar": "^3.5.0", 268 | "compression-webpack-plugin": "4.0.1", 269 | "css-loader": "5.2.7", 270 | "esbuild-loader": "2.15.1", 271 | "extra-watch-webpack-plugin": "1.0.3", 272 | "extract-loader": "5.1.0", 273 | "file-loader": "6.2.0", 274 | "fork-ts-checker-webpack-plugin": "6.2.1", 275 | "glob": "7.1.6", 276 | "html-webpack-plugin": "4.5.1", 277 | "lit-css-loader": "0.1.0", 278 | "loader-utils": "2.0.0", 279 | "typescript": "4.5.3", 280 | "webpack": "4.46.0", 281 | "webpack-cli": "4.9.2", 282 | "webpack-dev-server": "4.7.4", 283 | "webpack-merge": "4.2.2", 284 | "workbox-core": "6.5.0", 285 | "workbox-precaching": "6.5.0", 286 | "workbox-webpack-plugin": "6.5.0" 287 | }, 288 | "hash": "eec4b9bdee9a894dd301a1ee174659ee416a5cc1a1d47bd24f9e46f6d19e571a" 289 | }, 290 | "overrides": { 291 | "@vaadin/bundles": "$@vaadin/bundles", 292 | "@vaadin/accordion": "$@vaadin/accordion", 293 | "@vaadin/app-layout": "$@vaadin/app-layout", 294 | "@vaadin/avatar": "$@vaadin/avatar", 295 | "@vaadin/avatar-group": "$@vaadin/avatar-group", 296 | "@vaadin/button": "$@vaadin/button", 297 | "@vaadin/checkbox": "$@vaadin/checkbox", 298 | "@vaadin/checkbox-group": "$@vaadin/checkbox-group", 299 | "@vaadin/combo-box": "$@vaadin/combo-box", 300 | "@vaadin/component-base": "$@vaadin/component-base", 301 | "@vaadin/context-menu": "$@vaadin/context-menu", 302 | "@vaadin/custom-field": "$@vaadin/custom-field", 303 | "@vaadin/date-picker": "$@vaadin/date-picker", 304 | "@vaadin/date-time-picker": "$@vaadin/date-time-picker", 305 | "@vaadin/details": "$@vaadin/details", 306 | "@vaadin/dialog": "$@vaadin/dialog", 307 | "@vaadin/email-field": "$@vaadin/email-field", 308 | "@vaadin/field-base": "$@vaadin/field-base", 309 | "@vaadin/field-highlighter": "$@vaadin/field-highlighter", 310 | "@vaadin/form-layout": "$@vaadin/form-layout", 311 | "@vaadin/grid": "$@vaadin/grid", 312 | "@vaadin/horizontal-layout": "$@vaadin/horizontal-layout", 313 | "@vaadin/icon": "$@vaadin/icon", 314 | "@vaadin/icons": "$@vaadin/icons", 315 | "@vaadin/input-container": "$@vaadin/input-container", 316 | "@vaadin/integer-field": "$@vaadin/integer-field", 317 | "@polymer/iron-icon": "$@polymer/iron-icon", 318 | "@polymer/iron-iconset-svg": "$@polymer/iron-iconset-svg", 319 | "@polymer/iron-list": "$@polymer/iron-list", 320 | "@polymer/iron-meta": "$@polymer/iron-meta", 321 | "@polymer/iron-resizable-behavior": "$@polymer/iron-resizable-behavior", 322 | "@vaadin/item": "$@vaadin/item", 323 | "@vaadin/list-box": "$@vaadin/list-box", 324 | "@vaadin/login": "$@vaadin/login", 325 | "@vaadin/menu-bar": "$@vaadin/menu-bar", 326 | "@vaadin/message-input": "$@vaadin/message-input", 327 | "@vaadin/message-list": "$@vaadin/message-list", 328 | "@vaadin/notification": "$@vaadin/notification", 329 | "@vaadin/number-field": "$@vaadin/number-field", 330 | "@vaadin/password-field": "$@vaadin/password-field", 331 | "@vaadin/polymer-legacy-adapter": "$@vaadin/polymer-legacy-adapter", 332 | "@vaadin/progress-bar": "$@vaadin/progress-bar", 333 | "@vaadin/radio-group": "$@vaadin/radio-group", 334 | "@vaadin/scroller": "$@vaadin/scroller", 335 | "@vaadin/select": "$@vaadin/select", 336 | "@webcomponents/shadycss": "$@webcomponents/shadycss", 337 | "@vaadin/split-layout": "$@vaadin/split-layout", 338 | "@vaadin/tabs": "$@vaadin/tabs", 339 | "@vaadin/text-area": "$@vaadin/text-area", 340 | "@vaadin/text-field": "$@vaadin/text-field", 341 | "@vaadin/time-picker": "$@vaadin/time-picker", 342 | "@vaadin/upload": "$@vaadin/upload", 343 | "@vaadin/vaadin-accordion": "$@vaadin/vaadin-accordion", 344 | "@vaadin/vaadin-app-layout": "$@vaadin/vaadin-app-layout", 345 | "@vaadin/vaadin-avatar": "$@vaadin/vaadin-avatar", 346 | "@vaadin/vaadin-button": "$@vaadin/vaadin-button", 347 | "@vaadin/vaadin-checkbox": "$@vaadin/vaadin-checkbox", 348 | "@vaadin/vaadin-combo-box": "$@vaadin/vaadin-combo-box", 349 | "@vaadin/vaadin-context-menu": "$@vaadin/vaadin-context-menu", 350 | "@vaadin/vaadin-custom-field": "$@vaadin/vaadin-custom-field", 351 | "@vaadin/vaadin-date-picker": "$@vaadin/vaadin-date-picker", 352 | "@vaadin/vaadin-date-time-picker": "$@vaadin/vaadin-date-time-picker", 353 | "@vaadin/vaadin-details": "$@vaadin/vaadin-details", 354 | "@vaadin/vaadin-development-mode-detector": "$@vaadin/vaadin-development-mode-detector", 355 | "@vaadin/vaadin-dialog": "$@vaadin/vaadin-dialog", 356 | "@vaadin/vaadin-form-layout": "$@vaadin/vaadin-form-layout", 357 | "@vaadin/vaadin-grid": "$@vaadin/vaadin-grid", 358 | "@vaadin/vaadin-icon": "$@vaadin/vaadin-icon", 359 | "@vaadin/vaadin-icons": "$@vaadin/vaadin-icons", 360 | "@vaadin/vaadin-item": "$@vaadin/vaadin-item", 361 | "@vaadin/vaadin-list-box": "$@vaadin/vaadin-list-box", 362 | "@vaadin/vaadin-list-mixin": "$@vaadin/vaadin-list-mixin", 363 | "@vaadin/vaadin-login": "$@vaadin/vaadin-login", 364 | "@vaadin/vaadin-lumo-styles": "$@vaadin/vaadin-lumo-styles", 365 | "@vaadin/vaadin-material-styles": "$@vaadin/vaadin-material-styles", 366 | "@vaadin/vaadin-menu-bar": "$@vaadin/vaadin-menu-bar", 367 | "@vaadin/vaadin-messages": "$@vaadin/vaadin-messages", 368 | "@vaadin/vaadin-notification": "$@vaadin/vaadin-notification", 369 | "@vaadin/vaadin-ordered-layout": "$@vaadin/vaadin-ordered-layout", 370 | "@vaadin/vaadin-overlay": "$@vaadin/vaadin-overlay", 371 | "@vaadin/vaadin-progress-bar": "$@vaadin/vaadin-progress-bar", 372 | "@vaadin/vaadin-radio-button": "$@vaadin/vaadin-radio-button", 373 | "@vaadin/router": "$@vaadin/router", 374 | "@vaadin/vaadin-select": "$@vaadin/vaadin-select", 375 | "@vaadin/vaadin-split-layout": "$@vaadin/vaadin-split-layout", 376 | "@vaadin/vaadin-tabs": "$@vaadin/vaadin-tabs", 377 | "@vaadin/vaadin-template-renderer": "$@vaadin/vaadin-template-renderer", 378 | "@vaadin/vaadin-text-field": "$@vaadin/vaadin-text-field", 379 | "@vaadin/vaadin-themable-mixin": "$@vaadin/vaadin-themable-mixin", 380 | "@vaadin/vaadin-time-picker": "$@vaadin/vaadin-time-picker", 381 | "@vaadin/vaadin-upload": "$@vaadin/vaadin-upload", 382 | "@vaadin/vaadin-usage-statistics": "$@vaadin/vaadin-usage-statistics", 383 | "@vaadin/vaadin-virtual-list": "$@vaadin/vaadin-virtual-list", 384 | "@vaadin/vertical-layout": "$@vaadin/vertical-layout", 385 | "@vaadin/virtual-list": "$@vaadin/virtual-list", 386 | "@vaadin/board": "$@vaadin/board", 387 | "@vaadin/charts": "$@vaadin/charts", 388 | "@vaadin/confirm-dialog": "$@vaadin/confirm-dialog", 389 | "@vaadin/cookie-consent": "$@vaadin/cookie-consent", 390 | "@vaadin/crud": "$@vaadin/crud", 391 | "@vaadin/grid-pro": "$@vaadin/grid-pro", 392 | "@vaadin/map": "$@vaadin/map", 393 | "@vaadin/rich-text-editor": "$@vaadin/rich-text-editor", 394 | "@vaadin/vaadin-board": "$@vaadin/vaadin-board", 395 | "@vaadin/vaadin-charts": "$@vaadin/vaadin-charts", 396 | "@vaadin/vaadin-confirm-dialog": "$@vaadin/vaadin-confirm-dialog", 397 | "@vaadin/vaadin-cookie-consent": "$@vaadin/vaadin-cookie-consent", 398 | "@vaadin/vaadin-crud": "$@vaadin/vaadin-crud", 399 | "@vaadin/vaadin-grid-pro": "$@vaadin/vaadin-grid-pro", 400 | "@vaadin/vaadin-rich-text-editor": "$@vaadin/vaadin-rich-text-editor", 401 | "@vaadin/common-frontend": "$@vaadin/common-frontend", 402 | "construct-style-sheets-polyfill": "$construct-style-sheets-polyfill", 403 | "lit": "$lit", 404 | "@polymer/polymer": "$@polymer/polymer", 405 | "chokidar": "$chokidar", 406 | "date-fns": "$date-fns" 407 | } 408 | } 409 | -------------------------------------------------------------------------------- /Back-end/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | org.springframework.boot 7 | spring-boot-starter-parent 8 | 2.6.4 9 | 10 | 11 | com.bookshop 12 | Back-end 13 | 0.0.1-SNAPSHOT 14 | Back-end 15 | Back-end 16 | 17 | 17 18 | 23.0.1 19 | 20 | 21 | 22 | org.springframework.boot 23 | spring-boot-starter-data-rest 24 | 25 | 26 | org.springframework.boot 27 | spring-boot-starter-hateoas 28 | 29 | 30 | org.springframework.boot 31 | spring-boot-starter-jersey 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-web 36 | 37 | 38 | org.springframework.boot 39 | spring-boot-starter-web-services 40 | 41 | 42 | org.springframework.boot 43 | spring-boot-starter-webflux 44 | 45 | 46 | com.vaadin 47 | vaadin-spring-boot-starter 48 | 49 | 50 | org.springframework.data 51 | spring-data-rest-hal-explorer 52 | 53 | 54 | org.springframework.session 55 | spring-session-jdbc 56 | 57 | 58 | org.springframework.boot 59 | spring-boot-devtools 60 | runtime 61 | true 62 | 63 | 64 | org.springframework.boot 65 | spring-boot-configuration-processor 66 | true 67 | 68 | 69 | org.projectlombok 70 | lombok 71 | true 72 | 73 | 74 | org.springframework.boot 75 | spring-boot-starter-test 76 | test 77 | 78 | 79 | io.projectreactor 80 | reactor-test 81 | test 82 | 83 | 84 | com.fasterxml.jackson.core 85 | jackson-databind 86 | 2.13.1 87 | 88 | 89 | org.apache.httpcomponents 90 | httpclient 91 | 4.5.13 92 | 93 | 94 | org.jsoup 95 | jsoup 96 | 1.14.3 97 | 98 | 99 | mysql 100 | mysql-connector-java 101 | runtime 102 | 103 | 104 | com.baomidou 105 | mybatis-plus-boot-starter 106 | 3.5.1 107 | 108 | 109 | 110 | 111 | 112 | com.vaadin 113 | vaadin-bom 114 | ${vaadin.version} 115 | pom 116 | import 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | org.springframework.boot 125 | spring-boot-maven-plugin 126 | 127 | 128 | 129 | org.projectlombok 130 | lombok 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/BackEndApplication.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend; 2 | 3 | import org.mybatis.spring.annotation.MapperScan; 4 | import org.springframework.boot.SpringApplication; 5 | import org.springframework.boot.autoconfigure.SpringBootApplication; 6 | 7 | @SpringBootApplication 8 | @MapperScan("com.bookshop.backend.mapper") 9 | public class BackEndApplication { 10 | 11 | public static void main(String[] args) { 12 | SpringApplication.run(BackEndApplication.class, args); 13 | } 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/CorsConfig.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend; 2 | 3 | import org.springframework.context.annotation.Bean; 4 | import org.springframework.context.annotation.Configuration; 5 | import org.springframework.http.HttpHeaders; 6 | import org.springframework.web.servlet.config.annotation.CorsRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 8 | 9 | /** 10 | * 跨域配置 11 | */ 12 | @Configuration 13 | public class CorsConfig implements WebMvcConfigurer { 14 | 15 | @Bean 16 | public WebMvcConfigurer corsConfigurer() { 17 | 18 | return new WebMvcConfigurer() { 19 | 20 | @Override 21 | public void addCorsMappings(CorsRegistry registry) { 22 | registry.addMapping("/**"). 23 | allowedOrigins("*"). //允许跨域的域名,可以用*表示允许任何域名使用 24 | allowedMethods("*"). //允许任何方法(post、get等) 25 | allowedHeaders("*"); //允许任何请求头 26 | } 27 | }; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/SqlResult.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend; 2 | 3 | public class SqlResult { 4 | } 5 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/callback/UserServiceCallback.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.callback; 2 | 3 | public final class UserServiceCallback { 4 | public static String ok = "0"; 5 | public static String err = "1"; 6 | 7 | public static String passwordFailed = "100"; 8 | public static String userNotExist = "101"; 9 | public static String userExisted = "102"; 10 | } 11 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/BookController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.Book; 5 | import com.bookshop.backend.jsonconvert.JsonResult; 6 | import com.bookshop.backend.mapper.BookMapper; 7 | import org.springframework.http.HttpHeaders; 8 | import org.springframework.http.HttpStatus; 9 | import org.springframework.http.MediaType; 10 | import org.springframework.http.ResponseEntity; 11 | import org.springframework.web.bind.annotation.*; 12 | 13 | import javax.annotation.Resource; 14 | import java.io.IOException; 15 | 16 | @RestController 17 | @RequestMapping("/api") 18 | public class BookController { 19 | 20 | @Resource 21 | private BookMapper bookMapper; 22 | 23 | @GetMapping("/book/{id}") 24 | public JsonResult sendBookInfo(@PathVariable Integer id) { 25 | QueryWrapper wrapper = new QueryWrapper<>(); 26 | wrapper.eq("id", id); 27 | Book book = bookMapper.selectOne(wrapper); 28 | if (book != null) { 29 | return new JsonResult<>(book); 30 | } 31 | return new JsonResult<>("1", "请求失败"); 32 | } 33 | 34 | @GetMapping("/book/pic/{id}") 35 | public ResponseEntity sendBookPic(@PathVariable Integer id) throws IOException { 36 | QueryWrapper wrapper = new QueryWrapper<>(); 37 | wrapper.select("id", "pic").eq("id", id); 38 | byte[] raw = bookMapper.selectOne(wrapper).getPic(); 39 | HttpHeaders headers = new HttpHeaders(); 40 | headers.setContentType(MediaType.IMAGE_JPEG); 41 | return new ResponseEntity<>(raw, headers, HttpStatus.OK); 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/CartController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.Book; 5 | import com.bookshop.backend.data.Cart; 6 | import com.bookshop.backend.data.CartItemPack; 7 | import com.bookshop.backend.jsonconvert.JsonResult; 8 | import com.bookshop.backend.mapper.BookMapper; 9 | import com.bookshop.backend.mapper.CartMapper; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | 16 | @RestController 17 | @RequestMapping("/user") 18 | public class CartController { 19 | 20 | @Resource 21 | private CartMapper cartMapper; 22 | @Resource 23 | private BookMapper bookMapper; 24 | 25 | @PostMapping("/cart") 26 | public JsonResult addItemToCart(@RequestBody Cart cart) { 27 | QueryWrapper cartWrapper = new QueryWrapper<>(); 28 | cartWrapper.eq("user_id", cart.getUserId()).eq("book_id", cart.getBookId()); 29 | Long cnt = cartMapper.selectCount(cartWrapper); 30 | 31 | // 如果用户之前的购物车有这件商品,则增加购买数量 32 | if (cnt != 0) { 33 | Cart oldCart = cartMapper.selectOne(cartWrapper); 34 | cart.setQuantity(cart.getQuantity() + oldCart.getQuantity()); 35 | } 36 | cartMapper.insert(cart); 37 | 38 | return new JsonResult<>(); 39 | } 40 | 41 | @GetMapping("/cart/{id}") 42 | public JsonResult> sendCartInfo(@PathVariable Integer id) { 43 | 44 | // 获取购物车信息 45 | QueryWrapper cartWrapper = new QueryWrapper<>(); 46 | cartWrapper.eq("user_id", id); 47 | List carts = cartMapper.selectList(cartWrapper); 48 | 49 | // 获取商品信息 50 | ArrayList res = new ArrayList<>(); 51 | for (Cart i : carts) { 52 | CartItemPack item = new CartItemPack(); 53 | item.setId(i.getBookId()); 54 | item.setQuantity(i.getQuantity()); 55 | QueryWrapper bookWrapper = new QueryWrapper<>(); 56 | bookWrapper.select("id", "name", "author", "publisher", "price", "date").eq("id", i.getBookId()); 57 | Book book = bookMapper.selectOne(bookWrapper); 58 | item.setAuthor(book.getAuthor()); 59 | item.setDate(book.getDate()); 60 | item.setPrice(book.getPrice()); 61 | item.setPublisher(book.getPublisher()); 62 | item.setName(book.getName()); 63 | 64 | res.add(item); 65 | } 66 | 67 | return new JsonResult<>(res); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/ChartController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.Chart; 5 | import com.bookshop.backend.data.ChartType; 6 | import com.bookshop.backend.mapper.BookMapper; 7 | import com.bookshop.backend.mapper.ChartMapper; 8 | import com.bookshop.backend.mapper.ChartTypeMapper; 9 | import com.bookshop.backend.data.Book; 10 | import com.bookshop.backend.jsonconvert.JsonResult; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.ArrayList; 15 | import java.util.List; 16 | 17 | @RestController 18 | @RequestMapping("/api") 19 | public class ChartController { 20 | 21 | @Autowired 22 | private BookMapper bookMapper; 23 | @Autowired 24 | private ChartMapper chartMapper; 25 | @Autowired 26 | private ChartTypeMapper chartTypeMapper; 27 | 28 | /** 29 | * 从数据库中提取榜单信息 30 | * @param type 榜单类型 31 | * @param detail 返回信息的详细程度 32 | * 1:除图书简介、作者简介、目录、ISBN号的全部内容 33 | * 2:只含书号、书名、作者名 34 | * 3:只含书号、书名 35 | * 4:只含书号 36 | * 5:只含书号、书名、图书简介 37 | * @param num 返回条数 38 | * 默认值为-1,返回能找到的全部 39 | */ 40 | @GetMapping("/chart/{type}") 41 | public JsonResult> sendChartInfo(@PathVariable String type, @RequestParam(required = false, defaultValue = "1") Integer detail, @RequestParam(required = false, defaultValue = "-1") Integer num) { 42 | 43 | // 获取榜单类型 44 | QueryWrapper chartTypeWrapper = new QueryWrapper<>(); 45 | chartTypeWrapper.eq("name", type); 46 | Integer typeId = chartTypeMapper.selectOne(chartTypeWrapper).getId(); 47 | 48 | // 获取榜单信息 49 | QueryWrapper chartWrapper = new QueryWrapper<>(); 50 | chartWrapper.eq("type_id", typeId).orderByAsc("rank_num"); 51 | List charts = chartMapper.selectList(chartWrapper); 52 | 53 | // 根据指定的详细程度获取对应书本的信息 54 | if (num == -1 || num > charts.size()) num = charts.size(); 55 | ArrayList res = new ArrayList<>(); 56 | QueryWrapper bookWrapper = new QueryWrapper<>(); 57 | if (detail == 1) { 58 | for (int i = 0; i < num; ++i) { 59 | bookWrapper.select("id", "name", "author", "date", "publisher", "price", "has_e_book", "has_secondhand_book").eq("id", charts.get(i).getBookId()); 60 | Book book = bookMapper.selectOne(bookWrapper); 61 | res.add(book); 62 | bookWrapper.clear(); 63 | } 64 | } 65 | else if (detail == 2) { 66 | for (int i = 0; i < num; ++i) { 67 | bookWrapper.select("id", "name", "author").eq("id", charts.get(i).getBookId()); 68 | Book book = bookMapper.selectOne(bookWrapper); 69 | res.add(book); 70 | bookWrapper.clear(); 71 | } 72 | } 73 | else if (detail == 3) { 74 | for (int i = 0; i < num; i++) { 75 | bookWrapper.select("id", "name").eq("id", charts.get(i).getBookId()); 76 | Book book = bookMapper.selectOne(bookWrapper); 77 | res.add(book); 78 | bookWrapper.clear(); 79 | } 80 | } 81 | else if (detail == 4) { 82 | for (int i = 0; i < num; i++) { 83 | bookWrapper.select("id").eq("id", charts.get(i).getBookId()); 84 | Book book = bookMapper.selectOne(bookWrapper); 85 | res.add(book); 86 | bookWrapper.clear(); 87 | } 88 | } 89 | else if (detail == 5) { 90 | for (int i = 0; i < num; i++) { 91 | bookWrapper.select("id", "name", "book_introduction").eq("id", charts.get(i).getBookId()); 92 | Book book = bookMapper.selectOne(bookWrapper); 93 | res.add(book); 94 | bookWrapper.clear(); 95 | } 96 | } 97 | else { 98 | return new JsonResult<>("1", "参数错误"); 99 | } 100 | 101 | return new JsonResult<>(res); 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/NewsController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.News; 5 | import com.bookshop.backend.jsonconvert.JsonResult; 6 | import com.bookshop.backend.mapper.NewsMapper; 7 | import org.springframework.web.bind.annotation.*; 8 | 9 | import javax.annotation.Resource; 10 | import java.util.ArrayList; 11 | import java.util.List; 12 | 13 | @RestController 14 | @RequestMapping("/api") 15 | public class NewsController { 16 | 17 | @Resource 18 | private NewsMapper newsMapper; 19 | 20 | @GetMapping("/news") 21 | public JsonResult> sendNewsChart(@RequestParam(required = false, defaultValue = "-1") Integer num) { 22 | List news = newsMapper.selectList(null); 23 | num = num == -1 || num > news.size() ? news.size() : num; 24 | ArrayList res = new ArrayList<>(); 25 | for (int i = 0; i < num; i++) { 26 | res.add(news.get(i)); 27 | } 28 | return new JsonResult<>(res); 29 | } 30 | 31 | @GetMapping("/news/{id}") 32 | public JsonResult sendNews(@PathVariable Integer id) { 33 | QueryWrapper newsWrapper = new QueryWrapper<>(); 34 | newsWrapper.eq("id", id); 35 | News news = newsMapper.selectOne(newsWrapper); 36 | return new JsonResult<>(news); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/ReviewController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.Review; 5 | import com.bookshop.backend.mapper.ReviewChartMapper; 6 | import com.bookshop.backend.mapper.ReviewMapper; 7 | import com.bookshop.backend.data.ReviewChart; 8 | import com.bookshop.backend.jsonconvert.JsonResult; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.web.bind.annotation.*; 11 | 12 | import java.util.ArrayList; 13 | import java.util.List; 14 | 15 | @RestController 16 | @RequestMapping("/api") 17 | public class ReviewController { 18 | 19 | @Autowired 20 | private ReviewMapper reviewMapper; 21 | @Autowired 22 | private ReviewChartMapper reviewChartMapper; 23 | 24 | @GetMapping("/review/chart") 25 | public JsonResult> getReviewChart(@RequestParam(required = false, defaultValue = "-1") Integer num) { 26 | Long cnt = reviewChartMapper.selectCount(null); 27 | if (num == -1 || num > cnt) { 28 | num = cnt.intValue(); 29 | } 30 | QueryWrapper reviewChartWrapper = new QueryWrapper<>(); 31 | reviewChartWrapper.le("rank_num", num); 32 | List charts = reviewChartMapper.selectList(reviewChartWrapper); 33 | 34 | ArrayList res = new ArrayList<>(); 35 | QueryWrapper reviewWrapper = new QueryWrapper<>(); 36 | for (int i = 0; i < num; i++) { 37 | reviewWrapper.select("id", "title", "author", "date", "intro", "book_id").eq("id", charts.get(i).getReviewId()); 38 | Review review = reviewMapper.selectOne(reviewWrapper); 39 | res.add(review); 40 | reviewWrapper.clear(); 41 | } 42 | 43 | return new JsonResult<>(res); 44 | } 45 | 46 | @GetMapping("/review/{id}") 47 | public JsonResult getReview(@PathVariable Integer id) { 48 | QueryWrapper reviewWrapper = new QueryWrapper<>(); 49 | reviewWrapper.select("id", "book_id", "title", "content", "author", "date").eq("id", id); 50 | Review review = reviewMapper.selectOne(reviewWrapper); 51 | return new JsonResult<>(review); 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/SearchController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.Book; 5 | import com.bookshop.backend.jsonconvert.JsonResult; 6 | import com.bookshop.backend.mapper.BookMapper; 7 | import org.springframework.web.bind.annotation.GetMapping; 8 | import org.springframework.web.bind.annotation.RequestMapping; 9 | import org.springframework.web.bind.annotation.RequestParam; 10 | import org.springframework.web.bind.annotation.RestController; 11 | 12 | import javax.annotation.Resource; 13 | import java.util.List; 14 | 15 | @RestController 16 | @RequestMapping("/api") 17 | public class SearchController { 18 | 19 | @Resource 20 | private BookMapper bookMapper; 21 | 22 | @GetMapping("/search") 23 | public JsonResult> sendSearchResult(@RequestParam String text) { 24 | QueryWrapper wrapper = new QueryWrapper<>(); 25 | wrapper.select("id", "name", "author", "date", "publisher", "price").like("name", text); 26 | List books = bookMapper.selectList(wrapper); 27 | return new JsonResult<>(books); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/TestController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.bookshop.backend.jsonconvert.JsonResult; 4 | import org.springframework.http.ResponseEntity; 5 | import org.springframework.web.bind.annotation.GetMapping; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RestController; 8 | 9 | import java.io.InputStream; 10 | 11 | @RestController 12 | @RequestMapping("/test") 13 | public class TestController { 14 | 15 | @GetMapping("/one") 16 | public JsonResult getData1() { 17 | return new JsonResult<>("Hello from page 1"); 18 | } 19 | 20 | @GetMapping("/two") 21 | public JsonResult getData2() { 22 | return new JsonResult<>("Hello from page 2"); 23 | } 24 | 25 | /* 26 | @GetMapping("pic") 27 | public ResponseEntity getPic() { 28 | InputStream input = new 29 | } 30 | */ 31 | } 32 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/controller/UserController.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.controller; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.callback.UserServiceCallback; 5 | import com.bookshop.backend.data.User; 6 | import com.bookshop.backend.jsonconvert.JsonResult; 7 | import com.bookshop.backend.mapper.UserMapper; 8 | import org.springframework.web.bind.annotation.GetMapping; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.RequestParam; 11 | import org.springframework.web.bind.annotation.RestController; 12 | 13 | import javax.annotation.Resource; 14 | 15 | @RestController 16 | @RequestMapping("/user") 17 | public class UserController { 18 | 19 | @Resource 20 | private UserMapper userMapper; 21 | 22 | @GetMapping("/reg") 23 | public JsonResult userSignup(@RequestParam String username, @RequestParam String password) { 24 | QueryWrapper wrapper = new QueryWrapper<>(); 25 | wrapper.select("name").eq("name", username); 26 | Long cnt = userMapper.selectCount(wrapper); 27 | if (cnt == 0) { 28 | User user = new User(); 29 | user.setName(username); 30 | user.setPassword(password); 31 | userMapper.insert(user); 32 | return new JsonResult<>(user); 33 | } 34 | else { 35 | System.out.println(UserServiceCallback.userExisted); 36 | return new JsonResult<>(UserServiceCallback.userExisted, "用户已存在"); 37 | } 38 | } 39 | 40 | @GetMapping("/login") 41 | public JsonResult userLogin(@RequestParam String username, @RequestParam String password) { 42 | QueryWrapper wrapper = new QueryWrapper<>(); 43 | wrapper.eq("name", username); 44 | if (userMapper.selectCount(wrapper) != 0) { 45 | wrapper.clear(); 46 | wrapper.eq("name", username).eq("password", password); 47 | User user = userMapper.selectOne(wrapper); 48 | if (user != null) { 49 | System.out.println(UserServiceCallback.ok); 50 | return new JsonResult<>(user); 51 | } 52 | else { 53 | System.out.println(UserServiceCallback.passwordFailed); 54 | return new JsonResult<>(UserServiceCallback.passwordFailed, "密码错误"); 55 | } 56 | } 57 | else { 58 | System.out.println(UserServiceCallback.userNotExist); 59 | return new JsonResult<>(UserServiceCallback.userNotExist, "用户不存在"); 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/Book.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.*; 4 | 5 | @Getter 6 | @Setter 7 | @NoArgsConstructor 8 | @RequiredArgsConstructor 9 | public class Book { 10 | 11 | @NonNull 12 | private Integer id; 13 | 14 | private String name = null; 15 | private String author = null; 16 | private String date = null; 17 | private String publisher = null; 18 | private Double price = null; 19 | private Boolean hasEBook = false; 20 | private Boolean hasSecondhandBook = false; 21 | private String bookIntroduction = null; 22 | private String authorIntroduction = null; 23 | private String directory = null; 24 | private byte[] pic = null; 25 | private String isbn = null; 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/Cart.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.*; 4 | 5 | @Data 6 | @NoArgsConstructor 7 | public class Cart { 8 | private Integer userId; 9 | private Integer bookId; 10 | private Integer quantity; 11 | } 12 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/CartItemPack.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class CartItemPack { 9 | private Integer id; 10 | private String name; 11 | private String author; 12 | private String date; 13 | private String publisher; 14 | private Double price; 15 | private Integer quantity; 16 | } 17 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/Chart.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Getter; 5 | import lombok.NoArgsConstructor; 6 | import lombok.Setter; 7 | 8 | @Getter 9 | @Setter 10 | @NoArgsConstructor 11 | @AllArgsConstructor 12 | public class Chart { 13 | private Integer typeId; 14 | private Integer bookId; 15 | private Integer rankNum; 16 | } 17 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/ChartType.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class ChartType { 9 | private Integer id; 10 | private String name; 11 | } 12 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/News.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class News { 9 | private Integer id = null; 10 | private String title = null; 11 | private String content = null; 12 | } 13 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/Review.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @Data 7 | @NoArgsConstructor 8 | public class Review { 9 | private Integer bookId; 10 | private Integer id; 11 | private String title = null; 12 | private String content = null; 13 | private String date = null; 14 | private String author = null; 15 | private String intro = null; 16 | } 17 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/ReviewChart.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.AllArgsConstructor; 4 | import lombok.Data; 5 | import lombok.NoArgsConstructor; 6 | 7 | @Data 8 | @NoArgsConstructor 9 | @AllArgsConstructor 10 | public class ReviewChart { 11 | private Integer reviewId; 12 | private Integer rankNum; 13 | } 14 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/data/User.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.data; 2 | 3 | import lombok.Data; 4 | import lombok.NoArgsConstructor; 5 | 6 | @NoArgsConstructor 7 | @Data 8 | public class User { 9 | private Integer id = null; 10 | private String name = null; 11 | private String phone = null; 12 | private String mail = null; 13 | private String gender = null; 14 | private String password = null; 15 | private byte[] pic = null; 16 | } 17 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/jsonconvert/JacksonConfig.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.jsonconvert; 2 | 3 | import com.fasterxml.jackson.core.JsonGenerator; 4 | import com.fasterxml.jackson.databind.JsonSerializer; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import com.fasterxml.jackson.databind.SerializerProvider; 7 | import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; 8 | import org.springframework.context.annotation.Bean; 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.context.annotation.Primary; 11 | import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; 12 | 13 | import java.io.IOException; 14 | 15 | @Configuration 16 | public class JacksonConfig { 17 | @Bean 18 | @Primary 19 | @ConditionalOnMissingBean(ObjectMapper.class) 20 | public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) { 21 | ObjectMapper objectMapper = builder.createXmlMapper(false).build(); 22 | objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer() { 23 | @Override 24 | public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { 25 | jsonGenerator.writeString(""); 26 | } 27 | }); 28 | return objectMapper; 29 | } 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/jsonconvert/JsonResult.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.jsonconvert; 2 | 3 | public class JsonResult { 4 | private T data; 5 | private String code; 6 | private String msg; 7 | 8 | /** 9 | * 若没有数据返回,默认状态码为0,提示信息为:操作成功! 10 | */ 11 | public JsonResult() { 12 | this.code = "0"; 13 | this.msg = "操作成功!"; 14 | } 15 | 16 | /** 17 | * 若没有数据返回,可以人为指定状态码和提示信息 18 | * @param code 19 | * @param msg 20 | */ 21 | public JsonResult(String code, String msg) { 22 | this.code = code; 23 | this.msg = msg; 24 | } 25 | 26 | /** 27 | * 有数据返回时,状态码为0,默认提示信息为:操作成功! 28 | * @param data 29 | */ 30 | public JsonResult(T data) { 31 | this.data = data; 32 | this.code = "0"; 33 | this.msg = "操作成功!"; 34 | } 35 | 36 | /** 37 | * 有数据返回,状态码为0,人为指定提示信息 38 | * @param data 39 | * @param msg 40 | */ 41 | public JsonResult(T data, String msg) { 42 | this.data = data; 43 | this.code = "0"; 44 | this.msg = msg; 45 | } 46 | 47 | public T getData() { 48 | return data; 49 | } 50 | 51 | public void setData(T data) { 52 | this.data = data; 53 | } 54 | 55 | public String getCode() { 56 | return code; 57 | } 58 | 59 | public void setCode(String code) { 60 | this.code = code; 61 | } 62 | 63 | public String getMsg() { 64 | return msg; 65 | } 66 | 67 | public void setMsg(String msg) { 68 | this.msg = msg; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/BookMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.Book; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface BookMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/CartMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.Cart; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface CartMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/ChartMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.Chart; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ChartMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/ChartTypeMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.ChartType; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ChartTypeMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/NewsMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.News; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface NewsMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/ReviewChartMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.ReviewChart; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ReviewChartMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/ReviewMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.Review; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface ReviewMapper extends BaseMapper { 9 | } 10 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/mapper/UserMapper.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.mapper; 2 | 3 | import com.baomidou.mybatisplus.core.mapper.BaseMapper; 4 | import com.bookshop.backend.data.User; 5 | import org.springframework.stereotype.Repository; 6 | 7 | @Repository 8 | public interface UserMapper extends BaseMapper { 9 | 10 | } 11 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/utility/BookUtil.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.utility; 2 | 3 | import com.bookshop.backend.data.Book; 4 | import org.jsoup.nodes.Document; 5 | import org.jsoup.nodes.Element; 6 | 7 | import java.util.regex.Matcher; 8 | import java.util.regex.Pattern; 9 | 10 | public class BookUtil { 11 | 12 | public static void extractBookInfo (Document doc, Book info) { 13 | Element element; 14 | Matcher matcher; 15 | 16 | element = doc.select("a.nbg > img").first(); 17 | assert element != null; 18 | // 获取封面图id 19 | String picUrl = element.attr("src"); 20 | byte[] pic = RequestUtil.requestPic(picUrl); 21 | info.setPic(pic); 22 | System.out.println("pic: " + picUrl); 23 | 24 | // 获取书名 25 | info.setName(element.attr("alt")); 26 | System.out.println("title: " + element.attr("alt")); 27 | 28 | // 获取作者名 29 | element = doc.select("div#info").first(); 30 | assert element != null; 31 | String infoText = element.text(); 32 | Pattern authorPattern = Pattern.compile("作者\s*:\s(.*?)\s*出版社"); 33 | matcher = authorPattern.matcher(infoText); 34 | if (matcher.find()) { 35 | info.setAuthor(matcher.group(1)); 36 | System.out.println("author: " + matcher.group(1)); 37 | } 38 | 39 | // 获取出版社名 40 | Pattern publisherPattern = Pattern.compile("出版社\s*:\s(.*?)\s*(出品方|原作名|译者|出版年|副标题)"); 41 | matcher = publisherPattern.matcher(infoText); 42 | if (matcher.find()) { 43 | info.setPublisher(matcher.group(1)); 44 | System.out.println("publisher: " + matcher.group(1)); 45 | } 46 | 47 | // 获取出版时间 48 | Pattern datePattern = Pattern.compile("出版年\s*:\s(.*?)\s*页数"); 49 | matcher = datePattern.matcher(infoText); 50 | if (matcher.find()) { 51 | info.setDate(matcher.group(1)); 52 | System.out.println("date: " + matcher.group(1)); 53 | } 54 | 55 | // 获取价格 56 | Pattern pricePattern = Pattern.compile("定价\s*:\s(\\d{1,3}.\\d{1,3}|\\d{1,3})\s*元*"); 57 | matcher = pricePattern.matcher(infoText); 58 | if (matcher.find()) { 59 | info.setPrice(Double.valueOf(matcher.group(1))); 60 | System.out.println("price: " + matcher.group(1)); 61 | } 62 | 63 | // 获取ISBN码 64 | Pattern isbnPattern = Pattern.compile("\\d{13}?"); 65 | matcher = isbnPattern.matcher(infoText); 66 | if (matcher.find()) { 67 | info.setIsbn(matcher.group(0)); 68 | System.out.println("ISBN: " + matcher.group(0)); 69 | } 70 | 71 | // 获取图书简介 72 | element = doc.select("span.all > div > div.intro, div.indent > div > div.intro").first(); 73 | if (element != null) { 74 | info.setBookIntroduction(element.text()); 75 | System.out.println("book introduction: "); 76 | System.out.println(element.text()); 77 | } 78 | 79 | // 获取作者简介 80 | element = doc.select("span.all > div.intro, div.indent > div > div.intro").first(); 81 | if (element != null) { 82 | info.setAuthorIntroduction(element.text()); 83 | System.out.println("author introduction: "); 84 | System.out.println(element.text()); 85 | } 86 | 87 | // 获取目录 88 | String cssPattern = "div#dir_" + info.getId() + "_full"; 89 | // System.out.println(cssPattern); 90 | element = doc.select(cssPattern).first(); 91 | if (element != null) { 92 | info.setDirectory(element.text()); 93 | System.out.println("directory: "); 94 | System.out.println(element.text()); 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /Back-end/src/main/java/com/bookshop/backend/utility/RequestUtil.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend.utility; 2 | 3 | import com.bookshop.backend.data.Chart; 4 | import org.apache.http.HttpEntity; 5 | import org.apache.http.client.config.RequestConfig; 6 | import org.apache.http.client.methods.CloseableHttpResponse; 7 | import org.apache.http.client.methods.HttpGet; 8 | import org.apache.http.client.utils.HttpClientUtils; 9 | import org.apache.http.impl.client.CloseableHttpClient; 10 | import org.apache.http.impl.client.HttpClients; 11 | import org.apache.http.util.EntityUtils; 12 | import org.jsoup.Jsoup; 13 | import org.jsoup.nodes.Document; 14 | import org.springframework.http.HttpStatus; 15 | 16 | import java.io.IOException; 17 | 18 | public class RequestUtil { 19 | 20 | private static final String USER_AGENT = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/99.0.4844.51 Safari/537.36"; 21 | 22 | public static Document requestHtml(String url, String encode) { 23 | System.out.println(url); 24 | 25 | // 1.生成httpclient,相当于该打开一个浏览器 26 | CloseableHttpClient httpClient = HttpClients.createDefault(); 27 | // 设置请求和传输超时时间 28 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build(); 29 | CloseableHttpResponse response = null; 30 | Document html = null; 31 | Chart info = new Chart(); 32 | 33 | // 2.创建get请求,相当于在浏览器地址栏输入 网址 34 | HttpGet request = new HttpGet(url); 35 | try { 36 | request.setHeader("User-Agent", USER_AGENT); 37 | request.setConfig(requestConfig); 38 | 39 | // 3.执行get请求,相当于在输入地址栏后敲回车键 40 | response = httpClient.execute(request); 41 | 42 | // 4.判断响应状态为200,进行处理 43 | if (response.getStatusLine().getStatusCode() == HttpStatus.OK.value()) { 44 | System.out.println("请求成功"); 45 | 46 | // 5.获取响应内容 47 | HttpEntity httpEntity = response.getEntity(); 48 | String tmp = EntityUtils.toString(httpEntity, encode); 49 | html = Jsoup.parse(tmp); 50 | return html; 51 | } 52 | else { 53 | // 如果返回状态不是200,比如404(页面不存在)等,根据情况做处理,这里略 54 | System.out.println("请求失败"); 55 | System.out.println(EntityUtils.toString(response.getEntity(), "utf-8")); 56 | } 57 | } 58 | catch (IOException e) { 59 | e.printStackTrace(); 60 | } 61 | finally { 62 | // 6.关闭 63 | HttpClientUtils.closeQuietly(response); 64 | HttpClientUtils.closeQuietly(httpClient); 65 | } 66 | return null; 67 | } 68 | 69 | public static byte[] requestPic(String url) { 70 | System.out.println(url); 71 | 72 | // 1.生成httpclient,相当于该打开一个浏览器 73 | CloseableHttpClient httpClient = HttpClients.createDefault(); 74 | // 设置请求和传输超时时间 75 | RequestConfig requestConfig = RequestConfig.custom().setSocketTimeout(5000).setConnectTimeout(5000).build(); 76 | CloseableHttpResponse response = null; 77 | 78 | // 2.创建get请求,相当于在浏览器地址栏输入 网址 79 | HttpGet request = new HttpGet(url); 80 | try { 81 | request.setHeader("User-Agent", USER_AGENT); 82 | request.setConfig(requestConfig); 83 | 84 | // 3.执行get请求,相当于在输入地址栏后敲回车键 85 | response = httpClient.execute(request); 86 | 87 | // 4.判断响应状态为200,进行处理 88 | if (response.getStatusLine().getStatusCode() == HttpStatus.OK.value()) { 89 | System.out.println("请求成功"); 90 | 91 | // 5.获取响应内容 92 | return EntityUtils.toByteArray(response.getEntity()); 93 | } 94 | else { 95 | // 如果返回状态不是200,比如404(页面不存在)等,根据情况做处理,这里略 96 | System.out.println("请求失败"); 97 | System.out.println(EntityUtils.toString(response.getEntity(), "utf-8")); 98 | } 99 | } 100 | catch (IOException e) { 101 | e.printStackTrace(); 102 | } 103 | finally { 104 | // 6.关闭 105 | HttpClientUtils.closeQuietly(response); 106 | HttpClientUtils.closeQuietly(httpClient); 107 | } 108 | return null; 109 | 110 | } 111 | 112 | } 113 | -------------------------------------------------------------------------------- /Back-end/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | server.port=8001 2 | 3 | spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver 4 | spring.datasource.url=jdbc:mysql://localhost:3306/bookshop_system?serverTimezone=Asia/Shanghai 5 | spring.datasource.username=root 6 | spring.datasource.password=12345678 7 | 8 | spring.session.store-type=jdbc 9 | spring.session.jdbc.initialize-schema=always 10 | 11 | mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl 12 | mybatis-plus.configuration.map-underscore-to-camel-case=true 13 | -------------------------------------------------------------------------------- /Back-end/src/test/java/com/bookshop/backend/BackEndApplicationTests.java: -------------------------------------------------------------------------------- 1 | package com.bookshop.backend; 2 | 3 | import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; 4 | import com.bookshop.backend.data.*; 5 | import com.bookshop.backend.mapper.*; 6 | import com.bookshop.backend.utility.BookUtil; 7 | import com.bookshop.backend.utility.RequestUtil; 8 | import org.jsoup.nodes.Document; 9 | import org.jsoup.nodes.Element; 10 | import org.jsoup.select.Elements; 11 | import org.junit.jupiter.api.Test; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.boot.test.context.SpringBootTest; 14 | 15 | import java.util.regex.Matcher; 16 | import java.util.regex.Pattern; 17 | 18 | @SpringBootTest 19 | class BackEndApplicationTests { 20 | 21 | @Autowired 22 | private BookMapper bookMapper; 23 | @Autowired 24 | private ChartTypeMapper chartTypeMapper; 25 | @Autowired 26 | private ChartMapper chartMapper; 27 | @Autowired 28 | private ReviewChartMapper reviewChartMapper; 29 | @Autowired 30 | private ReviewMapper reviewMapper; 31 | @Autowired 32 | private NewsMapper newsMapper; 33 | 34 | private final String[] types = {"all", "literary", "history", "social", "novel", "tech", "art", "new"}; 35 | 36 | @Test 37 | void updateData() { 38 | 39 | if (false) { 40 | // 更新榜单信息 41 | for (String i : types) { 42 | System.out.println("更新" + i + "榜单"); 43 | 44 | Document doc; 45 | 46 | if (i.equals("new")) 47 | doc = RequestUtil.requestHtml("https://book.douban.com/latest?tag=%E5%85%A8%E9%83%A8", "gbk"); 48 | else doc = RequestUtil.requestHtml("https://book.douban.com/chart?subcat=" + i, "gbk"); 49 | 50 | if (doc != null) { 51 | QueryWrapper wrapper = new QueryWrapper<>(); 52 | wrapper.eq("name", i); 53 | ChartType res = chartTypeMapper.selectOne(wrapper); 54 | extractChart(doc, res.getId()); 55 | } else { 56 | System.out.println(i + "请求失败"); 57 | } 58 | 59 | } 60 | } 61 | 62 | if (false) { 63 | // 更新书评信息 64 | System.out.println("更新书评"); 65 | 66 | Document doc = RequestUtil.requestHtml("https://book.douban.com/review/best/", "gbk"); 67 | if (doc != null) { 68 | extractReviewChart(doc); 69 | } 70 | } 71 | 72 | // 更新最新资讯 73 | System.out.println("更新最新资讯"); 74 | 75 | Document doc = RequestUtil.requestHtml("https://www.nationalreading.gov.cn/ReadBook/channels/6301.shtml", "utf-8"); 76 | if (doc != null) { 77 | extractNews(doc); 78 | } 79 | 80 | } 81 | 82 | private void extractNews(Document doc) { 83 | Elements elements = doc.select("li > a"); 84 | for (Element i : elements) { 85 | News news = new News(); 86 | 87 | // 获取id号 88 | String tmp = i.attr("href"); 89 | Pattern pattern = Pattern.compile("\\d{6}"); 90 | Matcher matcher = pattern.matcher(tmp); 91 | if (matcher.find()) { 92 | news.setId(Integer.parseInt(matcher.group(0))); 93 | System.out.println("id: " + matcher.group(0)); 94 | } 95 | 96 | // 获取标题 97 | news.setTitle(i.text()); 98 | System.out.println("title: " + i.text()); 99 | 100 | // 获取内容 101 | doc = RequestUtil.requestHtml("https://www.nationalreading.gov.cn/ReadBook/contents/6301/" + news.getId() + ".shtml", "utf-8"); 102 | if (doc != null) { 103 | StringBuilder builder = new StringBuilder(); 104 | Elements p = doc.select("div.zt_m2con > p"); 105 | for (Element j : p) { 106 | builder.append(j.text()); 107 | builder.append('\n'); 108 | } 109 | String content = builder.toString(); 110 | news.setContent(content); 111 | System.out.println(content); 112 | } 113 | 114 | newsMapper.insert(news); 115 | } 116 | } 117 | 118 | private void extractReviewChart(Document doc) { 119 | Elements elements = doc.select("div.review-item"); 120 | int rank = 1; 121 | for (Element i : elements) { 122 | Element element; 123 | Review review = new Review(); 124 | 125 | // 获取书评号 126 | review.setId(Integer.parseInt(i.attr("id"))); 127 | System.out.println("id: " + i.attr("id")); 128 | 129 | // 查看数据库是否有这篇书评,有的话跳过 130 | QueryWrapper reviewChartWrapper = new QueryWrapper<>(); 131 | reviewChartWrapper.eq("review_id", review.getId()); 132 | Long exist = reviewChartMapper.selectCount(reviewChartWrapper); 133 | if (exist != 0) continue; 134 | 135 | // 获取书号 136 | element = i.select("a.subject-img").first(); 137 | assert element != null; 138 | String tmp = element.attr("href"); 139 | Pattern bookPattern = Pattern.compile("\\d{1,8}"); 140 | Matcher matcher = bookPattern.matcher(tmp); 141 | if (matcher.find()) { 142 | review.setBookId(Integer.parseInt(matcher.group(0))); 143 | System.out.println("book id: " + matcher.group(0)); 144 | 145 | // 查看是否有这本书 146 | QueryWrapper bookWrapper = new QueryWrapper<>(); 147 | bookWrapper.select("id").eq("id", review.getBookId()); 148 | Long cnt = bookMapper.selectCount(bookWrapper); 149 | 150 | // 没有这本书,爬取 151 | if (cnt == 0) { 152 | Book book = new Book(review.getBookId()); 153 | 154 | String url = "https://book.douban.com/subject/" + book.getId(); 155 | 156 | doc = RequestUtil.requestHtml(url, "gbk"); 157 | 158 | if (doc != null) { 159 | BookUtil.extractBookInfo(doc, book); 160 | bookMapper.insert(book); // 存到数据库 161 | } 162 | else { 163 | System.out.println("爬虫失败"); 164 | return; 165 | } 166 | } 167 | } 168 | 169 | // 获取题目 170 | element = i.select("div.main-bd > h2 > a").first(); 171 | assert element != null; 172 | review.setTitle(element.text()); 173 | System.out.println("title: " + element.text()); 174 | 175 | // 获取作者名 176 | element = i.select("header.main-hd > a.name").first(); 177 | assert element != null; 178 | review.setAuthor(element.text()); 179 | System.out.println("author: " + element.text()); 180 | 181 | // 获取发布时间 182 | element = i.select("header.main-hd > span.main-meta").first(); 183 | assert element != null; 184 | review.setDate(element.text()); 185 | System.out.println("date: " + element.text()); 186 | 187 | // 获取书评简介 188 | element = i.select("div.short-content").first(); 189 | assert element != null; 190 | review.setIntro(element.text()); 191 | System.out.println("intro: " + element.text()); 192 | 193 | // 获取书评内容 194 | String url = "https://book.douban.com/review/" + review.getId(); 195 | doc = RequestUtil.requestHtml(url, "gbk"); 196 | if (doc != null) { 197 | extractReviewContent(doc, review); 198 | } 199 | 200 | // 存进数据库 201 | reviewMapper.insert(review); 202 | 203 | reviewChartMapper.insert(new ReviewChart(review.getId(), rank)); 204 | 205 | ++rank; 206 | } 207 | } 208 | 209 | private void extractReviewContent(Document doc, Review info) { 210 | Elements elements = doc.select("div.review-content > p"); 211 | StringBuilder tmp = new StringBuilder(); 212 | for (Element i : elements) { 213 | tmp.append(i.text()); 214 | tmp.append('\n'); 215 | } 216 | String content = tmp.toString(); 217 | info.setContent(content); 218 | } 219 | 220 | private void extractChart(Document doc, Integer typeId) { 221 | Element element; 222 | Elements elements; 223 | elements = doc.select("li.media"); 224 | 225 | // 获取每一本书的信息 226 | Integer rank = 1; 227 | for (Element i : elements) { 228 | 229 | // 获取书号 230 | Integer id; 231 | element = i.select("div.media__body > h2 > a").first(); 232 | assert element != null; 233 | String tmp = element.attr("href"); 234 | Pattern idPattern = Pattern.compile("\\d{1,8}"); 235 | Matcher matcher = idPattern.matcher(tmp); 236 | if (matcher.find()) { 237 | id = Integer.parseInt(matcher.group(0)); 238 | System.out.println("id: " + matcher.group(0)); 239 | } 240 | else { 241 | System.out.println("请求失败"); 242 | return; 243 | } 244 | 245 | // 是否有电子书 246 | boolean hasEBook = false; 247 | element = i.select("div.ebook-link").first(); 248 | if (element != null) { 249 | hasEBook = true; 250 | System.out.println("hasEBook: Yes"); 251 | } 252 | else { 253 | System.out.println("hasEBook: No"); 254 | } 255 | 256 | // 查看数据库是否有这本书 257 | QueryWrapper wrapper = new QueryWrapper<>(); 258 | wrapper.select("id").eq("id", id); 259 | Long cnt = bookMapper.selectCount(wrapper); 260 | 261 | // 数据库中没有对应的书,爬取 262 | Book book = new Book(id); 263 | if (cnt == 0) { 264 | String url = "https://book.douban.com/subject/" + id; 265 | 266 | doc = RequestUtil.requestHtml(url, "gbk"); 267 | 268 | if (doc != null) { 269 | BookUtil.extractBookInfo(doc, book); 270 | book.setHasEBook(hasEBook); 271 | bookMapper.insert(book); // 存到数据库 272 | } 273 | else { 274 | System.out.println("爬虫失败"); 275 | return; 276 | } 277 | 278 | } 279 | 280 | // 向数据库添加榜单信息 281 | QueryWrapper chartWrapper = new QueryWrapper<>(); 282 | chartWrapper.eq("type_id", typeId).eq("book_id", id); 283 | cnt = chartMapper.selectCount(chartWrapper); 284 | if (cnt == 0) { 285 | chartMapper.insert(new Chart(typeId, id, rank)); 286 | } 287 | 288 | ++rank; 289 | } 290 | } 291 | 292 | } 293 | -------------------------------------------------------------------------------- /Back-end/tsconfig.json: -------------------------------------------------------------------------------- 1 | // This TypeScript configuration file is generated by vaadin-maven-plugin. 2 | // This is needed for TypeScript compiler to compile your TypeScript code in the project. 3 | // It is recommended to commit this file to the VCS. 4 | // You might want to change the configurations to fit your preferences 5 | // For more information about the configurations, please refer to http://www.typescriptlang.org/docs/handbook/tsconfig-json.html 6 | { 7 | "compilerOptions": { 8 | "sourceMap": true, 9 | "inlineSources": true, 10 | "module": "esNext", 11 | "target": "es2019", 12 | "moduleResolution": "node", 13 | "strict": true, 14 | "noFallthroughCasesInSwitch": true, 15 | "noImplicitReturns": true, 16 | "noImplicitAny": true, 17 | "noImplicitThis": true, 18 | "noUnusedLocals": false, 19 | "noUnusedParameters": false, 20 | "experimentalDecorators": true, 21 | "baseUrl": "frontend", 22 | "paths": { 23 | "Frontend/*": [ 24 | "*" 25 | ] 26 | } 27 | }, 28 | "include": [ 29 | "frontend/**/*.ts", 30 | "frontend/index.js", 31 | "types.d.ts" 32 | ], 33 | "exclude": [] 34 | } 35 | -------------------------------------------------------------------------------- /Back-end/types.d.ts: -------------------------------------------------------------------------------- 1 | // This TypeScript modules definition file is generated by vaadin-maven-plugin. 2 | // You can not directly import your different static files into TypeScript, 3 | // This is needed for TypeScript compiler to declare and export as a TypeScript module. 4 | // It is recommended to commit this file to the VCS. 5 | // You might want to change the configurations to fit your preferences 6 | declare module '*.css' { 7 | import { CSSResultGroup } from 'lit'; 8 | const content: CSSResultGroup; 9 | export default content; 10 | } 11 | -------------------------------------------------------------------------------- /Back-end/webpack.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | * This file contains project specific customizations for the webpack build. 3 | * It is autogenerated if it didn't exist or if it was made for an older 4 | * incompatible version. 5 | * 6 | * Defaults are provided in an autogenerated webpack.generated.js file and used by this file. 7 | * The webpack.generated.js file is overwritten on each build and no customization can be done there. 8 | */ 9 | const merge = require('webpack-merge'); 10 | const flowDefaults = require('./webpack.generated.js'); 11 | 12 | /** 13 | * To change the webpack config, add a new configuration object in 14 | * the merge arguments below: 15 | */ 16 | module.exports = merge(flowDefaults, 17 | // Override default configuration 18 | // { 19 | // mode: 'development', 20 | // devtool: 'inline-source-map', 21 | // }, 22 | 23 | // Add a custom plugin 24 | // (install the plugin with `npm install --save-dev webpack-bundle-analyzer`) 25 | // { 26 | // plugins: [ 27 | // new require('webpack-bundle-analyzer').BundleAnalyzerPlugin({ 28 | // analyzerMode: 'static' 29 | // }) 30 | // ] 31 | // }, 32 | ); 33 | -------------------------------------------------------------------------------- /Back-end/webpack.generated.js: -------------------------------------------------------------------------------- 1 | /** 2 | * NOTICE: this is an auto-generated file 3 | * 4 | * This file has been generated by the `flow:prepare-frontend` maven goal. 5 | * This file will be overwritten on every run. Any custom changes should be made to webpack.config.js 6 | */ 7 | const fs = require('fs'); 8 | const HtmlWebpackPlugin = require('html-webpack-plugin'); 9 | const CompressionPlugin = require('compression-webpack-plugin'); 10 | const { InjectManifest } = require('workbox-webpack-plugin'); 11 | const { DefinePlugin } = require('webpack'); 12 | const ExtraWatchWebpackPlugin = require('extra-watch-webpack-plugin'); 13 | const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin'); 14 | 15 | // Flow plugins 16 | const BuildStatusPlugin = require('@vaadin/build-status-plugin'); 17 | const ThemeLiveReloadPlugin = require('@vaadin/theme-live-reload-plugin'); 18 | const { 19 | ApplicationThemePlugin, 20 | processThemeResources, 21 | extractThemeName, 22 | findParentThemes 23 | } = require('@vaadin/application-theme-plugin'); 24 | 25 | const path = require('path'); 26 | 27 | // this matches /themes/my-theme/ and is used to check css url handling and file path build. 28 | const themePartRegex = /(\\|\/)themes\1[\s\S]*?\1/; 29 | 30 | // the folder of app resources: 31 | // - flow templates for classic Flow 32 | // - client code with index.html and index.[ts/js] for CCDM 33 | const frontendFolder = path.resolve(__dirname, 'frontend'); 34 | const frontendGeneratedFolder = path.resolve(__dirname, 'frontend/generated'); 35 | const fileNameOfTheFlowGeneratedMainEntryPoint = path.resolve(__dirname, 'target/frontend/generated-flow-imports.js'); 36 | const mavenOutputFolderForFlowBundledFiles = path.resolve(__dirname, 'target/classes/META-INF/VAADIN/webapp'); 37 | const mavenOutputFolderForResourceFiles = path.resolve(__dirname, 'target/classes/META-INF/VAADIN'); 38 | const useClientSideIndexFileForBootstrapping = true; 39 | const clientSideIndexHTML = './index.html'; 40 | const clientSideIndexEntryPoint = path.resolve(__dirname, 'frontend', 'generated/', 'vaadin.ts');; 41 | const pwaEnabled = false; 42 | const offlinePath = 'offline.html'; 43 | const clientServiceWorkerEntryPoint = path.resolve(__dirname, 'target/sw'); 44 | // public path for resources, must match Flow VAADIN_BUILD 45 | const VAADIN = 'VAADIN'; 46 | const build = 'build'; 47 | // public path for resources, must match the request used in flow to get the /build/stats.json file 48 | const config = 'config'; 49 | const outputFolder = mavenOutputFolderForFlowBundledFiles; 50 | const indexHtmlPath = 'index.html'; 51 | // folder for outputting vaadin-bundle and other fragments 52 | const buildFolder = path.resolve(outputFolder, VAADIN, build); 53 | // folder for outputting stats.json 54 | const confFolder = path.resolve(mavenOutputFolderForResourceFiles, config); 55 | const serviceWorkerPath = 'sw.js'; 56 | // file which is used by flow to read templates for server `@Id` binding 57 | const statsFile = `${confFolder}/stats.json`; 58 | 59 | // Folders in the project which can contain static assets. 60 | const projectStaticAssetsFolders = [ 61 | path.resolve(__dirname, 'src', 'main', 'resources', 'META-INF', 'resources'), 62 | path.resolve(__dirname, 'src', 'main', 'resources', 'static'), 63 | frontendFolder 64 | ]; 65 | 66 | const projectStaticAssetsOutputFolder = path.resolve(__dirname, 'target/classes/META-INF/VAADIN/webapp/VAADIN/static'); 67 | 68 | // Folders in the project which can contain application themes 69 | const themeProjectFolders = projectStaticAssetsFolders.map((folder) => path.resolve(folder, 'themes')); 70 | 71 | const tsconfigJsonFile = path.resolve(__dirname, 'tsconfig.json'); 72 | const enableTypeScript = fs.existsSync(tsconfigJsonFile); 73 | 74 | // Target flow-fronted auto generated to be the actual target folder 75 | const flowFrontendFolder = path.resolve(__dirname, 'target/flow-frontend'); 76 | 77 | const statsSetViaCLI = process.argv.find((v) => v.indexOf('--stats') >= 0); 78 | const devMode = process.argv.find((v) => v.indexOf('webpack-dev-server') >= 0); 79 | if (!devMode) { 80 | // make sure that build folder exists before outputting anything 81 | const mkdirp = require('mkdirp'); 82 | mkdirp(buildFolder); 83 | mkdirp(confFolder); 84 | } 85 | 86 | let stats; 87 | 88 | // Open a connection with the Java dev-mode handler in order to finish 89 | // webpack-dev-mode when it exits or crashes. 90 | const watchDogPort = devMode && process.env.watchDogPort; 91 | if (watchDogPort) { 92 | const runWatchDog = () => { 93 | const client = new require('net').Socket(); 94 | client.setEncoding('utf8'); 95 | client.on('error', function () { 96 | console.log('Watchdog connection error. Terminating webpack process...'); 97 | client.destroy(); 98 | process.exit(0); 99 | }); 100 | client.on('close', function () { 101 | client.destroy(); 102 | runWatchDog(); 103 | }); 104 | 105 | client.connect(watchDogPort, 'localhost'); 106 | }; 107 | runWatchDog(); 108 | } 109 | 110 | // Compute the entries that webpack have to visit 111 | const webPackEntries = {}; 112 | if (useClientSideIndexFileForBootstrapping) { 113 | webPackEntries.bundle = clientSideIndexEntryPoint; 114 | const dirName = path.dirname(fileNameOfTheFlowGeneratedMainEntryPoint); 115 | const baseName = path.basename(fileNameOfTheFlowGeneratedMainEntryPoint, '.js'); 116 | if ( 117 | fs 118 | .readdirSync(dirName) 119 | .filter((fileName) => !fileName.startsWith(baseName) && fileName.endsWith('.js') && fileName.includes('-')).length 120 | ) { 121 | // if there are vaadin exported views, add a second entry 122 | webPackEntries.export = fileNameOfTheFlowGeneratedMainEntryPoint; 123 | } 124 | } else { 125 | webPackEntries.bundle = fileNameOfTheFlowGeneratedMainEntryPoint; 126 | } 127 | 128 | const appShellUrl = '.'; 129 | let appShellManifestEntry = undefined; 130 | 131 | const swManifestTransform = (manifestEntries) => { 132 | const warnings = []; 133 | const manifest = manifestEntries; 134 | if (useClientSideIndexFileForBootstrapping) { 135 | // `index.html` is a special case: in contrast with the JS bundles produced by webpack 136 | // it's not served as-is directly from the webpack output at `/index.html`. 137 | // It goes through IndexHtmlRequestHandler and is served at `/`. 138 | // 139 | // TODO: calculate the revision based on the IndexHtmlRequestHandler-processed content 140 | // of the index.html file 141 | const indexEntryIdx = manifest.findIndex((entry) => entry.url === 'index.html'); 142 | if (indexEntryIdx !== -1) { 143 | manifest[indexEntryIdx].url = appShellUrl; 144 | appShellManifestEntry = manifest[indexEntryIdx]; 145 | } else { 146 | // Index entry is only emitted on first compilation. Make sure it is cached also for incremental builds 147 | manifest.push(appShellManifestEntry); 148 | } 149 | } 150 | return { manifest, warnings }; 151 | }; 152 | 153 | const createServiceWorkerPlugin = function () { 154 | return new InjectManifest({ 155 | swSrc: clientServiceWorkerEntryPoint, 156 | swDest: serviceWorkerPath, 157 | manifestTransforms: [swManifestTransform], 158 | maximumFileSizeToCacheInBytes: 100 * 1024 * 1024, 159 | dontCacheBustURLsMatching: /.*-[a-z0-9]{20}\.cache\.js/, 160 | include: [ 161 | (chunk) => { 162 | return true; 163 | } 164 | ], 165 | webpackCompilationPlugins: [ 166 | new DefinePlugin({ 167 | OFFLINE_PATH: JSON.stringify(offlinePath) 168 | }) 169 | ] 170 | }); 171 | }; 172 | 173 | const flowFrontendThemesFolder = path.resolve(flowFrontendFolder, 'themes'); 174 | const themeOptions = { 175 | devMode: devMode, 176 | // The following matches folder 'target/flow-frontend/themes/' 177 | // (not 'frontend/themes') for theme in JAR that is copied there 178 | themeResourceFolder: flowFrontendThemesFolder, 179 | themeProjectFolders: themeProjectFolders, 180 | projectStaticAssetsOutputFolder: projectStaticAssetsOutputFolder, 181 | frontendGeneratedFolder: frontendGeneratedFolder 182 | }; 183 | let themeName = undefined; 184 | let themeWatchFolders = undefined; 185 | if (devMode) { 186 | // Current theme name is being extracted from theme.js located in frontend 187 | // generated folder 188 | themeName = extractThemeName(frontendGeneratedFolder); 189 | const parentThemePaths = findParentThemes(themeName, themeOptions); 190 | const currentThemeFolders = [ 191 | ...projectStaticAssetsFolders.map((folder) => path.resolve(folder, 'themes', themeName)), 192 | path.resolve(flowFrontendThemesFolder, themeName) 193 | ]; 194 | // Watch the components folders for component styles update in both 195 | // current theme and parent themes. Other folders or CSS files except 196 | // 'styles.css' should be referenced from `styles.css` anyway, so no need 197 | // to watch them. 198 | themeWatchFolders = [...currentThemeFolders, ...parentThemePaths].map((themeFolder) => 199 | path.resolve(themeFolder, 'components') 200 | ); 201 | } 202 | 203 | const processThemeResourcesCallback = (logger) => processThemeResources(themeOptions, logger); 204 | 205 | exports = { 206 | frontendFolder: `${frontendFolder}`, 207 | buildFolder: `${buildFolder}`, 208 | confFolder: `${confFolder}` 209 | }; 210 | 211 | module.exports = { 212 | mode: 'production', 213 | context: frontendFolder, 214 | entry: webPackEntries, 215 | 216 | output: { 217 | filename: `${VAADIN}/${build}/vaadin-[name]-[contenthash].cache.js`, 218 | path: outputFolder 219 | }, 220 | 221 | resolve: { 222 | // Search for import 'x/y' inside these folders, used at least for importing an application theme 223 | modules: ['node_modules', flowFrontendFolder, ...projectStaticAssetsFolders], 224 | extensions: [enableTypeScript && '.ts', '.js'].filter(Boolean), 225 | alias: { 226 | Frontend: frontendFolder 227 | } 228 | }, 229 | 230 | stats: devMode && !statsSetViaCLI ? 'errors-warnings' : 'normal', // Unclutter output in dev mode 231 | 232 | devServer: { 233 | hot: false, // disable HMR 234 | client: false, // disable wds client as we handle reloads and errors better 235 | // webpack-dev-server serves ./, webpack-generated, and java webapp 236 | static: [outputFolder, path.resolve(__dirname, 'src', 'main', 'webapp')], 237 | setupMiddlewares: function (middlewares, devServer) { 238 | devServer.app.get(`/assetsByChunkName`, function (req, res) { 239 | res.json(stats.assetsByChunkName); 240 | }); 241 | devServer.app.get(`/stop`, function (req, res) { 242 | // eslint-disable-next-line no-console 243 | console.log("Stopped 'webpack-dev-server'"); 244 | process.exit(0); 245 | }); 246 | return middlewares; 247 | } 248 | }, 249 | 250 | module: { 251 | rules: [ 252 | enableTypeScript && { 253 | test: /\.ts$/, 254 | loader: 'esbuild-loader', 255 | options: { 256 | loader: 'ts', 257 | target: 'es2019' 258 | } 259 | }, 260 | { 261 | test: /\.css$/i, 262 | use: [ 263 | { 264 | loader: 'lit-css-loader', 265 | options: { 266 | import: 'lit' 267 | } 268 | }, 269 | { 270 | loader: 'extract-loader' 271 | }, 272 | { 273 | loader: 'css-loader', 274 | options: { 275 | url: (url, resourcePath) => { 276 | // Only translate files from node_modules 277 | const resolve = resourcePath.match(/(\\|\/)node_modules\1/); 278 | const themeResource = resourcePath.match(themePartRegex) && url.match(/^themes\/[\s\S]*?\//); 279 | return resolve || themeResource; 280 | }, 281 | // use theme-loader to also handle any imports in css files 282 | importLoaders: 1 283 | } 284 | }, 285 | { 286 | // theme-loader will change any url starting with './' to start with 'VAADIN/static' instead 287 | // NOTE! this loader should be here so it's run before css-loader as loaders are applied Right-To-Left 288 | loader: '@vaadin/theme-loader', 289 | options: { 290 | devMode: devMode 291 | } 292 | } 293 | ] 294 | }, 295 | { 296 | // File-loader only copies files used as imports in .js files or handled by css-loader 297 | test: /\.(png|gif|jpg|jpeg|svg|eot|woff|woff2|otf|ttf)$/, 298 | use: [ 299 | { 300 | loader: 'file-loader', 301 | options: { 302 | outputPath: 'VAADIN/static/', 303 | name(resourcePath, resourceQuery) { 304 | if (resourcePath.match(/(\\|\/)node_modules\1/)) { 305 | return /(\\|\/)node_modules\1(?!.*node_modules)([\S]+)/.exec(resourcePath)[2].replace(/\\/g, '/'); 306 | } 307 | if (resourcePath.match(/(\\|\/)flow-frontend\1/)) { 308 | return /(\\|\/)flow-frontend\1(?!.*flow-frontend)([\S]+)/.exec(resourcePath)[2].replace(/\\/g, '/'); 309 | } 310 | return '[path][name].[ext]'; 311 | } 312 | } 313 | } 314 | ] 315 | } 316 | ].filter(Boolean) 317 | }, 318 | performance: { 319 | maxEntrypointSize: 2097152, // 2MB 320 | maxAssetSize: 2097152 // 2MB 321 | }, 322 | plugins: [ 323 | new ApplicationThemePlugin(themeOptions), 324 | 325 | ...(devMode && themeName 326 | ? [ 327 | new ExtraWatchWebpackPlugin({ 328 | files: [], 329 | dirs: themeWatchFolders 330 | }), 331 | new ThemeLiveReloadPlugin(processThemeResourcesCallback) 332 | ] 333 | : []), 334 | 335 | function (compiler) { 336 | // V14 bootstrapping needs the bundle names 337 | compiler.hooks.afterEmit.tapAsync("FlowStatsHelper", (compilation, done) => { 338 | let miniStats = { 339 | assetsByChunkName: compilation.getStats().toJson().assetsByChunkName 340 | }; 341 | if (!devMode) { 342 | fs.writeFile(statsFile, JSON.stringify(miniStats, null, 1), 343 | () => done()); 344 | } else { 345 | stats = miniStats; 346 | done(); 347 | } 348 | }); 349 | }, 350 | 351 | // Includes JS output bundles into "index.html" 352 | useClientSideIndexFileForBootstrapping && 353 | new HtmlWebpackPlugin({ 354 | template: clientSideIndexHTML, 355 | filename: indexHtmlPath, 356 | inject: 'head', 357 | scriptLoading: 'defer', 358 | chunks: ['bundle'] 359 | }), 360 | 361 | // Service worker for offline 362 | pwaEnabled && createServiceWorkerPlugin(), 363 | 364 | // Generate compressed bundles when not devMode 365 | !devMode && new CompressionPlugin(), 366 | 367 | enableTypeScript && 368 | new ForkTsCheckerWebpackPlugin({ 369 | typescript: { 370 | configFile: tsconfigJsonFile 371 | } 372 | }), 373 | 374 | new BuildStatusPlugin() 375 | ].filter(Boolean) 376 | }; 377 | -------------------------------------------------------------------------------- /Database/bookshop_system.sql: -------------------------------------------------------------------------------- 1 | /* 2 | Navicat Premium Data Transfer 3 | 4 | Source Server : admin 5 | Source Server Type : MySQL 6 | Source Server Version : 80026 7 | Source Host : localhost:3306 8 | Source Schema : bookshop_system 9 | 10 | Target Server Type : MySQL 11 | Target Server Version : 80026 12 | File Encoding : 65001 13 | 14 | Date: 22/03/2022 11:00:26 15 | */ 16 | 17 | SET NAMES utf8mb4; 18 | SET FOREIGN_KEY_CHECKS = 0; 19 | 20 | -- ---------------------------- 21 | -- Table structure for book 22 | -- ---------------------------- 23 | DROP TABLE IF EXISTS `book`; 24 | CREATE TABLE `book` ( 25 | `id` int NOT NULL, 26 | `author` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 27 | `date` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 28 | `publisher` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 29 | `price` decimal(8, 2) NULL DEFAULT NULL, 30 | `has_e_book` tinyint(1) NOT NULL, 31 | `has_secondhand_book` tinyint(1) NOT NULL, 32 | `book_introduction` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, 33 | `author_introduction` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, 34 | `directory` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, 35 | `pic` blob NULL, 36 | `isbn` char(13) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 37 | `name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 38 | PRIMARY KEY (`id`) USING BTREE, 39 | UNIQUE INDEX `book_id_uindex`(`id` ASC) USING BTREE 40 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 41 | 42 | -- ---------------------------- 43 | -- Table structure for cart 44 | -- ---------------------------- 45 | DROP TABLE IF EXISTS `cart`; 46 | CREATE TABLE `cart` ( 47 | `user_id` int NOT NULL, 48 | `book_id` int NOT NULL, 49 | `quantity` int NOT NULL 50 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 51 | 52 | -- ---------------------------- 53 | -- Table structure for chart 54 | -- ---------------------------- 55 | DROP TABLE IF EXISTS `chart`; 56 | CREATE TABLE `chart` ( 57 | `type_id` int NOT NULL, 58 | `book_id` int NOT NULL, 59 | `rank_num` int NOT NULL, 60 | INDEX `chart_book_fk`(`book_id` ASC) USING BTREE, 61 | INDEX `chart_type_fk`(`type_id` ASC) USING BTREE, 62 | CONSTRAINT `chart_book_fk` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT, 63 | CONSTRAINT `chart_type_fk` FOREIGN KEY (`type_id`) REFERENCES `chart_type` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT 64 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 65 | 66 | -- ---------------------------- 67 | -- Table structure for chart_type 68 | -- ---------------------------- 69 | DROP TABLE IF EXISTS `chart_type`; 70 | CREATE TABLE `chart_type` ( 71 | `id` int NOT NULL, 72 | `name` varchar(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 73 | PRIMARY KEY (`id`) USING BTREE, 74 | UNIQUE INDEX `chart_type_id_uindex`(`id` ASC) USING BTREE, 75 | UNIQUE INDEX `chart_type_name_uindex`(`name` ASC) USING BTREE 76 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 77 | 78 | -- ---------------------------- 79 | -- Table structure for news 80 | -- ---------------------------- 81 | DROP TABLE IF EXISTS `news`; 82 | CREATE TABLE `news` ( 83 | `id` int NOT NULL, 84 | `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 85 | `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL, 86 | PRIMARY KEY (`id`) USING BTREE, 87 | UNIQUE INDEX `news_id_uindex`(`id` ASC) USING BTREE 88 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 89 | 90 | -- ---------------------------- 91 | -- Table structure for review 92 | -- ---------------------------- 93 | DROP TABLE IF EXISTS `review`; 94 | CREATE TABLE `review` ( 95 | `id` int NOT NULL, 96 | `book_id` int NOT NULL, 97 | `title` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 98 | `content` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 99 | `author` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 100 | `date` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 101 | `intro` text CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 102 | PRIMARY KEY (`id`) USING BTREE, 103 | UNIQUE INDEX `review_book_id_uindex`(`book_id` ASC) USING BTREE, 104 | UNIQUE INDEX `review_id_uindex`(`id` ASC) USING BTREE, 105 | CONSTRAINT `review_book_fk` FOREIGN KEY (`book_id`) REFERENCES `book` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT 106 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 107 | 108 | -- ---------------------------- 109 | -- Table structure for review_chart 110 | -- ---------------------------- 111 | DROP TABLE IF EXISTS `review_chart`; 112 | CREATE TABLE `review_chart` ( 113 | `review_id` int NOT NULL, 114 | `rank_num` int NOT NULL, 115 | UNIQUE INDEX `review_chart_review_id_uindex`(`review_id` ASC) USING BTREE, 116 | CONSTRAINT `review_chart_review_id_fk` FOREIGN KEY (`review_id`) REFERENCES `review` (`id`) ON DELETE RESTRICT ON UPDATE RESTRICT 117 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 118 | 119 | -- ---------------------------- 120 | -- Table structure for spring_session 121 | -- ---------------------------- 122 | DROP TABLE IF EXISTS `spring_session`; 123 | CREATE TABLE `spring_session` ( 124 | `PRIMARY_ID` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 125 | `SESSION_ID` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 126 | `CREATION_TIME` bigint NOT NULL, 127 | `LAST_ACCESS_TIME` bigint NOT NULL, 128 | `MAX_INACTIVE_INTERVAL` int NOT NULL, 129 | `EXPIRY_TIME` bigint NOT NULL, 130 | `PRINCIPAL_NAME` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 131 | PRIMARY KEY (`PRIMARY_ID`) USING BTREE, 132 | UNIQUE INDEX `SPRING_SESSION_IX1`(`SESSION_ID` ASC) USING BTREE, 133 | INDEX `SPRING_SESSION_IX2`(`EXPIRY_TIME` ASC) USING BTREE, 134 | INDEX `SPRING_SESSION_IX3`(`PRINCIPAL_NAME` ASC) USING BTREE 135 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; 136 | 137 | -- ---------------------------- 138 | -- Table structure for spring_session_attributes 139 | -- ---------------------------- 140 | DROP TABLE IF EXISTS `spring_session_attributes`; 141 | CREATE TABLE `spring_session_attributes` ( 142 | `SESSION_PRIMARY_ID` char(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 143 | `ATTRIBUTE_NAME` varchar(200) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 144 | `ATTRIBUTE_BYTES` blob NOT NULL, 145 | PRIMARY KEY (`SESSION_PRIMARY_ID`, `ATTRIBUTE_NAME`) USING BTREE, 146 | CONSTRAINT `SPRING_SESSION_ATTRIBUTES_FK` FOREIGN KEY (`SESSION_PRIMARY_ID`) REFERENCES `spring_session` (`PRIMARY_ID`) ON DELETE CASCADE ON UPDATE RESTRICT 147 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = DYNAMIC; 148 | 149 | -- ---------------------------- 150 | -- Table structure for user 151 | -- ---------------------------- 152 | DROP TABLE IF EXISTS `user`; 153 | CREATE TABLE `user` ( 154 | `id` int NOT NULL AUTO_INCREMENT, 155 | `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 156 | `pic` longblob NULL, 157 | `phone` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 158 | `mail` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 159 | `gender` char(2) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL, 160 | `password` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL, 161 | PRIMARY KEY (`id`) USING BTREE, 162 | UNIQUE INDEX `user_name_uindex`(`name` ASC) USING BTREE, 163 | UNIQUE INDEX `user_id_uindex`(`id` ASC) USING BTREE 164 | ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; 165 | 166 | SET FOREIGN_KEY_CHECKS = 1; 167 | -------------------------------------------------------------------------------- /Front-end/.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | /dist 4 | 5 | # local env files 6 | .env.local 7 | .env.*.local 8 | 9 | # Log files 10 | npm-debug.log* 11 | yarn-debug.log* 12 | yarn-error.log* 13 | pnpm-debug.log* 14 | 15 | # Editor directories and files 16 | .idea 17 | .vscode 18 | *.suo 19 | *.ntvs* 20 | *.njsproj 21 | *.sln 22 | *.sw? 23 | 24 | /src/assets/testFile/ 25 | -------------------------------------------------------------------------------- /Front-end/README.md: -------------------------------------------------------------------------------- 1 | # untitled1 2 | 3 | ## Project setup 4 | ``` 5 | npm install 6 | ``` 7 | 8 | ### Compiles and hot-reloads for development 9 | ``` 10 | npm run serve 11 | ``` 12 | 13 | ### Compiles and minifies for production 14 | ``` 15 | npm run build 16 | ``` 17 | 18 | ### Lints and fixes files 19 | ``` 20 | npm run lint 21 | ``` 22 | 23 | ### Customize configuration 24 | See [Configuration Reference](https://cli.vuejs.org/config/). 25 | -------------------------------------------------------------------------------- /Front-end/babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: [ 3 | '@vue/cli-plugin-babel/preset' 4 | ] 5 | } 6 | -------------------------------------------------------------------------------- /Front-end/jsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "target": "es5", 4 | "module": "esnext", 5 | "baseUrl": "./", 6 | "moduleResolution": "node", 7 | "paths": { 8 | "@/*": [ 9 | "src/*" 10 | ] 11 | }, 12 | "lib": [ 13 | "esnext", 14 | "dom", 15 | "dom.iterable", 16 | "scripthost" 17 | ] 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Front-end/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "bookshop-system", 3 | "version": "0.1.0", 4 | "private": true, 5 | "scripts": { 6 | "serve": "vue-cli-service serve", 7 | "build": "vue-cli-service build", 8 | "lint": "vue-cli-service lint" 9 | }, 10 | "dependencies": { 11 | "@element-plus/icons-vue": "^0.2.7", 12 | "axios": "^0.26.1", 13 | "core-js": "^3.8.3", 14 | "element-plus": "^2.0.2", 15 | "less": "^4.1.2", 16 | "less-loader": "^10.2.0", 17 | "vue": "^3.2.13", 18 | "vue-router": "^4.0.13", 19 | "vuex": "^4.0.2" 20 | }, 21 | "devDependencies": { 22 | "@babel/core": "^7.12.16", 23 | "@babel/eslint-parser": "^7.12.16", 24 | "@vue/cli-plugin-babel": "~5.0.0", 25 | "@vue/cli-plugin-eslint": "~5.0.0", 26 | "@vue/cli-service": "~5.0.0", 27 | "babel-plugin-import": "^1.13.3", 28 | "eslint": "^7.32.0", 29 | "eslint-plugin-vue": "^8.0.3" 30 | }, 31 | "eslintConfig": { 32 | "root": true, 33 | "env": { 34 | "node": true 35 | }, 36 | "extends": [ 37 | "plugin:vue/vue3-essential", 38 | "eslint:recommended" 39 | ], 40 | "parserOptions": { 41 | "parser": "@babel/eslint-parser" 42 | }, 43 | "rules": {} 44 | }, 45 | "browserslist": [ 46 | "> 1%", 47 | "last 2 versions", 48 | "not dead", 49 | "not ie 11" 50 | ] 51 | } 52 | -------------------------------------------------------------------------------- /Front-end/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackieCooo/BookshopSystem/60fe2a5a835a845beecf6429c9a94f82fff97df2/Front-end/public/favicon.ico -------------------------------------------------------------------------------- /Front-end/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= htmlWebpackPlugin.options.title %> 9 | 10 | 11 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /Front-end/src/App.vue: -------------------------------------------------------------------------------- 1 | 4 | 5 | 15 | 16 | 24 | -------------------------------------------------------------------------------- /Front-end/src/assets/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/JackieCooo/BookshopSystem/60fe2a5a835a845beecf6429c9a94f82fff97df2/Front-end/src/assets/Logo.png -------------------------------------------------------------------------------- /Front-end/src/components/AccountCenterPage.vue: -------------------------------------------------------------------------------- 1 | 33 | 34 | 56 | 57 | -------------------------------------------------------------------------------- /Front-end/src/components/AccountInfoPage.vue: -------------------------------------------------------------------------------- 1 | 32 | 33 | 51 | 52 | -------------------------------------------------------------------------------- /Front-end/src/components/AccountPanel.vue: -------------------------------------------------------------------------------- 1 | 76 | 77 | 84 | 85 | 204 | 205 | -------------------------------------------------------------------------------- /Front-end/src/components/AddressPage.vue: -------------------------------------------------------------------------------- 1 | 46 | 47 | 67 | 68 | -------------------------------------------------------------------------------- /Front-end/src/components/BriefCard.vue: -------------------------------------------------------------------------------- 1 | 61 | 62 | 65 | 66 | 162 | 163 | -------------------------------------------------------------------------------- /Front-end/src/components/BriefChartBoard.vue: -------------------------------------------------------------------------------- 1 | 8 | 9 | 37 | 38 | -------------------------------------------------------------------------------- /Front-end/src/components/ChartBoard.vue: -------------------------------------------------------------------------------- 1 | 26 | 27 | 33 | 34 | 82 | 83 | -------------------------------------------------------------------------------- /Front-end/src/components/ChartPage.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 40 | 41 | -------------------------------------------------------------------------------- /Front-end/src/components/CollectionPage.vue: -------------------------------------------------------------------------------- 1 | 20 | 21 | 36 | 37 | -------------------------------------------------------------------------------- /Front-end/src/components/DetailedChartBoard.vue: -------------------------------------------------------------------------------- 1 | 45 | 46 | 95 | 96 | -------------------------------------------------------------------------------- /Front-end/src/components/FrontPage.vue: -------------------------------------------------------------------------------- 1 | 31 | 32 | 48 | 49 | 59 | 60 | -------------------------------------------------------------------------------- /Front-end/src/components/HottestSellersPage.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 18 | 19 | 70 | 71 | -------------------------------------------------------------------------------- /Front-end/src/components/InfoCard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 36 | 37 | -------------------------------------------------------------------------------- /Front-end/src/components/LatestBookPage.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 27 | 28 | -------------------------------------------------------------------------------- /Front-end/src/components/LoginDialog.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 20 | 21 | 71 | 72 | -------------------------------------------------------------------------------- /Front-end/src/components/MainFrame.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 31 | 32 | 49 | -------------------------------------------------------------------------------- /Front-end/src/components/MemberPage.vue: -------------------------------------------------------------------------------- 1 | 29 | 30 | 40 | 41 | -------------------------------------------------------------------------------- /Front-end/src/components/MenuBar.vue: -------------------------------------------------------------------------------- 1 | 10 | 11 | 16 | 17 | 44 | 45 | -------------------------------------------------------------------------------- /Front-end/src/components/OrderPage.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 35 | 36 | -------------------------------------------------------------------------------- /Front-end/src/components/PageHeader.vue: -------------------------------------------------------------------------------- 1 | 11 | 12 | 17 | 18 | -------------------------------------------------------------------------------- /Front-end/src/components/PasswordChangingPage.vue: -------------------------------------------------------------------------------- 1 | 27 | 28 | 45 | 46 | -------------------------------------------------------------------------------- /Front-end/src/components/ProductPage.vue: -------------------------------------------------------------------------------- 1 | 53 | 54 | 59 | 60 | 120 | 121 | -------------------------------------------------------------------------------- /Front-end/src/components/RankChangeIcon.vue: -------------------------------------------------------------------------------- 1 | 19 | 20 | 43 | 44 | -------------------------------------------------------------------------------- /Front-end/src/components/RegisterDialog.vue: -------------------------------------------------------------------------------- 1 | 12 | 13 | 28 | 29 | -------------------------------------------------------------------------------- /Front-end/src/components/ReviewChartPage.vue: -------------------------------------------------------------------------------- 1 | 22 | 23 | 29 | 30 | 64 | 65 | -------------------------------------------------------------------------------- /Front-end/src/components/ReviewPage.vue: -------------------------------------------------------------------------------- 1 | 39 | 40 | 72 | 73 | -------------------------------------------------------------------------------- /Front-end/src/components/RightSideBoard.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 47 | 48 | -------------------------------------------------------------------------------- /Front-end/src/components/SearchPanel.vue: -------------------------------------------------------------------------------- 1 | 38 | 39 | 84 | 85 | -------------------------------------------------------------------------------- /Front-end/src/components/SearchResultPage.vue: -------------------------------------------------------------------------------- 1 | 14 | 15 | 43 | 44 | -------------------------------------------------------------------------------- /Front-end/src/components/SideBoard.vue: -------------------------------------------------------------------------------- 1 | 16 | 17 | 44 | 45 | -------------------------------------------------------------------------------- /Front-end/src/components/TabPage.vue: -------------------------------------------------------------------------------- 1 | 15 | 16 | 54 | 55 | -------------------------------------------------------------------------------- /Front-end/src/components/TopBar.vue: -------------------------------------------------------------------------------- 1 | 9 | 10 | 20 | 21 | -------------------------------------------------------------------------------- /Front-end/src/components/WindowHeader.vue: -------------------------------------------------------------------------------- 1 | 57 | 58 | 61 | 62 | 130 | 131 | -------------------------------------------------------------------------------- /Front-end/src/main.js: -------------------------------------------------------------------------------- 1 | import { createApp } from 'vue' 2 | import App from './App.vue' 3 | import ElementPlus from 'element-plus' 4 | import router from './router'; 5 | import 'element-plus/dist/index.css' 6 | import axios from 'axios'; 7 | import store from './store' 8 | 9 | const app = createApp(App) 10 | axios.defaults.baseURL = 'http://localhost:8001' 11 | app.config.globalProperties.$http = axios; 12 | 13 | app.use(ElementPlus) 14 | app.use(router) 15 | app.use(store) 16 | app.mount('#app') 17 | -------------------------------------------------------------------------------- /Front-end/src/router.js: -------------------------------------------------------------------------------- 1 | import {createRouter, createWebHashHistory} from "vue-router"; 2 | 3 | import FrontPage from "@/components/FrontPage"; 4 | import ChartPage from "@/components/ChartPage"; 5 | import ReviewChartPage from "@/components/ReviewChartPage"; 6 | import LatestBookPage from "@/components/LatestBookPage"; 7 | import HottestSellersPage from "@/components/HottestSellersPage"; 8 | import ChartBoard from "@/components/ChartBoard"; 9 | import ProductPage from "@/components/ProductPage"; 10 | import MemberPage from "@/components/MemberPage"; 11 | import AccountCenterPage from "@/components/AccountCenterPage"; 12 | import AccountInfoPage from "@/components/AccountInfoPage"; 13 | import PasswordChangingPage from "@/components/PasswordChangingPage"; 14 | import AddressPage from "@/components/AddressPage"; 15 | import CollectionPage from "@/components/CollectionPage"; 16 | import OrderPage from "@/components/OrderPage"; 17 | import ReviewPage from "@/components/ReviewPage"; 18 | import SearchResultPage from "@/components/SearchResultPage"; 19 | 20 | const StyleChartBoard = { 21 | template: '', 22 | components: { 23 | ChartBoard, 24 | } 25 | } 26 | 27 | const routes = [ 28 | { 29 | path: '/', 30 | redirect: '/home' 31 | }, 32 | { 33 | path: '/home', 34 | component: FrontPage, 35 | }, 36 | { 37 | path: '/chart', 38 | component: ChartPage, 39 | }, 40 | { 41 | path: '/review', 42 | component: ReviewChartPage, 43 | }, 44 | { 45 | path: '/review/:id', 46 | component: ReviewPage, 47 | }, 48 | { 49 | path: '/latest', 50 | component: LatestBookPage 51 | }, 52 | { 53 | path: '/hottest', 54 | redirect: '/hottest/all', 55 | component: HottestSellersPage, 56 | children: [ 57 | {path: ':type', component: StyleChartBoard}, 58 | ], 59 | }, 60 | { 61 | path: '/book/:id', 62 | component: ProductPage, 63 | }, 64 | { 65 | path: '/member', 66 | redirect: '/member/home', 67 | component: MemberPage, 68 | children: [ 69 | {path: 'home', component: AccountCenterPage}, 70 | {path: 'account', component: AccountInfoPage}, 71 | {path: 'password', component: PasswordChangingPage}, 72 | {path: 'address', component: AddressPage}, 73 | {path: 'collection', component: CollectionPage}, 74 | {path: 'order', component: OrderPage}, 75 | ] 76 | }, 77 | { 78 | path: '/search/:content', 79 | component: SearchResultPage, 80 | }, 81 | ] 82 | 83 | const router = createRouter({ 84 | history: createWebHashHistory(), 85 | routes, 86 | }) 87 | 88 | export default router 89 | -------------------------------------------------------------------------------- /Front-end/src/store.js: -------------------------------------------------------------------------------- 1 | import {createStore} from 'vuex'; 2 | 3 | const store = createStore({ 4 | state: { 5 | isLogin: false, 6 | baseUrl: 'http://localhost:8001/', 7 | user: { 8 | id: null, 9 | name: null, 10 | phone: null, 11 | mail: null, 12 | gender: null, 13 | password: null, 14 | }, 15 | }, 16 | mutations: { 17 | login(state, user) { 18 | state.user.id = user.id 19 | state.user.name = user.name 20 | state.user.phone = user.phone 21 | state.user.gender = user.gender 22 | state.user.mail = user.mail 23 | state.user.password = user.password 24 | state.isLogin = true 25 | }, 26 | logout(state) { 27 | state.user.id = null 28 | state.user.name = null 29 | state.user.phone = null 30 | state.user.gender = null 31 | state.user.mail = null 32 | state.user.password = null 33 | state.isLogin = false 34 | } 35 | }, 36 | }) 37 | 38 | export default store 39 | -------------------------------------------------------------------------------- /Front-end/vue.config.js: -------------------------------------------------------------------------------- 1 | const { defineConfig } = require('@vue/cli-service') 2 | module.exports = defineConfig({ 3 | transpileDependencies: true, 4 | runtimeCompiler: true, // 运行时编译 5 | publicPath: './', 6 | productionSourceMap: false, 7 | }) 8 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 四、本站软件环境要求 2 | 4.1 基本环境要求 3 | 1.开发工具: 4 | ① 前端IDE:WebStorm 2021.3 5 | ② 后端IDE:IntelliJ IDEA 2021.3 6 | 2.前端环境: 7 | ① 主框架:Vue3 全家桶 8 | ② GUI:Element-plus 2.1.x 9 | ③ 包管理:Node.js 6.14.8 10 | ④ 项目打包:Webpack 11 | 3.后端环境: 12 | ① 主框架:Springboot 2.6.4 13 | ② 数据库:MySQL 8.0.x 14 | ③ 数据库框架:Mybatis-plus 3.5.1 15 | ④ 项目管理:Maven 4.0.x 16 | ⑤ 服务器:Tomcat 9.0.x 17 | ⑥ 项目打包:Webpack 18 | ⑦ JDK:jdk 17 19 | 4.2 使用注意事项 20 | 本设计是一个前后端分离的项目,基本结构包括前端、后端与数据库。在源代码文件夹下包含Front-end、Back-end及Database文件夹,分别对应上述3个结构。 21 | 其中网页的index.html文件在Front-end文件夹的dist文件夹内,打开该html文件将只会渲染网页的离线部分;若要成功渲染网页的全部部分,必须同时启动运行后端服务器与数据库。 22 | 后端的项目管理使用Maven进行管理,因此在运行后端服务器前,确认本机上已配置对应的Maven服务,否则将无法加载对应的第三方包。 23 | 若开启后端服务器后,网页仍不能成功渲染到全部数据,请检查“Front-end”-“src”-“main.js”文件内的“axios.defaults.baseURL = 'http://localhost:8001'”代码是否对应自己电脑的IP地址。 --------------------------------------------------------------------------------