├── .gitattributes ├── .gitignore ├── .mvn └── wrapper │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── README.md ├── images ├── IMDB.png ├── PushOrPull.png ├── anyncQueue.png ├── beanNull.png ├── chooseDependencies.png ├── createrWebSpringboot.png ├── hackerNews.png ├── mailformError.png ├── projectProperties.png ├── reddit.png ├── springbootProject.png ├── stackoverflow.png ├── ticket.png ├── timeline.png └── tree.png ├── mvnw ├── mvnw.cmd ├── pom.xml └── src └── main ├── java └── com │ └── suny │ ├── WendaApplication.java │ ├── aspect │ └── LogAspect.java │ ├── async │ ├── EventConsumer.java │ ├── EventHandler.java │ ├── EventModel.java │ ├── EventProducer.java │ ├── EventType.java │ └── handler │ │ ├── FeedHandler.java │ │ ├── LikeHandler.java │ │ └── LoginExceptionHandler.java │ ├── configuration │ └── WendaWebConfiguration.java │ ├── controller │ ├── CommentController.java │ ├── FeedController.java │ ├── FollowController.java │ ├── HomeController.java │ ├── LikeController.java │ ├── LoginController.java │ ├── MessageController.java │ ├── QuestionController.java │ ├── SearchController.java │ └── SettingController.java │ ├── dao │ ├── CommentDAO.java │ ├── FeedDAO.java │ ├── LoginTicketDAO.java │ ├── MessageDAO.java │ ├── QuestionDAO.java │ └── UserDAO.java │ ├── interceptor │ ├── LoginRequiredInterceptor.java │ └── PassportInterceptor.java │ ├── model │ ├── Comment.java │ ├── EntityType.java │ ├── Feed.java │ ├── HostHolder.java │ ├── LoginTicket.java │ ├── Message.java │ ├── Question.java │ ├── User.java │ └── ViewObject.java │ ├── service │ ├── CommentService.java │ ├── FeedService.java │ ├── FollowService.java │ ├── LikeService.java │ ├── MessageService.java │ ├── QuestionService.java │ ├── SearchService.java │ ├── SensitiveService.java │ ├── TicketService.java │ ├── UserService.java │ └── WendaService.java │ └── utils │ ├── JedisAdapter.java │ ├── MailSender.java │ ├── MailSetting.java │ ├── RedisKeyUtil.java │ └── WendaUtil.java └── resources ├── SensitiveWord.txt ├── application.properties ├── com └── suny │ └── dao │ ├── FeedDAO.xml │ ├── LoginTicketDAO.xml │ └── QuestionDAO.xml ├── log4j.properties ├── mybatis-config.xml ├── sql ├── comment.sql ├── feed.sql ├── login_ticket.sql ├── message.sql ├── question.sql └── user.sql ├── static ├── images │ ├── img │ │ ├── spinner2.8f60205d.gif │ │ ├── sprites-1.9.2.4c54885a.png │ │ ├── sprites-1.9.2@2x.6e638473.png │ │ ├── sprites.auto.915a539c.png │ │ └── sprites@2x.auto.dd5c79c1.png │ └── res │ │ ├── 070a9fb26_is.jpg │ │ ├── 0b856ef58f76b7c83a0e130f6ef71281_200x112.jpg │ │ ├── 0ba3da3f03ced7a438118b0be77df56c_m.jpg │ │ ├── 0c6a39621ab1d456b1e6e492d0becc0c_s.jpg │ │ ├── 0cf21546298ad1ed3cb64be61d822c27_m.jpg │ │ ├── 0cffb89d0b0bd4e726ae54b212a31c3b_200x112.jpg │ │ ├── 102799979_m.jpg │ │ ├── 10a4cd7fb082375332be33eadfd14c58_is.jpg │ │ ├── 11ba31c8bb5473a44b7690eff24b9123_is.jpg │ │ ├── 11be4a90ed938abfbab4899df56ee754_s.png │ │ ├── 12a8e1ce5ad8060fddb93ae2df98028b_m.jpg │ │ ├── 19456ebfe8b207320735f282769ac635_s.jpg │ │ ├── 1ac7840eeb19ada0bbf85f51702d5784_s.jpg │ │ ├── 1ce495b02_m.jpg │ │ ├── 23cace5cf60f39dbc095bd7a12b2cfad_200x112.jpg │ │ ├── 24ce38dd5fc2a0c4e0525e577eef7d64_s.png │ │ ├── 272627e471a533f58f319a9e600e0a94_is.png │ │ ├── 2e21e58a990f5c756e813a64a4bba14c_m.jpg │ │ ├── 31826765d442d8222a05cd67d0643a25_270x225.png │ │ ├── 33fb6f51a3f4e16b6e89172040451dca_b.png │ │ ├── 3b673d6335ef6788d1659ee2b6381e97_m.jpg │ │ ├── 41d652d947a489e056b0179ba137294b_m.png │ │ ├── 450c9d9f5240f05f73d21fe3ae76f1a6_200x112.jpg │ │ ├── 4528283ed249589634546327431667bf_is.jpg │ │ ├── 4d104b6c6a08f7e1a48f4f32c88b1ce2_m.jpg │ │ ├── 51559bbebaa7fd395c271b7b1c8b9f26_m.jpg │ │ ├── 558b3e3d8be209247159ba4f83ab1c02_200x112.jpg │ │ ├── 6088e39f2_is.jpg │ │ ├── 66a689b2c60557eae79d839aaedf48b0_200x112.png │ │ ├── 6c76223a5aef2f1d29c680a2524de791_is.jpg │ │ ├── 6ceea810748d179f57cac0baa5cf9592_s.jpg │ │ ├── 6cfbfc0c5e3c5ecd8784f7e733a75b4f_m.jpg │ │ ├── 6fd46860a4b6cbc1e52d676f217ea9fd_is.jpg │ │ ├── 7412830858217e93f6c5d06f6328cbd5_200x112.png │ │ ├── 7986ef6045f2cef8352be5affce5f7d1_is.jpg │ │ ├── 837b60aa36029a2309974e74de3b62e9_xl.png │ │ ├── 845303838eca4a5b5f03cc3ca994ec28_s.png │ │ ├── 845c492813e72b85c6e11cccf8ed0ff8_m.jpg │ │ ├── 935f87219_m.jpg │ │ ├── 975baaf73fd76f48ce6f05e19b176878_xl.jpg │ │ ├── 9cfe980ca44e38bd9b0e5c3dee5b0f3e_270x225.jpg │ │ ├── 9ec0168d1b210d9b8f089e16f521b82b_200x112.jpg │ │ ├── a3f80f6a6_m.jpg │ │ ├── a4df63114bd9374a73775e30db1cdd36_is.jpg │ │ ├── a57fdaff90865eaaef2e87051624862b_m.jpg │ │ ├── aa49b7ceff22ea68ca5f747115cd17af_200x112.jpg │ │ ├── aadd7b895_m.jpg │ │ ├── aadd7b895_s.jpg │ │ ├── actioncard-suggested-avatar.c5af416d.png │ │ ├── actioncard-validation@2x.65147043.png │ │ ├── b144d91ec_m.jpg │ │ ├── b3aadf89405941b05a5ce00fb06f8281_m.jpg │ │ ├── b476f1461388bf4907634009904739e6_is.jpg │ │ ├── b4a6228e6810d38e19e491c173af4d5c_m.png │ │ ├── b85ddd8aa_xl.jpg │ │ ├── badaaf312e59c125928bd1ea2d4b5a51_b.jpg │ │ ├── bb73b7fb48b0cb63aa573415dfe4d0c5_m.jpg │ │ ├── bc93fd5289c13c06fd569bc4f6df821e_is.jpg │ │ ├── be39d110759e68f389b7d2934d7353bc_m.jpg │ │ ├── c07f969d8dcc6251ef114fe6b1a9a563_200x112.jpg │ │ ├── c28e9af7f_m.jpg │ │ ├── c54bb4367803ba590035d2d89d1a84dd_is.jpg │ │ ├── c79247853_m.jpg │ │ ├── c898060535edfdbe4147d2135c29787f_200x112.jpg │ │ ├── c94c92af8_m.jpg │ │ ├── cafae465b8ea283498c69ab9757f86ba_xl.jpg │ │ ├── captcha.gif │ │ ├── cbc5d3c6f333215a1c480cb3b4735b45_m.jpg │ │ ├── d207854fffc9e0289fbd6bbbb3986988_s.jpg │ │ ├── d6842d77b4bda238e0db09217e3d2f8d_270x225.jpg │ │ ├── d822a919d93a761634a67c2022a3f614_m.jpg │ │ ├── da8e974dc_is.jpg │ │ ├── da8e974dc_m.jpg │ │ ├── da8e974dc_s.jpg │ │ ├── df4aa616fdcfbd861c010ff71aaef95c_b.jpg │ │ ├── e174d6d0c_is.jpg │ │ ├── e6f4caaaa_m.jpg │ │ ├── e8757728eb70adeb8ebaa0864874c29d_is.jpg │ │ ├── ec6bb3fa05625b848ac4d475ecce35c9_is.jpg │ │ ├── f1148eb1c7170cabb0a78dad73b590f6_200x112.jpg │ │ ├── f99201e42237de4fb7f8d5fbf9a2d270_is.jpg │ │ ├── fa70eff301ba417d4a9d3f55d603a29e_s.png │ │ ├── facfb45ac94f174655695853d4470bac_200x112.jpg │ │ ├── fb05f15bf8bffd1590df442ff6ba7812_200x112.jpg │ │ ├── fb6c4dd60a9f19f5fcd8265395e11f9e_200x112.jpg │ │ ├── fff25000064308791f739149af611439_is.jpg │ │ ├── hour.3d371c99.png │ │ ├── nacl.656ec1c4.png │ │ ├── nk.png │ │ └── weekly.65279d61.png ├── scripts │ ├── aza-0.1.1.min.js │ ├── base.fb262f0e.js │ ├── common.fed5411f.js │ ├── ga.js │ ├── instant.e7a17de6.js │ ├── main │ │ ├── base │ │ │ ├── base.js │ │ │ ├── event.js │ │ │ └── util.js │ │ ├── component │ │ │ ├── component.js │ │ │ ├── popup.js │ │ │ ├── popupAdd.js │ │ │ └── popupMsg.js │ │ ├── jquery.js │ │ ├── site │ │ │ ├── detail.js │ │ │ ├── follow.js │ │ │ ├── home.js │ │ │ └── profile.js │ │ └── util │ │ │ ├── action.js │ │ │ └── business.js │ ├── page-index.d7b54ac7.js │ ├── page-main.5d4c554d.js │ ├── richtexteditor.aa0a4005.js │ ├── vendor.ff76fbae.js │ └── za-0.1.1.min.js └── styles │ ├── bootstrap.min.css │ ├── detail.css │ ├── index.css │ ├── letter.css │ ├── login.css │ └── result.css ├── templates ├── detail.html ├── error.html ├── feeds.html ├── followees.html ├── followers.html ├── footer.html ├── header.html ├── index.html ├── js.html ├── letter.html ├── letterDetail.html ├── login.html ├── mails │ └── login_exception.html └── profile.html └── toolbox.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | *.css linguist-language=java 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | logs/ 4 | out/ 5 | src/test/ 6 | 7 | ### important password file 8 | src/main/resources/mailSetting.properties 9 | 10 | ### STS ### 11 | .apt_generated 12 | .classpath 13 | .factorypath 14 | .project 15 | .settings 16 | .springBeans 17 | 18 | ### IntelliJ IDEA ### 19 | .idea 20 | *.iws 21 | *.iml 22 | *.ipr 23 | 24 | 25 | ### NetBeans ### 26 | nbproject/private/ 27 | build/ 28 | nbbuild/ 29 | dist/ 30 | nbdist/ 31 | .nb-gradle/ 32 | 33 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionUrl=https://repo1.maven.org/maven2/org/apache/maven/apache-maven/3.5.0/apache-maven-3.5.0-bin.zip 2 | -------------------------------------------------------------------------------- /images/IMDB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/IMDB.png -------------------------------------------------------------------------------- /images/PushOrPull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/PushOrPull.png -------------------------------------------------------------------------------- /images/anyncQueue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/anyncQueue.png -------------------------------------------------------------------------------- /images/beanNull.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/beanNull.png -------------------------------------------------------------------------------- /images/chooseDependencies.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/chooseDependencies.png -------------------------------------------------------------------------------- /images/createrWebSpringboot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/createrWebSpringboot.png -------------------------------------------------------------------------------- /images/hackerNews.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/hackerNews.png -------------------------------------------------------------------------------- /images/mailformError.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/mailformError.png -------------------------------------------------------------------------------- /images/projectProperties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/projectProperties.png -------------------------------------------------------------------------------- /images/reddit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/reddit.png -------------------------------------------------------------------------------- /images/springbootProject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/springbootProject.png -------------------------------------------------------------------------------- /images/stackoverflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/stackoverflow.png -------------------------------------------------------------------------------- /images/ticket.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/ticket.png -------------------------------------------------------------------------------- /images/timeline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/timeline.png -------------------------------------------------------------------------------- /images/tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/images/tree.png -------------------------------------------------------------------------------- /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 http://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 Maven2 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 key stroke 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 enable echoing my setting MAVEN_BATCH_ECHO to 'on' 39 | @if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% 40 | 41 | @REM set %HOME% to equivalent of $HOME 42 | if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") 43 | 44 | @REM Execute a user defined script before this one 45 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre 46 | @REM check for pre script, once with legacy .bat ending and once with .cmd ending 47 | if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" 48 | if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" 49 | :skipRcPre 50 | 51 | @setlocal 52 | 53 | set ERROR_CODE=0 54 | 55 | @REM To isolate internal variables from possible post scripts, we use another setlocal 56 | @setlocal 57 | 58 | @REM ==== START VALIDATION ==== 59 | if not "%JAVA_HOME%" == "" goto OkJHome 60 | 61 | echo. 62 | echo Error: JAVA_HOME not found in your environment. >&2 63 | echo Please set the JAVA_HOME variable in your environment to match the >&2 64 | echo location of your Java installation. >&2 65 | echo. 66 | goto error 67 | 68 | :OkJHome 69 | if exist "%JAVA_HOME%\bin\java.exe" goto init 70 | 71 | echo. 72 | echo Error: JAVA_HOME is set to an invalid directory. >&2 73 | echo JAVA_HOME = "%JAVA_HOME%" >&2 74 | echo Please set the JAVA_HOME variable in your environment to match the >&2 75 | echo location of your Java installation. >&2 76 | echo. 77 | goto error 78 | 79 | @REM ==== END VALIDATION ==== 80 | 81 | :init 82 | 83 | @REM Find the project base dir, i.e. the directory that contains the folder ".mvn". 84 | @REM Fallback to current working directory if not found. 85 | 86 | set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% 87 | IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir 88 | 89 | set EXEC_DIR=%CD% 90 | set WDIR=%EXEC_DIR% 91 | :findBaseDir 92 | IF EXIST "%WDIR%"\.mvn goto baseDirFound 93 | cd .. 94 | IF "%WDIR%"=="%CD%" goto baseDirNotFound 95 | set WDIR=%CD% 96 | goto findBaseDir 97 | 98 | :baseDirFound 99 | set MAVEN_PROJECTBASEDIR=%WDIR% 100 | cd "%EXEC_DIR%" 101 | goto endDetectBaseDir 102 | 103 | :baseDirNotFound 104 | set MAVEN_PROJECTBASEDIR=%EXEC_DIR% 105 | cd "%EXEC_DIR%" 106 | 107 | :endDetectBaseDir 108 | 109 | IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig 110 | 111 | @setlocal EnableExtensions EnableDelayedExpansion 112 | for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a 113 | @endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% 114 | 115 | :endReadAdditionalConfig 116 | 117 | SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" 118 | 119 | set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" 120 | set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain 121 | 122 | %MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* 123 | if ERRORLEVEL 1 goto error 124 | goto end 125 | 126 | :error 127 | set ERROR_CODE=1 128 | 129 | :end 130 | @endlocal & set ERROR_CODE=%ERROR_CODE% 131 | 132 | if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost 133 | @REM check for post script, once with legacy .bat ending and once with .cmd ending 134 | if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" 135 | if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" 136 | :skipRcPost 137 | 138 | @REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' 139 | if "%MAVEN_BATCH_PAUSE%" == "on" pause 140 | 141 | if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% 142 | 143 | exit /B %ERROR_CODE% 144 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 4.0.0 5 | 6 | com.suny 7 | wenda 8 | 0.0.1-SNAPSHOT 9 | jar 10 | 11 | wenda 12 | a wenda project for Spring Boot 13 | 14 | 15 | org.springframework.boot 16 | spring-boot-starter-parent 17 | 1.3.6.RELEASE 18 | 19 | 20 | 21 | 22 | UTF-8 23 | 1.8 24 | 25 | 26 | 27 | 28 | org.springframework.boot 29 | spring-boot-starter-aop 30 | 31 | 32 | org.springframework.boot 33 | spring-boot-devtools 34 | 35 | 36 | org.springframework.boot 37 | spring-boot-starter-velocity 38 | 39 | 40 | org.springframework.boot 41 | spring-boot-starter-web 42 | 43 | 44 | 45 | mysql 46 | mysql-connector-java 47 | runtime 48 | 49 | 50 | org.mybatis.spring.boot 51 | mybatis-spring-boot-starter 52 | 1.1.1 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-test 57 | test 58 | 59 | 60 | 61 | 62 | com.alibaba 63 | fastjson 64 | 1.2.31 65 | 66 | 67 | 68 | redis.clients 69 | jedis 70 | 2.8.0 71 | 72 | 73 | 74 | javax.mail 75 | mail 76 | 1.4.7 77 | 78 | 79 | 80 | org.apache.solr 81 | solr-solrj 82 | 6.2.0 83 | 84 | 85 | 86 | 87 | 88 | org.springframework.boot 89 | spring-boot-configuration-processor 90 | true 91 | 92 | 93 | 94 | 95 | 96 | 97 | org.springframework.boot 98 | spring-boot-maven-plugin 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/com/suny/WendaApplication.java: -------------------------------------------------------------------------------- 1 | package com.suny; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class WendaApplication { 8 | 9 | public static void main(String[] args) { 10 | SpringApplication.run(WendaApplication.class, args); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/com/suny/aspect/LogAspect.java: -------------------------------------------------------------------------------- 1 | package com.suny.aspect; 2 | 3 | import org.aspectj.lang.JoinPoint; 4 | import org.aspectj.lang.annotation.After; 5 | import org.aspectj.lang.annotation.Aspect; 6 | import org.aspectj.lang.annotation.Before; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.stereotype.Component; 10 | 11 | import java.util.Date; 12 | 13 | /** 14 | * 切面日志类 15 | * Created by 孙建荣 on 17-9-2.下午6:23 16 | */ 17 | @Aspect 18 | @Component 19 | public class LogAspect { 20 | 21 | private static final Logger logger = LoggerFactory.getLogger(LogAspect.class); 22 | 23 | 24 | @Before("execution(* com.suny.controller.*.*(..))") 25 | public void beforeMethod(JoinPoint joinPoint) { 26 | StringBuilder sb = new StringBuilder(); 27 | for (Object arg : joinPoint.getArgs()) { 28 | if (arg != null) { 29 | sb.append("arg:").append(arg.toString()).append("|"); 30 | } 31 | } 32 | logger.info("before method" + sb.toString()); 33 | } 34 | 35 | 36 | @After("execution(* com.suny.controller.*.*(..))") 37 | public void afterMethod() { 38 | logger.info("after method" + new Date()); 39 | } 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/EventConsumer.java: -------------------------------------------------------------------------------- 1 | package com.suny.async; 2 | 3 | import com.alibaba.fastjson.JSON; 4 | import com.suny.utils.JedisAdapter; 5 | import com.suny.utils.RedisKeyUtil; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.BeansException; 9 | import org.springframework.beans.factory.BeanInitializationException; 10 | import org.springframework.beans.factory.InitializingBean; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.context.ApplicationContext; 13 | import org.springframework.context.ApplicationContextAware; 14 | import org.springframework.stereotype.Service; 15 | 16 | import java.util.ArrayList; 17 | import java.util.HashMap; 18 | import java.util.List; 19 | import java.util.Map; 20 | 21 | /** 22 | * Created by 孙建荣 on 17-9-7.下午4:50 23 | */ 24 | @Service 25 | public class EventConsumer implements InitializingBean, ApplicationContextAware { 26 | 27 | private static Logger logger = LoggerFactory.getLogger(EventConsumer.class); 28 | 29 | private Map> config = new HashMap<>(); 30 | 31 | private ApplicationContext applicationContext; 32 | 33 | 34 | private final JedisAdapter jedisAdapter; 35 | 36 | @Autowired 37 | public EventConsumer(JedisAdapter jedisAdapter) { 38 | this.jedisAdapter = jedisAdapter; 39 | } 40 | 41 | 42 | @Override 43 | public void afterPropertiesSet() throws Exception { 44 | 45 | Map beans = applicationContext.getBeansOfType(EventHandler.class); 46 | if (beans != null) { 47 | for (Map.Entry entry : beans.entrySet()) { 48 | List eventTypes = entry.getValue().getSupportEventTypes(); 49 | 50 | for (EventType type : eventTypes) { 51 | if (!config.containsKey(type)) { 52 | config.put(type, new ArrayList<>()); 53 | } 54 | config.get(type).add(entry.getValue()); 55 | } 56 | } 57 | } 58 | 59 | 60 | Thread thread = new Thread(new Runnable() { 61 | @Override 62 | public void run() { 63 | while (true) { 64 | String key = RedisKeyUtil.getEventQueueKey(); 65 | List events = jedisAdapter.brpop(0, key); 66 | 67 | for (String message : events) { 68 | if (message.equals(key)) { 69 | continue; 70 | } 71 | EventModel eventModel = JSON.parseObject(message, EventModel.class); 72 | if (!config.containsKey(eventModel.getType())) { 73 | logger.error("不能识别的事件"); 74 | continue; 75 | } 76 | 77 | for (EventHandler handler : config.get(eventModel.getType())) { 78 | handler.doHandle(eventModel); 79 | } 80 | 81 | } 82 | } 83 | } 84 | }); 85 | 86 | thread.start(); 87 | 88 | } 89 | 90 | 91 | @Override 92 | public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 93 | this.applicationContext = applicationContext; 94 | } 95 | } 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/EventHandler.java: -------------------------------------------------------------------------------- 1 | package com.suny.async; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-7.下午4:38 7 | */ 8 | public interface EventHandler { 9 | 10 | void doHandle(EventModel model); 11 | 12 | 13 | List getSupportEventTypes(); 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/EventModel.java: -------------------------------------------------------------------------------- 1 | package com.suny.async; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * Created by 孙建荣 on 17-9-7.下午4:15 8 | */ 9 | public class EventModel { 10 | 11 | private EventType type; 12 | private int actorId; 13 | private int entityType; 14 | private int entityId; 15 | private int entityOwnerId; 16 | 17 | 18 | private Map exts = new HashMap<>(); 19 | 20 | public EventModel() { 21 | } 22 | 23 | 24 | public EventModel setExt(String key, String value) { 25 | exts.put(key, value); 26 | return this; 27 | } 28 | 29 | 30 | public EventModel(EventType type) { 31 | this.type = type; 32 | } 33 | 34 | public String getExt(String key) { 35 | return exts.get(key); 36 | } 37 | 38 | 39 | public EventType getType() { 40 | return type; 41 | } 42 | 43 | 44 | public EventModel setType(EventType type) { 45 | this.type = type; 46 | return this; 47 | } 48 | 49 | 50 | public int getActorId() { 51 | return actorId; 52 | } 53 | 54 | 55 | public EventModel setActorId(int actorId) { 56 | this.actorId = actorId; 57 | return this; 58 | } 59 | 60 | public int getEntityType() { 61 | return entityType; 62 | } 63 | 64 | 65 | public EventModel setEntityType(int entityType) { 66 | this.entityType = entityType; 67 | return this; 68 | } 69 | 70 | 71 | public int getEntityId() { 72 | return entityId; 73 | } 74 | 75 | 76 | public EventModel setEntityId(int entityId) { 77 | this.entityId = entityId; 78 | return this; 79 | } 80 | 81 | 82 | public int getEntityOwnerId() { 83 | return entityOwnerId; 84 | } 85 | 86 | 87 | public EventModel setEntityOwnerId(int entityOwnerId) { 88 | this.entityOwnerId = entityOwnerId; 89 | return this; 90 | } 91 | 92 | public Map getExts() { 93 | return exts; 94 | } 95 | 96 | 97 | public EventModel setExts(Map exts) { 98 | this.exts = exts; 99 | return this; 100 | } 101 | 102 | 103 | } 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/EventProducer.java: -------------------------------------------------------------------------------- 1 | package com.suny.async; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.suny.utils.JedisAdapter; 5 | import com.suny.utils.RedisKeyUtil; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.stereotype.Service; 8 | 9 | /** 10 | * Created by 孙建荣 on 17-9-7.下午4:13 11 | */ 12 | @Service 13 | public class EventProducer { 14 | 15 | 16 | private final JedisAdapter jedisAdapter; 17 | 18 | 19 | @Autowired 20 | public EventProducer(JedisAdapter jedisAdapter) { 21 | this.jedisAdapter = jedisAdapter; 22 | } 23 | 24 | 25 | public boolean fireEvent(EventModel eventModel) { 26 | try { 27 | String json = JSONObject.toJSONString(eventModel); 28 | String key = RedisKeyUtil.getEventQueueKey(); 29 | jedisAdapter.lpush(key, json); 30 | return true; 31 | } catch (Exception e) { 32 | return false; 33 | } 34 | } 35 | } 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/EventType.java: -------------------------------------------------------------------------------- 1 | package com.suny.async; 2 | 3 | /** 4 | * Created by 孙建荣 on 17-9-7.下午4:16 5 | */ 6 | public enum EventType { 7 | 8 | LIKE(0), 9 | COMMENT(1), 10 | LOGIN(2), 11 | MAIL(3), 12 | FOLLOW(4), 13 | UNFOLLOW(5); 14 | 15 | private int value; 16 | 17 | 18 | EventType(int value) { 19 | this.value = value; 20 | } 21 | 22 | public int getValue() { 23 | return value; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/handler/FeedHandler.java: -------------------------------------------------------------------------------- 1 | package com.suny.async.handler; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import com.suny.async.EventHandler; 5 | import com.suny.async.EventModel; 6 | import com.suny.async.EventType; 7 | import com.suny.model.EntityType; 8 | import com.suny.model.Feed; 9 | import com.suny.model.Question; 10 | import com.suny.model.User; 11 | import com.suny.service.FeedService; 12 | import com.suny.service.FollowService; 13 | import com.suny.service.QuestionService; 14 | import com.suny.service.UserService; 15 | import com.suny.utils.JedisAdapter; 16 | import com.suny.utils.RedisKeyUtil; 17 | import org.springframework.beans.factory.annotation.Autowired; 18 | import org.springframework.stereotype.Component; 19 | 20 | import java.util.*; 21 | 22 | /** 23 | * Created by 孙建荣 on 17-9-11.上午7:57 24 | */ 25 | @Component 26 | public class FeedHandler implements EventHandler { 27 | 28 | private final FollowService followService; 29 | 30 | private final UserService userService; 31 | 32 | private final FeedService feedService; 33 | 34 | private final JedisAdapter jedisAdapter; 35 | 36 | private final QuestionService questionService; 37 | 38 | @Autowired 39 | public FeedHandler(FollowService followService, UserService userService, FeedService feedService, JedisAdapter jedisAdapter, QuestionService questionService) { 40 | this.followService = followService; 41 | this.userService = userService; 42 | this.feedService = feedService; 43 | this.jedisAdapter = jedisAdapter; 44 | this.questionService = questionService; 45 | } 46 | 47 | private String buildFeedData(EventModel model) { 48 | HashMap map = new HashMap<>(); 49 | // 触发用户都是通用的 50 | User actor = userService.getUser(model.getActorId()); 51 | if (actor == null) { 52 | return null; 53 | } 54 | map.put("userId", String.valueOf(model.getActorId())); 55 | map.put("userHead", actor.getHeadUrl()); 56 | map.put("userName", actor.getName()); 57 | 58 | if (model.getType() == EventType.COMMENT || 59 | (model.getType() == EventType.FOLLOW && model.getEntityType() == EntityType.ENTITY_QUESTION)) { 60 | Question question = questionService.getById(model.getEntityId()); 61 | if (question == null) { 62 | return null; 63 | } 64 | map.put("questionId", String.valueOf(question.getId())); 65 | map.put("questionTitle", question.getTitle()); 66 | return JSONObject.toJSONString(map); 67 | } 68 | 69 | 70 | return null; 71 | } 72 | 73 | @Override 74 | public void doHandle(EventModel model) { 75 | // 为了测试,把model的user的modelId随一下 76 | Random random = new Random(); 77 | model.setActorId(1 + random.nextInt(10)); 78 | 79 | // 构造一个新鲜事 80 | Feed feed = new Feed(); 81 | feed.setCreateDate(new Date()); 82 | feed.setType(model.getType().getValue()); 83 | feed.setUserId(model.getActorId()); 84 | feed.setData(buildFeedData(model)); 85 | if (feed.getData() == null) { 86 | // 不支持的feed 87 | return; 88 | } 89 | feedService.addFeed(feed); 90 | 91 | // 获取所有的粉丝 92 | List followers = followService.getFollowers(EntityType.ENTITY_USER, model.getActorId(), Integer.MAX_VALUE); 93 | 94 | //系统队列 95 | followers.add(0); 96 | 97 | // 给所有的粉丝推事件 98 | for (Integer follower : followers) { 99 | String tImelineKey = RedisKeyUtil.getTImeline(follower); 100 | jedisAdapter.lpush(tImelineKey, String.valueOf(feed.getId())); 101 | // 限制最长的长度,如果timeLineKey的长度过于大,就删除后面的额新鲜事 102 | 103 | } 104 | } 105 | 106 | @Override 107 | public List getSupportEventTypes() { 108 | return Arrays.asList(new EventType[]{EventType.COMMENT, EventType.FOLLOW}); 109 | } 110 | } 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/handler/LikeHandler.java: -------------------------------------------------------------------------------- 1 | package com.suny.async.handler; 2 | 3 | import com.suny.async.EventHandler; 4 | import com.suny.async.EventModel; 5 | import com.suny.async.EventType; 6 | import com.suny.model.Message; 7 | import com.suny.model.User; 8 | import com.suny.service.MessageService; 9 | import com.suny.service.UserService; 10 | import com.suny.utils.WendaUtil; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Arrays; 15 | import java.util.Date; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by 孙建荣 on 17-9-7.下午4:41 20 | */ 21 | @Component 22 | public class LikeHandler implements EventHandler { 23 | 24 | private final MessageService messageService; 25 | 26 | private final UserService userService; 27 | 28 | @Autowired 29 | public LikeHandler(MessageService messageService, UserService userService) { 30 | this.messageService = messageService; 31 | this.userService = userService; 32 | } 33 | 34 | @Override 35 | public void doHandle(EventModel model) { 36 | Message message = new Message(); 37 | message.setFromId(WendaUtil.SYSTEM_USERID); 38 | message.setToId(model.getEntityOwnerId()); 39 | message.setCreateDate(new Date()); 40 | User user = userService.getUser(model.getActorId()); 41 | message.setContent("用户" + user.getName() + "赞了你的评论,http://127.0.0.1:8080/question/" + model.getExt("questionId")); 42 | // 这里的会话ID肯定是系统管理员跟通知用户之间的会话 43 | message.setConversationId(WendaUtil.SYSTEM_USERID + "_" + model.getEntityOwnerId()); 44 | messageService.addMessage(message); 45 | } 46 | 47 | @Override 48 | public List getSupportEventTypes() { 49 | return Arrays.asList(EventType.LIKE); 50 | } 51 | } 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | -------------------------------------------------------------------------------- /src/main/java/com/suny/async/handler/LoginExceptionHandler.java: -------------------------------------------------------------------------------- 1 | package com.suny.async.handler; 2 | 3 | import com.suny.async.EventHandler; 4 | import com.suny.async.EventModel; 5 | import com.suny.async.EventType; 6 | import com.suny.utils.MailSender; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | 10 | import java.util.Arrays; 11 | import java.util.HashMap; 12 | import java.util.List; 13 | import java.util.Map; 14 | 15 | /** 16 | * Created by 孙建荣 on 17-9-8.上午9:31 17 | */ 18 | @Component 19 | public class LoginExceptionHandler implements EventHandler { 20 | 21 | private final MailSender mailSender; 22 | 23 | @Autowired 24 | public LoginExceptionHandler(MailSender mailSender) { 25 | this.mailSender = mailSender; 26 | } 27 | 28 | @Override 29 | public void doHandle(EventModel model) { 30 | // 判断用户是否登录异常 31 | Map map = new HashMap<>(); 32 | map.put("username", model.getExt("username")); 33 | mailSender.sendWithHTMLTemplate(model.getExt("email"), "登录ip异常", "mails/login_exception.html", map); 34 | } 35 | 36 | @Override 37 | public List getSupportEventTypes() { 38 | return Arrays.asList(EventType.LOGIN); 39 | } 40 | } 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/java/com/suny/configuration/WendaWebConfiguration.java: -------------------------------------------------------------------------------- 1 | package com.suny.configuration; 2 | 3 | import com.suny.interceptor.LoginRequiredInterceptor; 4 | import com.suny.interceptor.PassportInterceptor; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Component; 7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 9 | 10 | /** 11 | * 注册拦截器 12 | * Created by 孙建荣 on 17-9-2.下午9:12 13 | */ 14 | @Component 15 | public class WendaWebConfiguration extends WebMvcConfigurerAdapter { 16 | 17 | private final PassportInterceptor passportInterceptor; 18 | 19 | private final LoginRequiredInterceptor loginRequiredInterceptor; 20 | 21 | @Autowired 22 | public WendaWebConfiguration(LoginRequiredInterceptor loginRequiredInterceptor, PassportInterceptor passportInterceptor) { 23 | this.loginRequiredInterceptor = loginRequiredInterceptor; 24 | this.passportInterceptor = passportInterceptor; 25 | } 26 | 27 | @Override 28 | public void addInterceptors(InterceptorRegistry registry) { 29 | registry.addInterceptor(passportInterceptor); 30 | registry.addInterceptor(loginRequiredInterceptor).addPathPatterns("/user/**"); 31 | super.addInterceptors(registry); 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/CommentController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.model.Comment; 4 | import com.suny.model.EntityType; 5 | import com.suny.model.HostHolder; 6 | import com.suny.service.CommentService; 7 | import com.suny.service.QuestionService; 8 | import com.suny.service.SensitiveService; 9 | import com.suny.service.UserService; 10 | import org.slf4j.Logger; 11 | import org.slf4j.LoggerFactory; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Controller; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.util.HtmlUtils; 18 | 19 | import java.util.Date; 20 | 21 | /** 22 | * Created by 孙建荣 on 17-9-4.下午4:44 23 | */ 24 | @Controller 25 | public class CommentController { 26 | private static Logger logger = LoggerFactory.getLogger(CommentController.class); 27 | 28 | private final HostHolder hostHolder; 29 | private final UserService userService; 30 | private final CommentService commentService; 31 | private final QuestionService questionService; 32 | private final SensitiveService sensitiveService; 33 | 34 | @Autowired 35 | public CommentController(HostHolder hostHolder, UserService userService, CommentService commentService, QuestionService questionService, SensitiveService sensitiveService) { 36 | this.hostHolder = hostHolder; 37 | this.userService = userService; 38 | this.commentService = commentService; 39 | this.questionService = questionService; 40 | this.sensitiveService = sensitiveService; 41 | } 42 | 43 | @RequestMapping(path = {"/addComment"}, method = {RequestMethod.POST}) 44 | public String addComment(@RequestParam("questionId") int questionId, 45 | @RequestParam("content") String content) { 46 | try { 47 | content = HtmlUtils.htmlEscape(content); 48 | content = sensitiveService.filter(content); 49 | // 过滤content 50 | Comment comment = new Comment(); 51 | if (hostHolder.getUser() != null) { 52 | comment.setUserId(hostHolder.getUser().getId()); 53 | } else { 54 | // 在我的数据库里面10000用户为管理员,这里起到一个匿名用户的作用 55 | comment.setUserId(10000); 56 | } 57 | comment.setContent(content); 58 | comment.setEntityId(questionId); 59 | comment.setEntityType(EntityType.ENTITY_QUESTION); 60 | comment.setCreateDate(new Date()); 61 | comment.setStatus(0); 62 | 63 | commentService.addComment(comment); 64 | 65 | // 更新题目里面的评论数量 66 | int count = commentService.getCommentCount(comment.getEntityId(), comment.getEntityType()); 67 | questionService.updateCommentCount(comment.getEntityId(), count); 68 | //异步 69 | } catch (Exception e) { 70 | logger.error("增加评论失败" + e.getMessage()); 71 | } 72 | return "redirect:/question/" + String.valueOf(questionId); 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/FeedController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.dao.FeedDAO; 4 | import com.suny.model.EntityType; 5 | import com.suny.model.Feed; 6 | import com.suny.model.HostHolder; 7 | import com.suny.service.FeedService; 8 | import com.suny.service.FollowService; 9 | import com.suny.utils.JedisAdapter; 10 | import com.suny.utils.RedisKeyUtil; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | 19 | import java.util.ArrayList; 20 | import java.util.List; 21 | 22 | /** 23 | * Created by 孙建荣 on 17-9-10.下午10:21 24 | */ 25 | @Controller 26 | public class FeedController { 27 | private static Logger logger = LoggerFactory.getLogger(FeedController.class); 28 | 29 | private final FeedService feedService; 30 | 31 | private final FollowService followService; 32 | 33 | private final HostHolder hostHolder; 34 | 35 | private final JedisAdapter jedisAdapter; 36 | 37 | 38 | @Autowired 39 | public FeedController(FeedService feedService, FollowService followService, HostHolder hostHolder, JedisAdapter jedisAdapter) { 40 | this.feedService = feedService; 41 | this.followService = followService; 42 | this.hostHolder = hostHolder; 43 | this.jedisAdapter = jedisAdapter; 44 | } 45 | 46 | @RequestMapping(path = "/pushfeeds", method = {RequestMethod.POST, RequestMethod.GET}) 47 | private String getPushFeeds(Model model) { 48 | int localUserId = hostHolder.getUser() != null ? hostHolder.getUser().getId() : 0; 49 | List feedIds = jedisAdapter.lrange(RedisKeyUtil.getTImeline(localUserId), 0, 10); 50 | List feeds = new ArrayList<>(); 51 | for (String feedId : feedIds) { 52 | Feed feed = feedService.getById(Integer.parseInt(feedId)); 53 | if (feed != null) { 54 | feeds.add(feed); 55 | } 56 | } 57 | model.addAttribute("feeds", feeds); 58 | return "feeds"; 59 | } 60 | 61 | @RequestMapping(path = "/pullfeeds", method = {RequestMethod.POST, RequestMethod.GET}) 62 | private String getPullFeeds(Model model) { 63 | int localUserId = hostHolder.getUser() != null ? hostHolder.getUser().getId() : 0; 64 | List followees = new ArrayList<>(); 65 | if (localUserId != 0) { 66 | // 关注的人 67 | followService.getFollowers(localUserId, EntityType.ENTITY_USER, Integer.MAX_VALUE); 68 | } 69 | List feeds = feedService.getUserFeeds(Integer.MAX_VALUE, followees, 10); 70 | model.addAttribute("feeds", feeds); 71 | return "feeds"; 72 | } 73 | } 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/HomeController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.model.*; 4 | import com.suny.service.CommentService; 5 | import com.suny.service.FollowService; 6 | import com.suny.service.QuestionService; 7 | import com.suny.service.UserService; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.web.bind.annotation.PathVariable; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | 18 | import java.util.ArrayList; 19 | import java.util.List; 20 | 21 | /** 22 | * 控制项目的主页面相关类 23 | * Created by 孙建荣 on 17-9-1.上午10:21 24 | */ 25 | @Controller 26 | public class HomeController { 27 | 28 | private static final Logger logger = LoggerFactory.getLogger(HomeController.class); 29 | 30 | private final QuestionService questionService; 31 | 32 | private final UserService userService; 33 | 34 | private final FollowService followService; 35 | 36 | private final HostHolder hostHolder; 37 | 38 | private final CommentService commentService; 39 | 40 | @Autowired 41 | public HomeController(QuestionService questionService, UserService userService, FollowService followService, HostHolder hostHolder, CommentService commentService) { 42 | this.questionService = questionService; 43 | this.userService = userService; 44 | this.followService = followService; 45 | this.hostHolder = hostHolder; 46 | this.commentService = commentService; 47 | } 48 | 49 | /** 50 | * 私有的获取问题列表方法 51 | * 52 | * @param userId 用户的id 53 | * @param offset 从数据库的第几条开始查询 54 | * @param limit 限制查询几条数据 55 | * @return 查询出来的问题集合 56 | */ 57 | private List getQuestion(int userId, int offset, int limit) { 58 | List questionList = questionService.getLatestQuestion(userId, offset, limit); 59 | List objectList = new ArrayList<>(); 60 | for (Question question : questionList) { 61 | ViewObject viewObject = new ViewObject(); 62 | viewObject.set("question", question); 63 | viewObject.set("followCount", followService.getFollowerCount(EntityType.ENTITY_QUESTION, question.getId())); 64 | viewObject.set("user", userService.getUser(question.getUserId())); 65 | objectList.add(viewObject); 66 | } 67 | return objectList; 68 | } 69 | 70 | 71 | /** 72 | * 项目主页面,默认获取前10条问题列表 73 | * 74 | * @param model 存放数据模型 75 | * @param pop 如果为0则查询所有问题,否则就查询指定用户问题列表 76 | * @return 对应的主页面 77 | */ 78 | @RequestMapping(path = {"/", "/index"}, method = {RequestMethod.GET, RequestMethod.POST}) 79 | public String index(Model model, @RequestParam(value = "pop", defaultValue = "0") int pop) { 80 | model.addAttribute("vos", getQuestion(0, 0, 10)); 81 | return "index"; 82 | } 83 | 84 | /** 85 | * 根据用户的id查询用户的问题列表 86 | * 87 | * @param model 存放数据模型 88 | * @param userId 指定用户的id 89 | * @return 查询出来的结果页面 90 | */ 91 | @RequestMapping(path = {"/user/{userId}"}, method = {RequestMethod.GET, RequestMethod.POST}) 92 | public String userIndex(Model model, @PathVariable("userId") int userId) { 93 | model.addAttribute("vos", getQuestion(userId, 0, 10)); 94 | User user = userService.getUser(userId); 95 | ViewObject vo = new ViewObject(); 96 | vo.set("user", user); 97 | vo.set("commentCount", commentService.getUserCommentCount(userId)); 98 | vo.set("followerCount", followService.getFollowerCount(EntityType.ENTITY_USER, userId)); 99 | vo.set("followeeCount", followService.getFolloweeCount(userId, EntityType.ENTITY_USER)); 100 | if (hostHolder.getUser() != null) { 101 | vo.set("followed", followService.isFollower(hostHolder.getUser().getId(), EntityType.ENTITY_USER, userId)); 102 | } else { 103 | vo.set("followed", false); 104 | } 105 | model.addAttribute("profileUser", vo); 106 | return "profile"; 107 | } 108 | 109 | } 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/LikeController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.async.EventModel; 4 | import com.suny.async.EventProducer; 5 | import com.suny.async.EventType; 6 | import com.suny.model.Comment; 7 | import com.suny.model.EntityType; 8 | import com.suny.model.HostHolder; 9 | import com.suny.service.CommentService; 10 | import com.suny.service.LikeService; 11 | import com.suny.utils.WendaUtil; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.stereotype.Controller; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | import org.springframework.web.bind.annotation.ResponseBody; 18 | 19 | /** 20 | * Created by 孙建荣 on 17-9-6.上午8:22 21 | */ 22 | @Controller 23 | public class LikeController { 24 | 25 | 26 | private final LikeService likeService; 27 | 28 | private final HostHolder hostHolder; 29 | 30 | private final CommentService commentService; 31 | 32 | private final EventProducer eventProducer; 33 | 34 | 35 | @Autowired 36 | public LikeController(LikeService likeService, HostHolder hostHolder, CommentService commentService, EventProducer eventProducer) { 37 | this.likeService = likeService; 38 | this.hostHolder = hostHolder; 39 | this.commentService = commentService; 40 | this.eventProducer = eventProducer; 41 | } 42 | 43 | @RequestMapping(path = {"/like"}, method = {RequestMethod.POST}) 44 | @ResponseBody 45 | public String like(@RequestParam("commentId") int commentId) { 46 | if (hostHolder.getUser() == null) { 47 | return WendaUtil.getJSONString(999); 48 | } 49 | Comment comment = commentService.getCommentById(commentId); 50 | 51 | 52 | eventProducer.fireEvent(new EventModel(EventType.LIKE) 53 | .setActorId(hostHolder.getUser().getId()).setEntityId(commentId) 54 | .setEntityType(EntityType.ENTITY_COMMENT) 55 | .setEntityOwnerId(comment.getUserId()).setExt("questionId", String.valueOf(comment.getEntityId()))); 56 | long likeCount = likeService.like(hostHolder.getUser().getId(), EntityType.ENTITY_COMMENT, commentId); 57 | return WendaUtil.getJSONString(0, String.valueOf(likeCount)); 58 | } 59 | 60 | @RequestMapping(path = {"/dislike"}, method = {RequestMethod.POST}) 61 | @ResponseBody 62 | public String dislike(@RequestParam("commentId") int commentId) { 63 | if (hostHolder.getUser() == null) { 64 | return WendaUtil.getJSONString(999); 65 | } 66 | long likeCount = likeService.disLike(hostHolder.getUser().getId(), EntityType.ENTITY_COMMENT, commentId); 67 | return WendaUtil.getJSONString(0, String.valueOf(likeCount)); 68 | } 69 | 70 | 71 | } 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/LoginController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.async.EventModel; 4 | import com.suny.async.EventProducer; 5 | import com.suny.async.EventType; 6 | import com.suny.service.UserService; 7 | import org.apache.commons.lang.StringUtils; 8 | import org.slf4j.Logger; 9 | import org.slf4j.LoggerFactory; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Controller; 12 | import org.springframework.ui.Model; 13 | import org.springframework.web.bind.annotation.CookieValue; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | import org.springframework.web.bind.annotation.RequestParam; 17 | 18 | import javax.servlet.http.Cookie; 19 | import javax.servlet.http.HttpServletResponse; 20 | import java.util.Map; 21 | 22 | /** 23 | * Created by 孙建荣 on 17-9-2.下午6:46 24 | */ 25 | @Controller 26 | public class LoginController { 27 | private static Logger logger = LoggerFactory.getLogger(LoginController.class); 28 | 29 | private final UserService userService; 30 | 31 | private final EventProducer eventProducer; 32 | 33 | @Autowired 34 | public LoginController(UserService userService, EventProducer eventProducer) { 35 | this.userService = userService; 36 | this.eventProducer = eventProducer; 37 | } 38 | 39 | 40 | @SuppressWarnings("Duplicates") 41 | @RequestMapping(path = {"/reg"}, method = {RequestMethod.POST}) 42 | public String reg(Model model, @RequestParam("username") String username, 43 | @RequestParam("password") String password, 44 | @RequestParam(value = "next", required = false) String next, 45 | @RequestParam(value = "rememberme", defaultValue = "false") boolean remeberme, 46 | HttpServletResponse response) { 47 | try { 48 | Map map = userService.register(username, password); 49 | if (map.containsKey("ticket")) { 50 | Cookie cookie = new Cookie("ticket", map.get("ticket").toString()); 51 | cookie.setPath("/"); 52 | if (remeberme) { 53 | cookie.setMaxAge(3600 * 24 * 5); 54 | } 55 | response.addCookie(cookie); 56 | if (StringUtils.isNotBlank(next)) { 57 | return "redirect:" + next; 58 | } 59 | return "redirect:/"; 60 | } else { 61 | model.addAttribute("msg", map.get("msg")); 62 | return "login"; 63 | } 64 | } catch (Exception e) { 65 | logger.error("注册异常" + e.getMessage()); 66 | model.addAttribute("msg", "服务器错误"); 67 | return "login"; 68 | } 69 | } 70 | 71 | @RequestMapping(path = {"/reglogin"}, method = {RequestMethod.GET}) 72 | public String regloginPage(Model model, @RequestParam(value = "next", required = false) String next) { 73 | model.addAttribute("next", next); 74 | return "login"; 75 | } 76 | 77 | @SuppressWarnings("Duplicates") 78 | @RequestMapping(path = {"/login/"}, method = {RequestMethod.POST}) 79 | public String login(Model model, @RequestParam("username") String username, 80 | @RequestParam("password") String password, 81 | @RequestParam(value = "next", required = false) String next, 82 | @RequestParam(value = "rememberme", defaultValue = "false") boolean remeberme, 83 | HttpServletResponse response) { 84 | try { 85 | Map map = userService.login(username, password); 86 | if (map.containsKey("ticket")) { 87 | Cookie cookie = new Cookie("ticket", map.get("ticket").toString()); 88 | cookie.setPath("/"); 89 | if (remeberme) { 90 | cookie.setMaxAge(3600 * 24 * 5); 91 | } 92 | response.addCookie(cookie); 93 | 94 | logger.info("开始发送登录操作邮件"); 95 | // 发送邮件事件,这里由于个别原因暂时注释,首先这里的邮件地址是需要根据用户的邮箱来进行发送的,这里固定了所以不合适 96 | 97 | /* eventProducer.fireEvent(new EventModel(EventType.LOGIN) 98 | .setExt("username", username) 99 | .setExt("email", "demo@vip.qq.com") 100 | .setActorId((Integer) map.get("userId")));*/ 101 | 102 | if (StringUtils.isNotBlank(next)) { 103 | return "redirect:" + next; 104 | } 105 | return "redirect:/"; 106 | } else { 107 | model.addAttribute("msg", map.get("msg")); 108 | return "login"; 109 | } 110 | } catch (Exception e) { 111 | logger.error("登录异常" + e.getMessage()); 112 | return "login"; 113 | } 114 | } 115 | 116 | 117 | @RequestMapping(path = {"/logout"}, method = {RequestMethod.GET, RequestMethod.POST}) 118 | public String logout(@CookieValue("ticket") String ticket) { 119 | userService.logout(ticket); 120 | return "redirect:/"; 121 | } 122 | 123 | } 124 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/QuestionController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.model.*; 4 | import com.suny.service.*; 5 | import com.suny.utils.WendaUtil; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.slf4j.Logger; 8 | import org.slf4j.LoggerFactory; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Controller; 11 | import org.springframework.ui.Model; 12 | import org.springframework.web.bind.annotation.*; 13 | 14 | import java.util.ArrayList; 15 | import java.util.Date; 16 | import java.util.List; 17 | 18 | /** 19 | * Created by 孙建荣 on 17-9-3.下午6:22 20 | */ 21 | @Controller 22 | public class QuestionController { 23 | 24 | private static Logger logger = LoggerFactory.getLogger(QuestionController.class); 25 | 26 | private final QuestionService questionService; 27 | private final HostHolder hostHolder; 28 | private final UserService userService; 29 | 30 | private final CommentService commentService; 31 | 32 | private final FollowService followService; 33 | 34 | 35 | private final LikeService likeService; 36 | 37 | @Autowired 38 | public QuestionController(QuestionService questionService, HostHolder hostHolder, UserService userService, CommentService commentService, LikeService likeService, FollowService followService) { 39 | this.questionService = questionService; 40 | this.hostHolder = hostHolder; 41 | this.userService = userService; 42 | this.commentService = commentService; 43 | this.likeService = likeService; 44 | this.followService = followService; 45 | } 46 | 47 | 48 | @RequestMapping(value = "/question/{qid}", method = {RequestMethod.GET}) 49 | public String questionDetail(Model model, @PathVariable("qid") int qid) { 50 | 51 | Question question = questionService.getById(qid); 52 | model.addAttribute("question", question); 53 | List commentList = commentService.getCommentsByEntity(qid, EntityType.ENTITY_QUESTION); 54 | ArrayList vos = new ArrayList<>(); 55 | for (Comment comment : commentList) { 56 | ViewObject vo = new ViewObject(); 57 | vo.set("comment", comment); 58 | if (hostHolder.getUser() == null) { 59 | vo.set("liked", 0); 60 | } else { 61 | vo.set("liked", likeService.getLikeStatus(hostHolder.getUser().getId(), EntityType.ENTITY_COMMENT, comment.getId())); 62 | } 63 | vo.set("likeCount", likeService.getLikeCount(EntityType.ENTITY_COMMENT, comment.getId())); 64 | vo.set("user", userService.getUser(comment.getUserId())); 65 | vos.add(vo); 66 | } 67 | model.addAttribute("comments", vos); 68 | 69 | ArrayList followUsers = new ArrayList<>(); 70 | // 获取关注用户信息 71 | List users = followService.getFollowers(EntityType.ENTITY_QUESTION, qid, 20); 72 | for (Integer userId : users) { 73 | ViewObject vo = new ViewObject(); 74 | User user = userService.getUser(userId); 75 | if (user == null) { 76 | continue; 77 | } 78 | vo.set("name", user.getName()); 79 | vo.set("headUrl", user.getHeadUrl()); 80 | vo.set("id", user.getId()); 81 | followUsers.add(vo); 82 | } 83 | model.addAttribute("followUsers", followUsers); 84 | if (hostHolder.getUser() != null) { 85 | model.addAttribute("followed", followService.isFollower(hostHolder.getUser().getId(), EntityType.ENTITY_QUESTION, qid)); 86 | } else { 87 | model.addAttribute("followed", false); 88 | } 89 | return "detail"; 90 | } 91 | 92 | @RequestMapping(value = "/question/add", method = {RequestMethod.POST}) 93 | @ResponseBody 94 | public String addQuestion(@RequestParam("title") String title, 95 | @RequestParam("content") String content) { 96 | try { 97 | Question question = new Question(); 98 | question.setContent(content); 99 | question.setCreateDate(new Date()); 100 | question.setTitle(title); 101 | if (hostHolder.getUser() == null) { 102 | // 这里可以设置一个匿名用户,这里我使用10000,这是我的admin用户 103 | question.setUserId(10000); 104 | } else { 105 | question.setUserId(hostHolder.getUser().getId()); 106 | } 107 | if (questionService.addQuestion(question) > 0) { 108 | return WendaUtil.getJSONString(0); 109 | } 110 | } catch (Exception e) { 111 | logger.error("增加题目失败" + e.getMessage()); 112 | } 113 | return WendaUtil.getJSONString(1, "失败"); 114 | } 115 | } 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/SearchController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.model.EntityType; 4 | import com.suny.model.Question; 5 | import com.suny.model.ViewObject; 6 | import com.suny.service.FollowService; 7 | import com.suny.service.QuestionService; 8 | import com.suny.service.SearchService; 9 | import com.suny.service.UserService; 10 | import org.apache.solr.client.solrj.SolrServerException; 11 | import org.slf4j.Logger; 12 | import org.slf4j.LoggerFactory; 13 | import org.springframework.beans.factory.annotation.Autowired; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.ui.Model; 16 | import org.springframework.web.bind.annotation.RequestMapping; 17 | import org.springframework.web.bind.annotation.RequestMethod; 18 | import org.springframework.web.bind.annotation.RequestParam; 19 | 20 | import java.io.IOException; 21 | import java.util.ArrayList; 22 | import java.util.List; 23 | 24 | /** 25 | * Created by 孙建荣 on 17-9-13.下午10:52 26 | */ 27 | @Controller 28 | public class SearchController { 29 | 30 | private static Logger logger = LoggerFactory.getLogger(SearchController.class); 31 | private final SearchService searchService; 32 | private final FollowService followService; 33 | private final UserService userService; 34 | private final QuestionService questionService; 35 | 36 | 37 | @Autowired 38 | public SearchController(SearchService searchService, FollowService followService, UserService userService, QuestionService questionService) { 39 | this.searchService = searchService; 40 | this.followService = followService; 41 | this.userService = userService; 42 | this.questionService = questionService; 43 | } 44 | 45 | @RequestMapping(path = {"/search"}, method = {RequestMethod.POST, RequestMethod.GET}) 46 | public String search(Model model, @RequestParam("q") String keyword, 47 | @RequestParam(value = "offset", defaultValue = "0") int offset, 48 | @RequestParam(value = "count", defaultValue = "10") int count) { 49 | try { 50 | List questionList = searchService.searchQuestion(keyword, offset, count, "", ""); 51 | 52 | List vos = new ArrayList<>(); 53 | for (Question question : questionList) { 54 | Question q = questionService.getById(question.getId()); 55 | ViewObject vo = new ViewObject(); 56 | if (question.getContent() != null) { 57 | q.setContent(question.getContent()); 58 | } 59 | if (question.getTitle() != null) { 60 | q.setTitle(question.getTitle()); 61 | } 62 | vo.set("question", vos); 63 | vo.set("followCount", followService.getFollowerCount(EntityType.ENTITY_QUESTION, question.getId())); 64 | vo.set("user", userService.getUser(q.getUserId())); 65 | vos.add(vo); 66 | } 67 | model.addAttribute("vos", vos); 68 | model.addAttribute("keyword", keyword); 69 | } catch (IOException e) { 70 | logger.error("传输流出错", e.getMessage()); 71 | } catch (SolrServerException e) { 72 | logger.error("solr服务失败", e.getMessage()); 73 | } 74 | return "result"; 75 | } 76 | 77 | 78 | } 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | -------------------------------------------------------------------------------- /src/main/java/com/suny/controller/SettingController.java: -------------------------------------------------------------------------------- 1 | package com.suny.controller; 2 | 3 | import com.suny.service.WendaService; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.RequestMapping; 7 | import org.springframework.web.bind.annotation.RequestMethod; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | 10 | import javax.servlet.http.HttpSession; 11 | 12 | /** 13 | * Created by 孙建荣 on 17-9-1.下午10:44 14 | */ 15 | @Controller 16 | public class SettingController { 17 | 18 | private final WendaService wendaService; 19 | 20 | @Autowired 21 | public SettingController(WendaService wendaService) { 22 | this.wendaService = wendaService; 23 | } 24 | 25 | @RequestMapping(path = {"/setting"}, method = {RequestMethod.GET}) 26 | @ResponseBody 27 | public String setting(HttpSession httpSession) { 28 | return "Setting OK ." + wendaService.getMessage(1); 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/CommentDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.Comment; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by 孙建荣 on 17-9-4.上午8:34 10 | */ 11 | @Mapper 12 | public interface CommentDAO { 13 | 14 | String TABLE_NAME = " comment "; 15 | String INSERT_FIELDS = " user_id, content, create_date, entity_id, entity_type, status"; 16 | String SELECT_FIELDS = " id, " + INSERT_FIELDS; 17 | 18 | @Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS, 19 | ") values (#{userId},#{content},#{createDate},#{entityId},#{entityType},#{status})"}) 20 | int addComment(Comment comment); 21 | 22 | @Update({"update ", TABLE_NAME, " set status=#{status} where entity_id=#{entityId} and entity_type=#{entityType}"}) 23 | void updateStatus(@Param("entityId") int entityId, @Param("entityType") int entityType, @Param("status") int status); 24 | 25 | @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, 26 | " where entity_id=#{entityId} and entity_type=#{entityType} order by id desc"}) 27 | List selectByEntity(@Param("entityId") int entityId, @Param("entityType") int entityType); 28 | 29 | @Select({"select count(id) from ", TABLE_NAME, " where entity_id=#{entityId} and entity_type=#{entityType} "}) 30 | int getCommentCount(@Param("entityId") int entityId, @Param("entityType") int entityType); 31 | 32 | @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where id=#{id}"}) 33 | Comment getCommentById(int id); 34 | 35 | @Select({"select count(*) from ", TABLE_NAME, " where user_id=#{userId}"}) 36 | int getUserCommentCount(int userId); 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/FeedDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.Feed; 4 | import org.apache.ibatis.annotations.Insert; 5 | import org.apache.ibatis.annotations.Mapper; 6 | import org.apache.ibatis.annotations.Param; 7 | import org.apache.ibatis.annotations.Select; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by 孙建荣 on 17-9-10.下午9:53 13 | */ 14 | @Mapper 15 | public interface FeedDAO { 16 | 17 | String TABLE_NAME = " feed "; 18 | String INSERT_FIELDS = " user_id, data, create_date, type "; 19 | String SELECT_FIELDS = " id, " + INSERT_FIELDS; 20 | 21 | @Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS, 22 | ") values (#{userId},#{data},#{createDate},#{type})"}) 23 | int addFeed(Feed feed); 24 | 25 | @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where id=#{id}"}) 26 | Feed getFeedById(int id); 27 | 28 | 29 | List selectUserFeeds(@Param("maxId") int maxId, 30 | @Param("userIds") List userIds, 31 | @Param("count") int count); 32 | 33 | 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/LoginTicketDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.LoginTicket; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | /** 7 | * Created by 孙建荣 on 17-9-1.上午10:43 8 | */ 9 | @Mapper 10 | public interface LoginTicketDAO { 11 | 12 | String TABLE_NAME = "login_ticket"; 13 | String INSERT_FIELDS = " user_id, expired, status, ticket"; 14 | String SELECT_FIELDS = "id " + INSERT_FIELDS; 15 | 16 | 17 | @Insert({"insert into " + TABLE_NAME + "(" + INSERT_FIELDS + ") values(#{userId},#{expired},#{status},#{ticket})"}) 18 | int addTicket(LoginTicket loginTicket); 19 | 20 | @Insert({"delete from " + TABLE_NAME + " where id=#{id}"}) 21 | int deleteTicket(int id); 22 | 23 | 24 | // @Select({"select", SELECT_FIELDS + " from " + TABLE_NAME + " where ticket=#{ticket}"}) 25 | LoginTicket selectByTicket(String ticket); 26 | 27 | // @Select({"select", SELECT_FIELDS + " from " + TABLE_NAME + " where user_id=#{userId}"}) 28 | LoginTicket selectByUserId(int userId); 29 | 30 | 31 | @Update({"update", TABLE_NAME, "set status=#{status} where ticket=#{ticket}"}) 32 | void updateStatus(@Param("ticket") String ticket, @Param("status") int status); 33 | 34 | } 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/MessageDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.Message; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.util.List; 7 | 8 | import static com.suny.dao.MessageDAO.INSERT_FIELDS; 9 | import static com.suny.dao.MessageDAO.SELECT_FIELDS; 10 | import static com.suny.dao.MessageDAO.TABLE_NAME; 11 | 12 | /** 13 | * Created by 孙建荣 on 17-9-4.下午5:21 14 | */ 15 | @Mapper 16 | public interface MessageDAO { 17 | String TABLE_NAME = " message "; 18 | String INSERT_FIELDS = " from_id, to_id, content, has_read, conversation_id, create_date "; 19 | String SELECT_FIELDS = " id, " + INSERT_FIELDS; 20 | String HAS_READER_TRUE = "1"; 21 | 22 | @Update({"update ", TABLE_NAME, " set has_read=", HAS_READER_TRUE, " where conversation_id=#{conversationId}"}) 23 | void updateMessagesReadStatus(String conversationId); 24 | 25 | @Insert({"insert into ", TABLE_NAME, "(", INSERT_FIELDS, 26 | ") values (#{fromId},#{toId},#{content},#{hasRead},#{conversationId},#{createDate})"}) 27 | int addMessage(Message message); 28 | 29 | @Select({"select ", SELECT_FIELDS, " from ", TABLE_NAME, " where conversation_id=#{conversationId} order by id desc limit #{offset}, #{limit}"}) 30 | List getConversationDetail(@Param("conversationId") String conversationId, 31 | @Param("offset") int offset, 32 | @Param("limit") int limit); 33 | 34 | 35 | @Select({"select count(id) from ", TABLE_NAME, " where has_read=0 and to_id=#{userId} and conversation_id=#{conversationId} "}) 36 | int getConversationUnreadCount(@Param("userId") int userId, @Param("conversationId") String conversationId); 37 | 38 | 39 | // group by conversation_id 40 | 41 | @Select({"select ", INSERT_FIELDS, " , count(id) as id from ( select * from ", TABLE_NAME, " where from_id=#{userId} or to_id=#{userId} order by create_date desc) tt GROUP BY conversation_id order by create_date desc limit #{offset}, #{limit}"}) 42 | List getConversationList(@Param("userId") int userId, 43 | @Param("offset") int offset, 44 | @Param("limit") int limit); 45 | 46 | } 47 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/QuestionDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.Question; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | import java.util.List; 7 | 8 | /** 9 | * Created by 孙建荣 on 17-8-31.上午11:26 10 | */ 11 | @Mapper 12 | public interface QuestionDAO { 13 | 14 | String TABLE_NAME = "question"; 15 | String INSERT_FIELDS = "title, content, user_id, create_date, comment_count"; 16 | String SELECT_FIELDS = "id," + INSERT_FIELDS; 17 | 18 | /** 19 | * 添加问题 20 | * 21 | * @param question 问题的详情 22 | * @return 插入成功的条数 23 | */ 24 | @Insert({"insert into", TABLE_NAME, "(", INSERT_FIELDS, ") values (#{title}, #{content}, #{userId}, #{createDate}, #{commentCount})"}) 25 | int addQuestion(Question question); 26 | 27 | List selectLatestQuestions(@Param("userId") int userId, @Param("offset") int offset, @Param("limit") int limit); 28 | 29 | @Select({"select " + SELECT_FIELDS + " from " + TABLE_NAME + " where id=#{id} "}) 30 | Question getById(int id); 31 | 32 | 33 | @Update({"update ", TABLE_NAME, " set comment_count =#{commentCount} where id=#{id}"}) 34 | int updateCommentCount(@Param("id") int id, @Param("commentCount") int commentCount); 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/com/suny/dao/UserDAO.java: -------------------------------------------------------------------------------- 1 | package com.suny.dao; 2 | 3 | import com.suny.model.User; 4 | import org.apache.ibatis.annotations.*; 5 | 6 | /** 7 | * Created by 孙建荣 on 17-9-1.上午10:27 8 | */ 9 | @Mapper 10 | public interface UserDAO { 11 | 12 | String TABLE_NAME = "user"; 13 | String INSERT_FIELDS = "name, password, salt, head_url"; 14 | String SELECT_FIELDS = "id , name, password, salt, head_url"; 15 | 16 | 17 | @Insert({"insert into", TABLE_NAME, "(", INSERT_FIELDS, ") values(#{name}, #{password}, #{salt}, #{headUrl})"}) 18 | int addUser(User user); 19 | 20 | @Select({"select ", SELECT_FIELDS, "from", TABLE_NAME, "where id= #{id}"}) 21 | User selectById(int id); 22 | 23 | @Select({"select ", SELECT_FIELDS, "from", TABLE_NAME, "where name= #{name}"}) 24 | User selectByName(String name); 25 | 26 | @Update({"update", TABLE_NAME, "set password=#{password} where id=#{id}"}) 27 | void updatePassword(User user); 28 | 29 | @Delete({"delete from ", TABLE_NAME, "where id =#{id}"}) 30 | void deleteById(int id); 31 | } 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/java/com/suny/interceptor/LoginRequiredInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.suny.interceptor; 2 | 3 | import com.suny.model.HostHolder; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.stereotype.Component; 6 | import org.springframework.web.servlet.HandlerInterceptor; 7 | import org.springframework.web.servlet.ModelAndView; 8 | 9 | import javax.servlet.http.HttpServletRequest; 10 | import javax.servlet.http.HttpServletResponse; 11 | 12 | /** 13 | * Created by 孙建荣 on 17-9-2.下午9:06 14 | */ 15 | @Component 16 | public class LoginRequiredInterceptor implements HandlerInterceptor { 17 | 18 | private final HostHolder hostHolder; 19 | 20 | @Autowired 21 | public LoginRequiredInterceptor(HostHolder hostHolder) { 22 | this.hostHolder = hostHolder; 23 | } 24 | 25 | 26 | @Override 27 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 28 | if (hostHolder.getUser() == null) { 29 | response.sendRedirect("/reglogin?next=" + request.getRequestURI()); 30 | } 31 | return true; 32 | } 33 | 34 | 35 | @Override 36 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 37 | 38 | } 39 | 40 | 41 | @Override 42 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 43 | 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /src/main/java/com/suny/interceptor/PassportInterceptor.java: -------------------------------------------------------------------------------- 1 | package com.suny.interceptor; 2 | 3 | import com.suny.dao.LoginTicketDAO; 4 | import com.suny.dao.UserDAO; 5 | import com.suny.model.HostHolder; 6 | import com.suny.model.LoginTicket; 7 | import com.suny.model.User; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Component; 10 | import org.springframework.web.servlet.AsyncHandlerInterceptor; 11 | import org.springframework.web.servlet.HandlerInterceptor; 12 | import org.springframework.web.servlet.ModelAndView; 13 | 14 | import javax.servlet.http.Cookie; 15 | import javax.servlet.http.HttpServletRequest; 16 | import javax.servlet.http.HttpServletResponse; 17 | import java.util.Date; 18 | 19 | /** 20 | * Created by 孙建荣 on 17-9-2.下午10:12 21 | */ 22 | @Component 23 | public class PassportInterceptor implements HandlerInterceptor { 24 | 25 | @Autowired 26 | private LoginTicketDAO loginTicketDAO; 27 | @Autowired 28 | private UserDAO userDAO; 29 | @Autowired 30 | private HostHolder hostHolder; 31 | 32 | @Override 33 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 34 | String ticket = null; 35 | if (request.getCookies() != null) { 36 | for (Cookie cookie : request.getCookies()) { 37 | if (cookie.getName().equals("ticket")) { 38 | ticket = cookie.getValue(); 39 | break; 40 | } 41 | } 42 | } 43 | if (ticket != null) { 44 | LoginTicket loginTicket = loginTicketDAO.selectByTicket(ticket); 45 | if (loginTicket == null || loginTicket.getExpired().before(new Date()) || loginTicket.getStatus() != 1) { 46 | return true; 47 | } 48 | User user = userDAO.selectById(loginTicket.getUserId()); 49 | hostHolder.setUser(user); 50 | } 51 | return true; 52 | } 53 | 54 | 55 | @Override 56 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 57 | if (modelAndView != null && hostHolder.getUser() != null) { 58 | modelAndView.addObject("user", hostHolder.getUser()); 59 | } 60 | } 61 | 62 | 63 | @Override 64 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 65 | hostHolder.clear(); 66 | } 67 | } 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/Comment.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-4.上午8:37 7 | */ 8 | public class Comment { 9 | 10 | private int id; 11 | private int userId; 12 | private int entityId; 13 | private int entityType; 14 | private String content; 15 | private Date createDate; 16 | // 在数据库里面默认0就是有效的,1是被逻辑删除的 17 | private int status; 18 | 19 | public Comment() { 20 | } 21 | 22 | public Comment(int id, int userId, int entityId, int entityType, String content, Date createDate, int status) { 23 | this.id = id; 24 | this.userId = userId; 25 | this.entityId = entityId; 26 | this.entityType = entityType; 27 | this.content = content; 28 | this.createDate = createDate; 29 | this.status = status; 30 | } 31 | 32 | public int getId() { 33 | return id; 34 | } 35 | 36 | public void setId(int id) { 37 | this.id = id; 38 | } 39 | 40 | public int getUserId() { 41 | return userId; 42 | } 43 | 44 | public void setUserId(int userId) { 45 | this.userId = userId; 46 | } 47 | 48 | public int getEntityId() { 49 | return entityId; 50 | } 51 | 52 | public void setEntityId(int entityId) { 53 | this.entityId = entityId; 54 | } 55 | 56 | public int getEntityType() { 57 | return entityType; 58 | } 59 | 60 | public void setEntityType(int entityType) { 61 | this.entityType = entityType; 62 | } 63 | 64 | public String getContent() { 65 | return content; 66 | } 67 | 68 | public void setContent(String content) { 69 | this.content = content; 70 | } 71 | 72 | public Date getCreateDate() { 73 | return createDate; 74 | } 75 | 76 | public void setCreateDate(Date createDate) { 77 | this.createDate = createDate; 78 | } 79 | 80 | public int getStatus() { 81 | return status; 82 | } 83 | 84 | public void setStatus(int status) { 85 | this.status = status; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/EntityType.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | /** 4 | * Created by 孙建荣 on 17-9-4.上午8:47 5 | */ 6 | public class EntityType { 7 | 8 | public static int ENTITY_QUESTION = 1; 9 | public static int ENTITY_COMMENT = 2; 10 | public static int ENTITY_USER = 3; 11 | } 12 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/Feed.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | import java.util.Date; 6 | 7 | /** 8 | * Created by 孙建荣 on 17-9-10.下午9:56 9 | */ 10 | public class Feed { 11 | private int id; 12 | private int type; 13 | private int userId; 14 | private Date createDate; 15 | private String data; 16 | private JSONObject dataJSON = null; 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public void setId(int id) { 23 | this.id = id; 24 | } 25 | 26 | public int getType() { 27 | return type; 28 | } 29 | 30 | public void setType(int type) { 31 | this.type = type; 32 | } 33 | 34 | public int getUserId() { 35 | return userId; 36 | } 37 | 38 | public void setUserId(int userId) { 39 | this.userId = userId; 40 | } 41 | 42 | public Date getCreateDate() { 43 | return createDate; 44 | } 45 | 46 | public void setCreateDate(Date createDate) { 47 | this.createDate = createDate; 48 | } 49 | 50 | public String getData() { 51 | return data; 52 | } 53 | 54 | public void setData(String data) { 55 | this.data = data; 56 | } 57 | 58 | public JSONObject getDataJSON() { 59 | return dataJSON; 60 | } 61 | 62 | public void setDataJSON(JSONObject dataJSON) { 63 | this.dataJSON = dataJSON; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/HostHolder.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import org.springframework.stereotype.Component; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-1.下午10:38 7 | */ 8 | @Component 9 | public class HostHolder { 10 | 11 | private static ThreadLocal users = new ThreadLocal<>(); 12 | 13 | public User getUser() { 14 | return users.get(); 15 | } 16 | 17 | public void setUser(User user) { 18 | users.set(user); 19 | } 20 | 21 | public void clear() { 22 | users.remove(); 23 | } 24 | 25 | 26 | } 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/LoginTicket.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-1.上午10:39 7 | */ 8 | public class LoginTicket { 9 | 10 | private int id; 11 | private int userId; 12 | private Date expired; 13 | // 0有效,1无效 14 | private int status; 15 | 16 | private String ticket; 17 | 18 | 19 | public int getId() { 20 | return id; 21 | } 22 | 23 | public void setId(int id) { 24 | this.id = id; 25 | } 26 | 27 | public int getUserId() { 28 | return userId; 29 | } 30 | 31 | public void setUserId(int userId) { 32 | this.userId = userId; 33 | } 34 | 35 | public Date getExpired() { 36 | return expired; 37 | } 38 | 39 | public void setExpired(Date expired) { 40 | this.expired = expired; 41 | } 42 | 43 | public int getStatus() { 44 | return status; 45 | } 46 | 47 | public void setStatus(int status) { 48 | this.status = status; 49 | } 50 | 51 | public String getTicket() { 52 | return ticket; 53 | } 54 | 55 | public void setTicket(String ticket) { 56 | this.ticket = ticket; 57 | } 58 | } 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/Message.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-4.下午5:23 7 | */ 8 | public class Message { 9 | 10 | private int id; 11 | private int fromId; 12 | private int toId; 13 | private String content; 14 | private Date createDate; 15 | // 0为没有阅读,1为已经阅读过了的 16 | private int hasRead; 17 | private String conversationId; 18 | 19 | public Message() { 20 | } 21 | 22 | public Message(int id, int fromId, int toId, String content, Date createDate, int hasRead, String conversationId) { 23 | this.id = id; 24 | this.fromId = fromId; 25 | this.toId = toId; 26 | this.content = content; 27 | this.createDate = createDate; 28 | this.hasRead = hasRead; 29 | this.conversationId = conversationId; 30 | } 31 | 32 | public int getId() { 33 | return id; 34 | } 35 | 36 | public void setId(int id) { 37 | this.id = id; 38 | } 39 | 40 | public int getFromId() { 41 | return fromId; 42 | } 43 | 44 | public void setFromId(int fromId) { 45 | this.fromId = fromId; 46 | } 47 | 48 | public int getToId() { 49 | return toId; 50 | } 51 | 52 | public void setToId(int toId) { 53 | this.toId = toId; 54 | } 55 | 56 | public String getContent() { 57 | return content; 58 | } 59 | 60 | public void setContent(String content) { 61 | this.content = content; 62 | } 63 | 64 | public Date getCreateDate() { 65 | return createDate; 66 | } 67 | 68 | public void setCreateDate(Date createDate) { 69 | this.createDate = createDate; 70 | } 71 | 72 | public int getHasRead() { 73 | return hasRead; 74 | } 75 | 76 | public void setHasRead(int hasRead) { 77 | this.hasRead = hasRead; 78 | } 79 | 80 | public String getConversationId() { 81 | return conversationId; 82 | } 83 | 84 | public void setConversationId(String conversationId) { 85 | this.conversationId = conversationId; 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/Question.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import java.util.Date; 4 | 5 | /** 6 | * 问题详情 7 | * Created by 孙建荣 on 17-8-31.下午12:45 8 | */ 9 | public class Question { 10 | 11 | private int id; 12 | private String title; 13 | private String content; 14 | private Date createDate; 15 | private int userId; 16 | private int commentCount; 17 | 18 | public Question() { 19 | } 20 | 21 | public int getId() { 22 | return id; 23 | } 24 | 25 | public void setId(int id) { 26 | this.id = id; 27 | } 28 | 29 | public String getTitle() { 30 | return title; 31 | } 32 | 33 | public void setTitle(String title) { 34 | this.title = title; 35 | } 36 | 37 | public String getContent() { 38 | return content; 39 | } 40 | 41 | public void setContent(String content) { 42 | this.content = content; 43 | } 44 | 45 | public Date getCreateDate() { 46 | return createDate; 47 | } 48 | 49 | public void setCreateDate(Date createDate) { 50 | this.createDate = createDate; 51 | } 52 | 53 | public int getUserId() { 54 | return userId; 55 | } 56 | 57 | public void setUserId(int userId) { 58 | this.userId = userId; 59 | } 60 | 61 | public int getCommentCount() { 62 | return commentCount; 63 | } 64 | 65 | public void setCommentCount(int commentCount) { 66 | this.commentCount = commentCount; 67 | } 68 | 69 | @Override 70 | public String toString() { 71 | return "Question{" + 72 | "id=" + id + 73 | ", title='" + title + '\'' + 74 | ", content='" + content + '\'' + 75 | ", createDate=" + createDate + 76 | ", userId=" + userId + 77 | ", commentCount=" + commentCount + 78 | '}'; 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/User.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | /** 4 | * 用户信息实体类 5 | * Created by 孙建荣 on 17-8-31.下午12:42 6 | */ 7 | public class User { 8 | private int id; 9 | private String name; 10 | private String password; 11 | private String salt; 12 | private String headUrl; 13 | 14 | public User() { 15 | } 16 | 17 | public User(String name) { 18 | this.name = name; 19 | this.salt = ""; 20 | this.headUrl = ""; 21 | } 22 | 23 | public int getId() { 24 | return id; 25 | } 26 | 27 | public void setId(int id) { 28 | this.id = id; 29 | } 30 | 31 | public String getName() { 32 | return name; 33 | } 34 | 35 | public void setName(String name) { 36 | this.name = name; 37 | } 38 | 39 | public String getPassword() { 40 | return password; 41 | } 42 | 43 | public void setPassword(String password) { 44 | this.password = password; 45 | } 46 | 47 | public String getSalt() { 48 | return salt; 49 | } 50 | 51 | public void setSalt(String salt) { 52 | this.salt = salt; 53 | } 54 | 55 | public String getHeadUrl() { 56 | return headUrl; 57 | } 58 | 59 | public void setHeadUrl(String headUrl) { 60 | this.headUrl = headUrl; 61 | } 62 | } 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | -------------------------------------------------------------------------------- /src/main/java/com/suny/model/ViewObject.java: -------------------------------------------------------------------------------- 1 | package com.suny.model; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * 封装的对象实体类 8 | * Created by 孙建荣 on 17-8-31.下午12:53 9 | */ 10 | public class ViewObject { 11 | 12 | private Map objs = new HashMap<>(); 13 | 14 | public void set(String key, Object value) { 15 | objs.put(key, value); 16 | } 17 | 18 | public Object get(String key) { 19 | return objs.get(key); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/CommentService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.dao.CommentDAO; 4 | import com.suny.model.Comment; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by 孙建荣 on 17-9-4.上午8:39 12 | */ 13 | @Service 14 | public class CommentService { 15 | 16 | private final CommentDAO commentDAO; 17 | 18 | @Autowired 19 | public CommentService(CommentDAO commentDAO) { 20 | this.commentDAO = commentDAO; 21 | } 22 | 23 | public List getCommentsByEntity(int entity, int entityType) { 24 | return commentDAO.selectByEntity(entity, entityType); 25 | } 26 | 27 | public int addComment(Comment comment) { 28 | return commentDAO.addComment(comment); 29 | } 30 | 31 | public int getCommentCount(int entityId, int entityType) { 32 | return commentDAO.getCommentCount(entityId, entityType); 33 | } 34 | 35 | public void deleteComment(int entityId, int entityType) { 36 | // 状态为1则表示被逻辑删除 37 | commentDAO.updateStatus(entityId, entityType, 1); 38 | } 39 | 40 | 41 | public Comment getCommentById(int id) { 42 | return commentDAO.getCommentById(id); 43 | } 44 | 45 | 46 | public int getUserCommentCount(int userId) { 47 | return commentDAO.getUserCommentCount(userId); 48 | } 49 | 50 | } 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/FeedService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.dao.FeedDAO; 4 | import com.suny.model.Feed; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by 孙建荣 on 17-9-10.下午10:15 12 | */ 13 | @Service 14 | public class FeedService { 15 | 16 | private final FeedDAO feedDAO; 17 | 18 | @Autowired 19 | public FeedService(FeedDAO feedDAO) { 20 | this.feedDAO = feedDAO; 21 | } 22 | 23 | public List getUserFeeds(int maxId, List userIds, int count) { 24 | return feedDAO.selectUserFeeds(maxId, userIds, count); 25 | } 26 | 27 | public boolean addFeed(Feed feed) { 28 | feedDAO.addFeed(feed); 29 | return feed.getId() > 0; 30 | } 31 | 32 | public Feed getById(int id) { 33 | return feedDAO.getFeedById(id); 34 | } 35 | 36 | 37 | } 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/FollowService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.utils.JedisAdapter; 4 | import com.suny.utils.RedisKeyUtil; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import redis.clients.jedis.Jedis; 8 | import redis.clients.jedis.Transaction; 9 | 10 | import java.util.ArrayList; 11 | import java.util.Date; 12 | import java.util.List; 13 | import java.util.Set; 14 | 15 | /** 16 | * Created by 孙建荣 on 17-9-9.下午12:38 17 | */ 18 | @Service 19 | public class FollowService { 20 | 21 | private final JedisAdapter jedisAdapter; 22 | 23 | 24 | @Autowired 25 | public FollowService(JedisAdapter jedisAdapter) { 26 | this.jedisAdapter = jedisAdapter; 27 | } 28 | 29 | 30 | /** 31 | * 用户关注了某个实体,实体可以是问题,用户,评论等任何实体 32 | * 33 | * @param userId 用户的ID 34 | * @param entityType 实体的类型 35 | * @param entityId 实体的ID 36 | * @return 37 | */ 38 | public boolean follow(int userId, int entityType, int entityId) { 39 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 40 | String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType); 41 | Date date = new Date(); 42 | // 实体的粉丝增加当前用户 43 | Jedis jedis = jedisAdapter.getJedis(); 44 | Transaction transaction = jedisAdapter.multi(jedis); 45 | transaction.zadd(followeeKey, date.getTime(), String.valueOf(userId)); 46 | // 当前对这类实体关注+1 47 | transaction.zadd(followeeKey, date.getTime(), String.valueOf(entityId)); 48 | List ret = jedisAdapter.exec(transaction, jedis); 49 | return ret.size() == 2 && (Long) ret.get(0) > 0 && (Long) ret.get(1) > 0; 50 | } 51 | 52 | 53 | public boolean unFollow(int userId, int entityType, int entityId) { 54 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 55 | String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType); 56 | Date date = new Date(); 57 | // 实体的粉丝增加当前用户 58 | Jedis jedis = jedisAdapter.getJedis(); 59 | Transaction transaction = jedisAdapter.multi(jedis); 60 | // 实体的粉丝移除当前用户 61 | transaction.zrem(followeeKey, String.valueOf(userId)); 62 | // 当前对这类实体关注-1 63 | transaction.zrem(followeeKey, String.valueOf(entityId)); 64 | // 执行事物 65 | List ret = jedisAdapter.exec(transaction, jedis); 66 | return ret.size() == 2 && (Long) ret.get(0) > 0 && (Long) ret.get(1) > 0; 67 | } 68 | 69 | public List getFollowers(int entityType, int entityId, int count) { 70 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 71 | return getIdsFormSet(jedisAdapter.zrevrange(followerKey, 0, count)); 72 | } 73 | 74 | public List getFollowers(int entityType, int entityId, int offset, int count) { 75 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 76 | return getIdsFormSet(jedisAdapter.zrevrange(followerKey, 0, offset + count)); 77 | } 78 | 79 | public List getFollowees(int entityType, int entityId, int offset, int count) { 80 | String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, entityId); 81 | return getIdsFormSet(jedisAdapter.zrevrange(followeeKey, 0, offset + count)); 82 | } 83 | 84 | public List getFollowees(int entityType, int entityId, int count) { 85 | String followeeKey = RedisKeyUtil.getFolloweeKey(entityType, entityId); 86 | return getIdsFormSet(jedisAdapter.zrevrange(followeeKey, 0, count)); 87 | } 88 | 89 | 90 | public long getFollowerCount(int entityType, int entityId) { 91 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 92 | return jedisAdapter.zcard(followerKey); 93 | } 94 | 95 | public long getFolloweeCount(int userId, int entityType) { 96 | String followeeKey = RedisKeyUtil.getFolloweeKey(userId, entityType); 97 | return jedisAdapter.zcard(followeeKey); 98 | } 99 | 100 | 101 | private List getIdsFormSet(Set idset) { 102 | List ids = new ArrayList<>(); 103 | for (String str : idset) { 104 | ids.add(Integer.parseInt(str)); 105 | } 106 | return ids; 107 | } 108 | 109 | 110 | /** 111 | * 判断用户是否关注了某个实体 112 | * 113 | * @param userId 114 | * @param entityType 115 | * @param entityId 116 | * @return 117 | */ 118 | public boolean isFollower(int userId, int entityType, int entityId) { 119 | String followerKey = RedisKeyUtil.getFollowerKey(entityType, entityId); 120 | return jedisAdapter.zscore(followerKey, String.valueOf(userId)) != null; 121 | } 122 | 123 | 124 | } 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/LikeService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.utils.JedisAdapter; 4 | import com.suny.utils.RedisKeyUtil; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | /** 9 | * Created by 孙建荣 on 17-9-6.上午8:03 10 | */ 11 | @Service 12 | public class LikeService { 13 | private final JedisAdapter jedisAdapter; 14 | 15 | 16 | @Autowired 17 | public LikeService(JedisAdapter jedisAdapter) { 18 | this.jedisAdapter = jedisAdapter; 19 | } 20 | 21 | 22 | public long getLikeCount(int entityType, int entityId) { 23 | String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId); 24 | return jedisAdapter.scard(likeKey); 25 | } 26 | 27 | public int getLikeStatus(int userId, int entityType, int entityId) { 28 | String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId); 29 | if (jedisAdapter.sismember(likeKey, String.valueOf(userId))) { 30 | return 1; 31 | } 32 | String disLikeKey = RedisKeyUtil.getDisLikeKey(entityType, entityId); 33 | return jedisAdapter.sismember(disLikeKey, String.valueOf(userId)) ? 1 : 0; 34 | } 35 | 36 | 37 | public long like(int userId, int entityType, int entityId) { 38 | String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId); 39 | jedisAdapter.sadd(likeKey, String.valueOf(userId)); 40 | 41 | String disLikeKey = RedisKeyUtil.getDisLikeKey(entityType, entityId); 42 | jedisAdapter.srem(disLikeKey, String.valueOf(userId)); 43 | 44 | return jedisAdapter.scard(likeKey); 45 | 46 | } 47 | 48 | public long disLike(int userId, int entityType, int entityId) { 49 | 50 | String disLikeKey = RedisKeyUtil.getDisLikeKey(entityType, entityId); 51 | jedisAdapter.sadd(disLikeKey, String.valueOf(userId)); 52 | 53 | 54 | String likeKey = RedisKeyUtil.getLikeKey(entityType, entityId); 55 | jedisAdapter.srem(likeKey, String.valueOf(userId)); 56 | 57 | 58 | return jedisAdapter.scard(likeKey); 59 | 60 | } 61 | 62 | 63 | } 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/MessageService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.dao.MessageDAO; 4 | import com.suny.model.Message; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | 8 | import java.util.List; 9 | 10 | /** 11 | * Created by 孙建荣 on 17-9-4.下午7:36 12 | */ 13 | @Service 14 | public class MessageService { 15 | 16 | private final MessageDAO messageDAO; 17 | private final SensitiveService sensitiveService; 18 | 19 | @Autowired 20 | public MessageService(MessageDAO messageDAO, SensitiveService sensitiveService) { 21 | this.messageDAO = messageDAO; 22 | this.sensitiveService = sensitiveService; 23 | } 24 | 25 | public void updateMessageReadStatus(String conversationId) { 26 | messageDAO.updateMessagesReadStatus(conversationId); 27 | } 28 | 29 | 30 | public int addMessage(Message message) { 31 | message.setContent(sensitiveService.filter(message.getContent())); 32 | return messageDAO.addMessage(message); 33 | } 34 | 35 | public List getConversationDetail(String conversationId, int offset, int limit) { 36 | return messageDAO.getConversationDetail(conversationId, offset, limit); 37 | } 38 | 39 | public List getConversationList(int userId, int offset, int limit) { 40 | return messageDAO.getConversationList(userId, offset, limit); 41 | } 42 | 43 | public int getConversationUnreadCount(int userId, String conversationId) { 44 | return messageDAO.getConversationUnreadCount(userId, conversationId); 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/QuestionService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.dao.QuestionDAO; 4 | import com.suny.model.Question; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.stereotype.Service; 7 | import org.springframework.web.util.HtmlUtils; 8 | 9 | import java.util.List; 10 | 11 | /** 12 | * Created by 孙建荣 on 17-9-1.上午10:15 13 | */ 14 | @Service 15 | public class QuestionService { 16 | 17 | private final QuestionDAO questionDAO; 18 | 19 | private final SensitiveService sensitiveService; 20 | 21 | @Autowired 22 | public QuestionService(QuestionDAO questionDAO, SensitiveService sensitiveService) { 23 | this.questionDAO = questionDAO; 24 | this.sensitiveService = sensitiveService; 25 | } 26 | 27 | 28 | public int addQuestion(Question question) { 29 | question.setTitle(HtmlUtils.htmlEscape(question.getTitle())); 30 | question.setContent(HtmlUtils.htmlEscape(question.getContent())); 31 | // 敏感词审查 32 | question.setTitle(sensitiveService.filter(question.getTitle())); 33 | question.setContent(sensitiveService.filter(question.getContent())); 34 | return questionDAO.addQuestion(question) > 0 ? question.getId() : 0; 35 | } 36 | 37 | public List getLatestQuestion(int userId, int offset, int limit) { 38 | return questionDAO.selectLatestQuestions(userId, offset, limit); 39 | } 40 | 41 | public int updateCommentCount(int id, int count) { 42 | return questionDAO.updateCommentCount(id, count); 43 | } 44 | 45 | 46 | public Question getById(int id) { 47 | return questionDAO.getById(id); 48 | } 49 | } 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/SearchService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.model.Question; 4 | import org.apache.solr.client.solrj.SolrQuery; 5 | import org.apache.solr.client.solrj.SolrServerException; 6 | import org.apache.solr.client.solrj.impl.HttpSolrClient; 7 | import org.apache.solr.client.solrj.response.QueryResponse; 8 | import org.apache.solr.client.solrj.response.UpdateResponse; 9 | import org.apache.solr.common.SolrInputDocument; 10 | import org.springframework.stereotype.Service; 11 | 12 | import java.io.IOException; 13 | import java.util.ArrayList; 14 | import java.util.List; 15 | import java.util.Map; 16 | 17 | /** 18 | * Created by 孙建荣 on 17-9-13.下午10:52 19 | */ 20 | @Service 21 | public class SearchService { 22 | private static final String SOLR_URL = "http://127.0.0.1:8983/solr/wenda"; 23 | private HttpSolrClient client = new HttpSolrClient.Builder(SOLR_URL).build(); 24 | private static final String QUESTION_TITLE_FIELD = "question_title"; 25 | private static final String QUESTION_CONTENT_FIELD = "question_content"; 26 | 27 | 28 | public List searchQuestion(String keyword, int offset, int count, String hlPre, String hlPos) throws IOException, SolrServerException { 29 | List questionList = new ArrayList<>(); 30 | SolrQuery query = new SolrQuery(keyword); 31 | query.setRows(count); 32 | query.setStart(offset); 33 | query.setHighlight(true); 34 | query.setHighlightSimplePre(hlPre); 35 | query.setHighlightSimplePost(hlPos); 36 | query.set("hl.fl", QUESTION_TITLE_FIELD + "," + QUESTION_CONTENT_FIELD); 37 | QueryResponse response = client.query(query); 38 | 39 | for (Map.Entry>> entry : response.getHighlighting().entrySet()) { 40 | Question q = new Question(); 41 | q.setId(Integer.parseInt(entry.getKey())); 42 | if (entry.getValue().containsKey(QUESTION_CONTENT_FIELD)) { 43 | List contentList = entry.getValue().get(QUESTION_CONTENT_FIELD); 44 | if (contentList.size() > 0) { 45 | q.setContent(contentList.get(0)); 46 | } 47 | } 48 | if (entry.getValue().containsKey(QUESTION_TITLE_FIELD)) { 49 | List titleList = entry.getValue().get(QUESTION_TITLE_FIELD); 50 | if (titleList.size() > 0) { 51 | q.setTitle(titleList.get(0)); 52 | } 53 | } 54 | questionList.add(q); 55 | } 56 | return questionList; 57 | } 58 | 59 | 60 | public boolean indexQuestion(int qid, String title, String content) throws IOException, SolrServerException { 61 | SolrInputDocument doc = new SolrInputDocument(); 62 | doc.setField("id", qid); 63 | doc.setField(QUESTION_TITLE_FIELD, title); 64 | doc.setField("QUESTION_CONTENT_FIELD", content); 65 | UpdateResponse response = client.add(doc, 1000); 66 | return response != null && response.getStatus() == 0; 67 | } 68 | } 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/SensitiveService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import org.apache.commons.lang.CharUtils; 4 | import org.apache.commons.lang.StringUtils; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.beans.factory.InitializingBean; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.io.BufferedReader; 11 | import java.io.IOException; 12 | import java.io.InputStream; 13 | import java.io.InputStreamReader; 14 | import java.util.HashMap; 15 | import java.util.Map; 16 | 17 | /** 18 | * Created by 孙建荣 on 17-9-3.下午10:13 19 | */ 20 | @Service 21 | public class SensitiveService implements InitializingBean { 22 | 23 | private static Logger logger = LoggerFactory.getLogger(SensitiveService.class); 24 | 25 | 26 | /** 27 | * 默认的敏感词 28 | */ 29 | private static final String DEFAULT_REPLACEMENT = "***"; 30 | 31 | 32 | private class TrieNode { 33 | // true 关键词的终结 ; false 继续 34 | private boolean end = false; 35 | // key 下一个字符,value是对应的节点 36 | private Map subNodes = new HashMap<>(); 37 | 38 | // 向指定位置添加节点树 39 | void addSubNode(Character key, TrieNode node) { 40 | subNodes.put(key, node); 41 | } 42 | 43 | /** 44 | * 获取下个节点 45 | */ 46 | TrieNode getSubNode(Character key) { 47 | return subNodes.get(key); 48 | } 49 | 50 | boolean isKeywordEnd() { 51 | return end; 52 | } 53 | 54 | void setKeywordEnd(boolean end) { 55 | this.end = end; 56 | } 57 | 58 | public int getSubNodeCount() { 59 | return subNodes.size(); 60 | } 61 | } 62 | 63 | /** 64 | * 根节点 65 | */ 66 | private TrieNode rootNode = new TrieNode(); 67 | 68 | 69 | /** 70 | * 判断是否是一个符号 71 | */ 72 | 73 | private boolean isSymbol(char c) { 74 | int ic = (int) c; 75 | // 0x2E80-0x9FF为东亚的文字范围 76 | return !CharUtils.isAsciiAlphanumeric(c) && (ic < 0x2E80 || ic > 0x9FFF); 77 | } 78 | 79 | /** 80 | * 过滤敏感词 81 | */ 82 | 83 | public String filter(String text) { 84 | if (StringUtils.isBlank(text)) { 85 | return text; 86 | } 87 | String replacement = DEFAULT_REPLACEMENT; 88 | StringBuilder result = new StringBuilder(); 89 | TrieNode tempNode = rootNode; 90 | int begin = 0; // 回滚数 91 | int position = 0; //当前的额位置 92 | 93 | while (position < text.length()) { 94 | char c = text.charAt(position); 95 | // 如果是空格的话就直接跳过 96 | if (isSymbol(c)) { 97 | if (tempNode == rootNode) { 98 | result.append(c); 99 | ++begin; 100 | } 101 | ++position; 102 | continue; 103 | } 104 | tempNode = tempNode.getSubNode(c); 105 | //当前的位置匹配结束 106 | if (tempNode == null) { 107 | // 以begin开始的字符串不存在敏感词 108 | result.append(text.charAt(begin)); 109 | // 调到下一个字符开始测试 110 | position = begin + 1; 111 | begin = position; 112 | // 回到树初始节点 113 | tempNode = rootNode; 114 | } else if (tempNode.isKeywordEnd()) { 115 | // 发现敏感词,从begin到position的位置用replacement替换掉 116 | result.append(replacement); 117 | position = position + 1; 118 | begin = position; 119 | tempNode = rootNode; 120 | } else { 121 | ++position; 122 | } 123 | } 124 | result.append(text.substring(begin)); 125 | 126 | return result.toString(); 127 | } 128 | 129 | private void addWord(String lineTxt) { 130 | TrieNode tempNode = rootNode; 131 | // 循环两个字节 132 | for (int i = 0; i < lineTxt.length(); i++) { 133 | Character c = lineTxt.charAt(i); 134 | // 过滤空格 135 | if (isSymbol(c)) { 136 | continue; 137 | } 138 | TrieNode node = tempNode.getSubNode(c); 139 | // 没有初始化 140 | if (node == null) { 141 | node = new TrieNode(); 142 | tempNode.addSubNode(c, node); 143 | } 144 | 145 | tempNode = node; 146 | 147 | if (i == lineTxt.length() - 1) { 148 | // 关键词结束,设置结束标志 149 | tempNode.setKeywordEnd(true); 150 | } 151 | 152 | } 153 | } 154 | 155 | @Override 156 | public void afterPropertiesSet() throws Exception { 157 | rootNode = new TrieNode(); 158 | try { 159 | InputStream is = Thread.currentThread().getContextClassLoader().getResourceAsStream("SensitiveWord.txt"); 160 | InputStreamReader reader = new InputStreamReader(is); 161 | BufferedReader bufferedReader = new BufferedReader(reader); 162 | String lineTxt; 163 | while ((lineTxt = bufferedReader.readLine()) != null) { 164 | lineTxt = lineTxt.trim(); 165 | addWord(lineTxt); 166 | } 167 | reader.close(); 168 | } catch (IOException e) { 169 | logger.error("读取敏感词文件失败" + e.getMessage()); 170 | } 171 | } 172 | 173 | 174 | /* public static void main(String[] args) { 175 | SensitiveService sensitiveService = new SensitiveService(); 176 | sensitiveService.addWord("色情"); 177 | sensitiveService.addWord("好色"); 178 | System.out.println(sensitiveService.filter("你好色情X色**情XX")); 179 | }*/ 180 | 181 | } 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/TicketService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-1.上午10:49 7 | */ 8 | @Service 9 | public class TicketService { 10 | } 11 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/UserService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import com.suny.dao.LoginTicketDAO; 4 | import com.suny.dao.UserDAO; 5 | import com.suny.model.LoginTicket; 6 | import com.suny.model.User; 7 | import com.suny.utils.WendaUtil; 8 | import org.apache.commons.lang.StringUtils; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.transaction.annotation.Transactional; 12 | 13 | import java.util.*; 14 | 15 | /** 16 | * Created by 孙建荣 on 17-9-1.上午10:27 17 | */ 18 | @Service 19 | public class UserService { 20 | 21 | private final UserDAO userDAO; 22 | 23 | private final LoginTicketDAO loginTicketDAO; 24 | 25 | @Autowired 26 | public UserService(UserDAO userDAO, LoginTicketDAO loginTicketDAO) { 27 | this.userDAO = userDAO; 28 | this.loginTicketDAO = loginTicketDAO; 29 | } 30 | 31 | public User selectByName(String name) { 32 | return userDAO.selectByName(name); 33 | } 34 | 35 | @Transactional 36 | public Map register(String username, String password) { 37 | Map map = new HashMap<>(); 38 | if (StringUtils.isBlank(username)) { 39 | map.put("msg", "用户名不能为空"); 40 | return map; 41 | } 42 | if (StringUtils.isBlank(password)) { 43 | map.put("msg", "密码不能为空"); 44 | return map; 45 | } 46 | User user = userDAO.selectByName(username); 47 | if (user != null) { 48 | map.put("msg", "用户名已经被注册"); 49 | return map; 50 | } 51 | 52 | // 密码强度 53 | user = new User(); 54 | user.setName(username); 55 | user.setSalt(UUID.randomUUID().toString()); 56 | String head = String.format("http://images.nowcoder.com/head/%dt.png", new Random().nextInt(1000)); 57 | user.setHeadUrl(head); 58 | user.setPassword(WendaUtil.MD5(password + user.getSalt())); 59 | userDAO.addUser(user); 60 | 61 | // 登录 62 | String ticket = addLoginTicket(user.getId()); 63 | map.put("ticket", ticket); 64 | return map; 65 | } 66 | 67 | public Map login(String username, String password) { 68 | Map map = new HashMap<>(); 69 | String ticket; 70 | if (StringUtils.isBlank(username)) { 71 | map.put("msg", "用户名不能为空"); 72 | return map; 73 | } 74 | if (StringUtils.isBlank(password)) { 75 | map.put("msg", "密码不能为空"); 76 | return map; 77 | } 78 | 79 | User user = userDAO.selectByName(username); 80 | if (user == null) { 81 | map.put("msg", "用户名不存在"); 82 | return map; 83 | } 84 | if (!WendaUtil.MD5(password + user.getSalt()).equals(user.getPassword())) { 85 | map.put("msg", "密码不正确"); 86 | return map; 87 | } 88 | LoginTicket loginTicket = loginTicketDAO.selectByUserId(user.getId()); 89 | boolean expiredStatus = checkTicketExpired(loginTicket); 90 | if (!expiredStatus) { 91 | ticket = addLoginTicket(user.getId()); 92 | } else { 93 | ticket = loginTicket.getTicket(); 94 | } 95 | map.put("ticket", ticket); 96 | map.put("userId", user.getId()); 97 | return map; 98 | } 99 | 100 | private boolean checkTicketExpired(LoginTicket loginTicket) { 101 | if (loginTicket == null) { 102 | return false; 103 | } 104 | // 0无效,1有效 105 | Date expiredDate = loginTicket.getExpired(); 106 | // 时间过期了或者是状态为0就直接删除好了 107 | if (expiredDate.before(new Date()) || loginTicket.getStatus() == 0) { 108 | loginTicketDAO.deleteTicket(loginTicket.getId()); 109 | return false; 110 | } 111 | return true; 112 | } 113 | 114 | private String addLoginTicket(int userId) { 115 | LoginTicket loginTicket = new LoginTicket(); 116 | loginTicket.setUserId(userId); 117 | Date date = new Date(); 118 | date.setTime(date.getTime() + 1000 * 3600 * 24); 119 | loginTicket.setExpired(date); 120 | loginTicket.setStatus(1); 121 | loginTicket.setTicket(UUID.randomUUID().toString().replaceAll("-", "")); 122 | loginTicketDAO.addTicket(loginTicket); 123 | return loginTicket.getTicket(); 124 | } 125 | 126 | 127 | public User getUser(int id) { 128 | return userDAO.selectById(id); 129 | } 130 | 131 | public void logout(String ticket) { 132 | loginTicketDAO.updateStatus(ticket, 0); 133 | } 134 | 135 | } 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | -------------------------------------------------------------------------------- /src/main/java/com/suny/service/WendaService.java: -------------------------------------------------------------------------------- 1 | package com.suny.service; 2 | 3 | import org.springframework.stereotype.Service; 4 | 5 | /** 6 | * Created by 孙建荣 on 17-9-1.下午10:45 7 | */ 8 | @Service 9 | public class WendaService { 10 | 11 | public String getMessage(int userId) { 12 | return "HelloMessage" + String.valueOf(userId); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/com/suny/utils/MailSender.java: -------------------------------------------------------------------------------- 1 | package com.suny.utils; 2 | 3 | import org.apache.velocity.app.VelocityEngine; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.InitializingBean; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.mail.javamail.JavaMailSenderImpl; 9 | import org.springframework.mail.javamail.MimeMessageHelper; 10 | import org.springframework.stereotype.Service; 11 | import org.springframework.ui.velocity.VelocityEngineUtils; 12 | 13 | import javax.mail.Authenticator; 14 | import javax.mail.PasswordAuthentication; 15 | import javax.mail.Session; 16 | import javax.mail.internet.InternetAddress; 17 | import javax.mail.internet.MimeMessage; 18 | import javax.mail.internet.MimeUtility; 19 | import java.io.UnsupportedEncodingException; 20 | import java.util.Map; 21 | import java.util.Properties; 22 | 23 | 24 | /** 25 | * Created by 孙建荣 on 17-9-8.上午9:33 26 | */ 27 | @Service 28 | public class MailSender implements InitializingBean { 29 | 30 | private static Logger logger = LoggerFactory.getLogger(MailSender.class); 31 | private JavaMailSenderImpl mailSender; 32 | 33 | private final VelocityEngine velocityEngine; 34 | 35 | private final MailSetting mailSetting; 36 | 37 | @Autowired 38 | public MailSender(VelocityEngine velocityEngine, MailSetting mailSetting) { 39 | this.velocityEngine = velocityEngine; 40 | this.mailSetting = mailSetting; 41 | } 42 | 43 | public boolean sendWithHTMLTemplate(String to, String subject, 44 | String template, Map model) { 45 | try { 46 | String nick = MimeUtility.encodeText("问答邮件通知"); 47 | InternetAddress from = new InternetAddress(nick + ""); 48 | MimeMessage mimeMessage = mailSender.createMimeMessage(); 49 | MimeMessageHelper mimeMessageHelper = new MimeMessageHelper(mimeMessage); 50 | String result = VelocityEngineUtils.mergeTemplateIntoString(velocityEngine, template, "UTF-8", model); 51 | mimeMessageHelper.setTo(to); 52 | mimeMessageHelper.setFrom(from); 53 | mimeMessageHelper.setSubject(subject); 54 | mimeMessageHelper.setText(result, true); 55 | mailSender.send(mimeMessage); 56 | return true; 57 | } catch (UnsupportedEncodingException e) { 58 | logger.error("发送邮件失败", e.getMessage()); 59 | return false; 60 | } catch (javax.mail.MessagingException e) { 61 | logger.error("消息发送异常", e.getMessage()); 62 | return false; 63 | } 64 | } 65 | 66 | @Override 67 | public void afterPropertiesSet() throws Exception { 68 | mailSender = new JavaMailSenderImpl(); 69 | mailSender.setUsername(mailSetting.getUsername()); 70 | mailSender.setPassword(mailSetting.getPassword()); 71 | mailSender.setHost(mailSetting.getHost()); 72 | mailSender.setPort(mailSetting.getPort()); 73 | mailSender.setProtocol(mailSetting.getProtocol()); 74 | mailSender.setDefaultEncoding(mailSetting.getDefaultEncoding()); 75 | Session session = Session.getDefaultInstance(mailSender.getJavaMailProperties(), new Authenticator() { 76 | // 身份认证 77 | @Override 78 | protected PasswordAuthentication getPasswordAuthentication() { 79 | return new PasswordAuthentication(mailSender.getUsername(), mailSender.getPassword()); 80 | } 81 | }); 82 | mailSender.setSession(session); 83 | Properties javaProperties = new Properties(); 84 | // javaProperties.put("mail.smtp.ssl.enable", true); 85 | javaProperties.put("mail.stmp.auth",true); 86 | javaProperties.put("mail.stmp.starttls.enable",true); 87 | 88 | mailSender.setJavaMailProperties(javaProperties); 89 | } 90 | 91 | 92 | } 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /src/main/java/com/suny/utils/MailSetting.java: -------------------------------------------------------------------------------- 1 | package com.suny.utils; 2 | 3 | import org.springframework.boot.context.properties.ConfigurationProperties; 4 | import org.springframework.stereotype.Component; 5 | 6 | /** 7 | * Created by 孙建荣 on 17-9-8.上午9:45 8 | */ 9 | @ConfigurationProperties(prefix = "mail", locations = "classpath:mailSetting.properties") 10 | @Component 11 | public class MailSetting { 12 | 13 | private String username; 14 | private String password; 15 | private String host; 16 | private int port; 17 | private String protocol; 18 | private String defaultEncoding; 19 | 20 | public MailSetting() { 21 | } 22 | 23 | public MailSetting(String username, String password, String host, int port, String protocol, String defaultEncoding) { 24 | this.username = username; 25 | this.password = password; 26 | this.host = host; 27 | this.port = port; 28 | this.protocol = protocol; 29 | this.defaultEncoding = defaultEncoding; 30 | } 31 | 32 | public String getUsername() { 33 | return username; 34 | } 35 | 36 | public void setUsername(String username) { 37 | this.username = username; 38 | } 39 | 40 | public String getPassword() { 41 | return password; 42 | } 43 | 44 | public void setPassword(String password) { 45 | this.password = password; 46 | } 47 | 48 | public String getHost() { 49 | return host; 50 | } 51 | 52 | public void setHost(String host) { 53 | this.host = host; 54 | } 55 | 56 | public int getPort() { 57 | return port; 58 | } 59 | 60 | public void setPort(int port) { 61 | this.port = port; 62 | } 63 | 64 | public String getProtocol() { 65 | return protocol; 66 | } 67 | 68 | public void setProtocol(String protocol) { 69 | this.protocol = protocol; 70 | } 71 | 72 | public String getDefaultEncoding() { 73 | return defaultEncoding; 74 | } 75 | 76 | public void setDefaultEncoding(String defaultEncoding) { 77 | this.defaultEncoding = defaultEncoding; 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/main/java/com/suny/utils/RedisKeyUtil.java: -------------------------------------------------------------------------------- 1 | package com.suny.utils; 2 | 3 | /** 4 | * Created by 孙建荣 on 17-9-6.上午8:05 5 | */ 6 | public class RedisKeyUtil { 7 | 8 | private static String SPLIT = ":"; 9 | private static String BIZ_LIKE = "LIKE"; 10 | private static String BIZ_DISLIKE = "DISLIKE"; 11 | private static String BIZ_EVENTQUEUE = "EVENT_QUEUE"; 12 | private static String BIZ_TIMELINE = "TIMELINE"; 13 | 14 | // 获取粉丝 15 | private static String BIZ_FOLLOWER = "FOLLOWER"; 16 | // 关注的对象 17 | private static String BIZ_FOLOWEE = "FOLLOWEE"; 18 | 19 | public static String getLikeKey(int entityType, int entityId) { 20 | return BIZ_LIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId); 21 | } 22 | 23 | public static String getDisLikeKey(int entityType, int entityId) { 24 | return BIZ_DISLIKE + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId); 25 | } 26 | 27 | public static String getEventQueueKey() { 28 | return BIZ_EVENTQUEUE; 29 | } 30 | 31 | /** 32 | * 某个用户的粉丝key 33 | * 34 | * @param entityType 实体类型 35 | * @param entityId 实体ID 36 | * @return 37 | */ 38 | public static String getFollowerKey(int entityType, int entityId) { 39 | return BIZ_FOLLOWER + SPLIT + String.valueOf(entityType) + SPLIT + String.valueOf(entityId); 40 | } 41 | 42 | /** 43 | * 每个用户对某类实体的关注key 44 | * 45 | * @param userId 用户的ID 46 | * @param entityType 实体类型 47 | * @return 48 | */ 49 | public static String getFolloweeKey(int userId, int entityType) { 50 | return BIZ_FOLLOWER + SPLIT + String.valueOf(userId) + SPLIT + String.valueOf(entityType); 51 | } 52 | 53 | 54 | public static String getTImeline(int userId) { 55 | return BIZ_TIMELINE + SPLIT + String.valueOf(userId); 56 | } 57 | 58 | 59 | } 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | -------------------------------------------------------------------------------- /src/main/java/com/suny/utils/WendaUtil.java: -------------------------------------------------------------------------------- 1 | package com.suny.utils; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | 7 | import java.security.MessageDigest; 8 | import java.util.Map; 9 | 10 | /** 11 | * Created by 孙建荣 on 17-9-1.上午10:57 12 | */ 13 | public class WendaUtil { 14 | 15 | private static final Logger logger = LoggerFactory.getLogger(WendaUtil.class); 16 | 17 | 18 | public static int ANONYMOUS_USERID = 10000; 19 | public static int SYSTEM_USERID = 10001; 20 | 21 | public static String getJSONString(int code) { 22 | JSONObject jsonObject = new JSONObject(); 23 | jsonObject.put("code", code); 24 | return jsonObject.toJSONString(); 25 | } 26 | 27 | public static String getJSONString(int code, String msg) { 28 | JSONObject jsonObject = new JSONObject(); 29 | jsonObject.put("code", code); 30 | jsonObject.put("msg", msg); 31 | return jsonObject.toJSONString(); 32 | } 33 | 34 | public static String getJSONString(int code, Map map) { 35 | JSONObject jsonObject = new JSONObject(); 36 | jsonObject.put("code", code); 37 | for (Map.Entry entry : map.entrySet()) { 38 | jsonObject.put(entry.getKey(), entry.getValue()); 39 | } 40 | return jsonObject.toJSONString(); 41 | } 42 | 43 | 44 | public static String MD5(String key) { 45 | char hexDigits[] = { 46 | '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 47 | }; 48 | try { 49 | byte[] btInput = key.getBytes(); 50 | // 获得MD5摘要算法的 MessageDigest 对象 51 | MessageDigest mdInst = MessageDigest.getInstance("MD5"); 52 | // 使用指定的字节更新摘要 53 | mdInst.update(btInput); 54 | // 获得密文 55 | byte[] md = mdInst.digest(); 56 | // 把密文转换成十六进制的字符串形式 57 | int j = md.length; 58 | char str[] = new char[j * 2]; 59 | int k = 0; 60 | for (int i = 0; i < j; i++) { 61 | byte byte0 = md[i]; 62 | str[k++] = hexDigits[byte0 >>> 4 & 0xf]; 63 | str[k++] = hexDigits[byte0 & 0xf]; 64 | } 65 | return new String(str); 66 | } catch (Exception e) { 67 | logger.error("生成MD5失败", e); 68 | return null; 69 | } 70 | 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/resources/SensitiveWord.txt: -------------------------------------------------------------------------------- 1 | 嫖娼 2 | 赌博 3 | 情赌 4 | 色情 -------------------------------------------------------------------------------- /src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | spring.velocity.suffix=.html 2 | spring.datasource.url=jdbc:mysql://localhost:3306/wenda?useUnicode=true&characterEncoding=utf8&useSSL=false 3 | spring.datasource.username=root 4 | spring.datasource.password=root 5 | mybatis.config-location=classpath:mybatis-config.xml 6 | spring.velocity.toolbox-config-location=toolbox.xml 7 | 8 | logging.level.org.springframework=WARN 9 | logging.level.org.spring.springboot.dao=DEBUG 10 | logging.file=logs/spring-boot-logging.log 11 | logging.level.com.suny.dao=DEBUG -------------------------------------------------------------------------------- /src/main/resources/com/suny/dao/FeedDAO.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | feed 5 | id, create_date, user_id, data, type 6 | 7 | 24 | 25 | -------------------------------------------------------------------------------- /src/main/resources/com/suny/dao/LoginTicketDAO.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | login_ticket 6 | id, user_id, expired, status, ticket 7 | 14 | 21 | -------------------------------------------------------------------------------- /src/main/resources/com/suny/dao/QuestionDAO.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | question 6 | id, title, content, comment_count, create_date, user_id 7 | 18 | -------------------------------------------------------------------------------- /src/main/resources/log4j.properties: -------------------------------------------------------------------------------- 1 | log4j.logger.org.apache.ibatis=debug 2 | log4j.logger.java.sql.PreparedStatement=DEBUG 3 | log4j.logger.java.sql.Statement=DEBUG 4 | log4j.logger.java.sql.Connection=DEBUG 5 | log4j.logger.java.sql.ResultSet=DEBUG -------------------------------------------------------------------------------- /src/main/resources/mybatis-config.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /src/main/resources/sql/comment.sql: -------------------------------------------------------------------------------- 1 | CREATE TABLE `comment` ( 2 | `id` int(11) NOT NULL AUTO_INCREMENT, 3 | `content` text NOT NULL, 4 | `user_id` int(11) NOT NULL, 5 | `entity_id` int(11) NOT NULL, 6 | `entity_type` int(11) NOT NULL, 7 | `created_date` datetime NOT NULL, 8 | `status` int(11) NOT NULL DEFAULT '0', 9 | PRIMARY KEY (`id`), 10 | KEY `entity_index` (`entity_id`,`entity_type`) 11 | ) ENGINE=InnoDB AUTO_INCREMENT=37228 DEFAULT CHARSET=utf8; -------------------------------------------------------------------------------- /src/main/resources/sql/feed.sql: -------------------------------------------------------------------------------- 1 | USE wenda; 2 | 3 | DROP TABLE IF EXISTS `feed`; 4 | 5 | 6 | CREATE TABLE `feed` ( 7 | `id` INT NOT NULL AUTO_INCREMENT, 8 | `create_date` DATETIME NULL, 9 | `user_id` INT NULL, 10 | `data` TINYINT NULL, 11 | `type` INT NULL, 12 | PRIMARY KEY (`id`), 13 | INDEX `user_index` (`user_id` ASC) 14 | 15 | ) 16 | ENGINE = InnoDB 17 | DEFAULT CHARACTER SET = utf8 -------------------------------------------------------------------------------- /src/main/resources/sql/login_ticket.sql: -------------------------------------------------------------------------------- 1 | -- 使用数据库 2 | USE wenda; 3 | 4 | DROP TABLE IF EXISTS `login_ticket`; 5 | 6 | -- 创建数据库 7 | CREATE TABLE `login_ticket`( 8 | `id` INT NOT NULL AUTO_INCREMENT, 9 | `user_id` INT NOT NULL , 10 | `ticket` VARCHAR(45) NOT NULL COMMENT '随机生成的时间', 11 | `expired` DATETIME NOT NULL COMMENT '过期时间', 12 | `status` INT NULL DEFAULT 0 COMMENT '状态', 13 | PRIMARY KEY (`id`), 14 | UNIQUE INDEX `ticket_UNIQE` (`ticket` ASC ) 15 | )ENGINE =InnoDB DEFAULT CHARSET=utf8 -------------------------------------------------------------------------------- /src/main/resources/sql/message.sql: -------------------------------------------------------------------------------- 1 | USE wenda; 2 | 3 | CREATE TABLE `message` ( 4 | `id` INT(11) NOT NULL AUTO_INCREMENT, 5 | `from_id` INT(11) NOT NULL, 6 | `to_id` INT(11) NOT NULL, 7 | `content` TEXT NOT NULL, 8 | `has_read` TINYINT(1) NOT NULL DEFAULT 0 9 | COMMENT '0为没有阅读,1为已经阅读过了的', 10 | `conversation_id` VARCHAR(50) NOT NULL DEFAULT 0, 11 | `create_date` DATETIME NOT NULL, 12 | PRIMARY KEY (`id`) 13 | ) 14 | ENGINE = InnoDB 15 | AUTO_INCREMENT = 10000 16 | DEFAULT CHARSET = utf8; 17 | 18 | -- 模拟数据 19 | INSERT INTO message (from_id, to_id, content, conversation_id, create_date) VALUES ( 20 | 10000, 100001, "你在吗", "1", CURRENT_TIMESTAMP), 21 | (10001, 100000, "你在吗", "1", CURRENT_TIMESTAMP()), 22 | (10000, 100001, "我很好啊", "1", CURRENT_TIMESTAMP()), 23 | (10001, 100000, "我也很好", "1", CURRENT_TIMESTAMP()), 24 | (10000, 100003, "爱", "1", CURRENT_TIMESTAMP()), 25 | (10003, 100000, "我也是", "1", CURRENT_TIMESTAMP()), 26 | (10000, 100003, "太好了", "1", CURRENT_TIMESTAMP()), 27 | (10000, 100004, "海螺", "1", CURRENT_TIMESTAMP()), 28 | (10000, 100005, "借钱呀", "1", CURRENT_TIMESTAMP()), 29 | (10000, 100006, "交个朋友罗", "1", CURRENT_TIMESTAMP() 30 | ); -------------------------------------------------------------------------------- /src/main/resources/sql/question.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS `question`; 2 | 3 | -- 先选择数据库,数据库名字不一致需要更改 4 | USE wenda; 5 | 6 | -- 建立问题表 7 | CREATE TABLE `question` ( 8 | `id` INT NOT NULL AUTO_INCREMENT, 9 | `title` VARCHAR(255) NOT NULL, 10 | `content` TEXT NULL, 11 | `user_id` INT NOT NULL, 12 | `created_date` DATETIME NOT NULL, 13 | `comment_count` INT NOT NULL, 14 | PRIMARY KEY (`id`), 15 | INDEX `date_index` (`created_date` ASC) 16 | ) 17 | ENGINE = InnoDB 18 | DEFAULT CHARSET = UTF8 COMMENT '问题表'; 19 | 20 | -- 模拟数据 21 | INSERT INTO question (id, title, content, user_id, created_date, comment_count) 22 | VALUES ( 23 | NULL, "如何看待知乎的行为", "无法看待", 10000, CURRENT_TIMESTAMP(), 55), 24 | (NULL, "如何看待知乎的行为1", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 25 | (NULL, "如何看待知乎的行为2", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 26 | (NULL, "如何看待知乎的行为3", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 27 | (NULL, "如何看待知乎的行为4", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 28 | (NULL, "如何看待知乎的行为5", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 29 | (NULL, "如何看待知乎的行为6", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 30 | (NULL, "如何看待知乎的行为7", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 31 | (NULL, "如何看待知乎的行为8", '无法看待', 10000, CURRENT_TIMESTAMP, 55), 32 | (NULL, "如何看待知乎的行为9", '无法看待', 10000, CURRENT_TIMESTAMP, 55); 33 | -------------------------------------------------------------------------------- /src/main/resources/sql/user.sql: -------------------------------------------------------------------------------- 1 | DROP TABLE IF EXISTS 'user'; 2 | -- 这里首先先选择数据库,如果数据库名不一样需要更改 3 | USE wenda; 4 | 5 | -- 创建用户表 6 | CREATE TABLE `user` ( 7 | `id` INT NOT NULL AUTO_INCREMENT 8 | COMMENT '主键id', 9 | `name` VARCHAR(64) NOT NULL UNIQUE 10 | COMMENT '用户名', 11 | `password` VARCHAR(128) NOT NULL 12 | COMMENT '密码', 13 | `salt` VARCHAR(50) NOT NULL 14 | COMMENT '盐值', 15 | `head_url` VARCHAR(255) NOT NULL 16 | COMMENT '头像地址', 17 | PRIMARY KEY (`id`) 18 | ) 19 | ENGINE = InnoDB 20 | AUTO_INCREMENT = 10000 21 | DEFAULT CHARSET = utf8 22 | COMMENT '用户表'; 23 | 24 | -- 模拟用户数据 25 | INSERT INTO user (id, name, password, salt, head_url) VALUES ( 26 | NULL, 'admin', 'admin', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 27 | (NULL, 'hktianya', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 28 | (NULL, 'pause', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 29 | (NULL, 'lfhack', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 30 | (NULL, 'b952658176', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 31 | (NULL, 'Server', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 32 | (NULL, '52yaowanzi', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 33 | (NULL, 'china-fengbao', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'), 34 | (NULL, 'david2013', 'abc', 'wendaSalt', 'https://www.baidu.com/img/bd_logo1.png'); 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/main/resources/static/images/img/spinner2.8f60205d.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/img/spinner2.8f60205d.gif -------------------------------------------------------------------------------- /src/main/resources/static/images/img/sprites-1.9.2.4c54885a.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/img/sprites-1.9.2.4c54885a.png -------------------------------------------------------------------------------- /src/main/resources/static/images/img/sprites-1.9.2@2x.6e638473.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/img/sprites-1.9.2@2x.6e638473.png -------------------------------------------------------------------------------- /src/main/resources/static/images/img/sprites.auto.915a539c.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/img/sprites.auto.915a539c.png -------------------------------------------------------------------------------- /src/main/resources/static/images/img/sprites@2x.auto.dd5c79c1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/img/sprites@2x.auto.dd5c79c1.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/070a9fb26_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/070a9fb26_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/0b856ef58f76b7c83a0e130f6ef71281_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/0b856ef58f76b7c83a0e130f6ef71281_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/0ba3da3f03ced7a438118b0be77df56c_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/0ba3da3f03ced7a438118b0be77df56c_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/0c6a39621ab1d456b1e6e492d0becc0c_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/0c6a39621ab1d456b1e6e492d0becc0c_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/0cf21546298ad1ed3cb64be61d822c27_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/0cf21546298ad1ed3cb64be61d822c27_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/0cffb89d0b0bd4e726ae54b212a31c3b_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/0cffb89d0b0bd4e726ae54b212a31c3b_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/102799979_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/102799979_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/10a4cd7fb082375332be33eadfd14c58_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/10a4cd7fb082375332be33eadfd14c58_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/11ba31c8bb5473a44b7690eff24b9123_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/11ba31c8bb5473a44b7690eff24b9123_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/11be4a90ed938abfbab4899df56ee754_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/11be4a90ed938abfbab4899df56ee754_s.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/12a8e1ce5ad8060fddb93ae2df98028b_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/12a8e1ce5ad8060fddb93ae2df98028b_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/19456ebfe8b207320735f282769ac635_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/19456ebfe8b207320735f282769ac635_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/1ac7840eeb19ada0bbf85f51702d5784_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/1ac7840eeb19ada0bbf85f51702d5784_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/1ce495b02_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/1ce495b02_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/23cace5cf60f39dbc095bd7a12b2cfad_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/23cace5cf60f39dbc095bd7a12b2cfad_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/24ce38dd5fc2a0c4e0525e577eef7d64_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/24ce38dd5fc2a0c4e0525e577eef7d64_s.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/272627e471a533f58f319a9e600e0a94_is.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/272627e471a533f58f319a9e600e0a94_is.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/2e21e58a990f5c756e813a64a4bba14c_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/2e21e58a990f5c756e813a64a4bba14c_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/31826765d442d8222a05cd67d0643a25_270x225.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/31826765d442d8222a05cd67d0643a25_270x225.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/33fb6f51a3f4e16b6e89172040451dca_b.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/33fb6f51a3f4e16b6e89172040451dca_b.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/3b673d6335ef6788d1659ee2b6381e97_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/3b673d6335ef6788d1659ee2b6381e97_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/41d652d947a489e056b0179ba137294b_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/41d652d947a489e056b0179ba137294b_m.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/450c9d9f5240f05f73d21fe3ae76f1a6_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/450c9d9f5240f05f73d21fe3ae76f1a6_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/4528283ed249589634546327431667bf_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/4528283ed249589634546327431667bf_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/4d104b6c6a08f7e1a48f4f32c88b1ce2_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/4d104b6c6a08f7e1a48f4f32c88b1ce2_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/51559bbebaa7fd395c271b7b1c8b9f26_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/51559bbebaa7fd395c271b7b1c8b9f26_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/558b3e3d8be209247159ba4f83ab1c02_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/558b3e3d8be209247159ba4f83ab1c02_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/6088e39f2_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/6088e39f2_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/66a689b2c60557eae79d839aaedf48b0_200x112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/66a689b2c60557eae79d839aaedf48b0_200x112.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/6c76223a5aef2f1d29c680a2524de791_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/6c76223a5aef2f1d29c680a2524de791_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/6ceea810748d179f57cac0baa5cf9592_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/6ceea810748d179f57cac0baa5cf9592_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/6cfbfc0c5e3c5ecd8784f7e733a75b4f_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/6cfbfc0c5e3c5ecd8784f7e733a75b4f_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/6fd46860a4b6cbc1e52d676f217ea9fd_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/6fd46860a4b6cbc1e52d676f217ea9fd_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/7412830858217e93f6c5d06f6328cbd5_200x112.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/7412830858217e93f6c5d06f6328cbd5_200x112.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/7986ef6045f2cef8352be5affce5f7d1_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/7986ef6045f2cef8352be5affce5f7d1_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/837b60aa36029a2309974e74de3b62e9_xl.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/837b60aa36029a2309974e74de3b62e9_xl.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/845303838eca4a5b5f03cc3ca994ec28_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/845303838eca4a5b5f03cc3ca994ec28_s.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/845c492813e72b85c6e11cccf8ed0ff8_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/845c492813e72b85c6e11cccf8ed0ff8_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/935f87219_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/935f87219_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/975baaf73fd76f48ce6f05e19b176878_xl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/975baaf73fd76f48ce6f05e19b176878_xl.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/9cfe980ca44e38bd9b0e5c3dee5b0f3e_270x225.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/9cfe980ca44e38bd9b0e5c3dee5b0f3e_270x225.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/9ec0168d1b210d9b8f089e16f521b82b_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/9ec0168d1b210d9b8f089e16f521b82b_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/a3f80f6a6_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/a3f80f6a6_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/a4df63114bd9374a73775e30db1cdd36_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/a4df63114bd9374a73775e30db1cdd36_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/a57fdaff90865eaaef2e87051624862b_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/a57fdaff90865eaaef2e87051624862b_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/aa49b7ceff22ea68ca5f747115cd17af_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/aa49b7ceff22ea68ca5f747115cd17af_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/aadd7b895_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/aadd7b895_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/aadd7b895_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/aadd7b895_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/actioncard-suggested-avatar.c5af416d.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/actioncard-suggested-avatar.c5af416d.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/actioncard-validation@2x.65147043.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/actioncard-validation@2x.65147043.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/b144d91ec_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/b144d91ec_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/b3aadf89405941b05a5ce00fb06f8281_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/b3aadf89405941b05a5ce00fb06f8281_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/b476f1461388bf4907634009904739e6_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/b476f1461388bf4907634009904739e6_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/b4a6228e6810d38e19e491c173af4d5c_m.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/b4a6228e6810d38e19e491c173af4d5c_m.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/b85ddd8aa_xl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/b85ddd8aa_xl.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/badaaf312e59c125928bd1ea2d4b5a51_b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/badaaf312e59c125928bd1ea2d4b5a51_b.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/bb73b7fb48b0cb63aa573415dfe4d0c5_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/bb73b7fb48b0cb63aa573415dfe4d0c5_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/bc93fd5289c13c06fd569bc4f6df821e_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/bc93fd5289c13c06fd569bc4f6df821e_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/be39d110759e68f389b7d2934d7353bc_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/be39d110759e68f389b7d2934d7353bc_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c07f969d8dcc6251ef114fe6b1a9a563_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c07f969d8dcc6251ef114fe6b1a9a563_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c28e9af7f_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c28e9af7f_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c54bb4367803ba590035d2d89d1a84dd_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c54bb4367803ba590035d2d89d1a84dd_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c79247853_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c79247853_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c898060535edfdbe4147d2135c29787f_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c898060535edfdbe4147d2135c29787f_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/c94c92af8_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/c94c92af8_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/cafae465b8ea283498c69ab9757f86ba_xl.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/cafae465b8ea283498c69ab9757f86ba_xl.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/captcha.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/captcha.gif -------------------------------------------------------------------------------- /src/main/resources/static/images/res/cbc5d3c6f333215a1c480cb3b4735b45_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/cbc5d3c6f333215a1c480cb3b4735b45_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/d207854fffc9e0289fbd6bbbb3986988_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/d207854fffc9e0289fbd6bbbb3986988_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/d6842d77b4bda238e0db09217e3d2f8d_270x225.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/d6842d77b4bda238e0db09217e3d2f8d_270x225.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/d822a919d93a761634a67c2022a3f614_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/d822a919d93a761634a67c2022a3f614_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/da8e974dc_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/da8e974dc_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/da8e974dc_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/da8e974dc_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/da8e974dc_s.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/da8e974dc_s.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/df4aa616fdcfbd861c010ff71aaef95c_b.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/df4aa616fdcfbd861c010ff71aaef95c_b.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/e174d6d0c_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/e174d6d0c_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/e6f4caaaa_m.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/e6f4caaaa_m.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/e8757728eb70adeb8ebaa0864874c29d_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/e8757728eb70adeb8ebaa0864874c29d_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/ec6bb3fa05625b848ac4d475ecce35c9_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/ec6bb3fa05625b848ac4d475ecce35c9_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/f1148eb1c7170cabb0a78dad73b590f6_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/f1148eb1c7170cabb0a78dad73b590f6_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/f99201e42237de4fb7f8d5fbf9a2d270_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/f99201e42237de4fb7f8d5fbf9a2d270_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/fa70eff301ba417d4a9d3f55d603a29e_s.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/fa70eff301ba417d4a9d3f55d603a29e_s.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/facfb45ac94f174655695853d4470bac_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/facfb45ac94f174655695853d4470bac_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/fb05f15bf8bffd1590df442ff6ba7812_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/fb05f15bf8bffd1590df442ff6ba7812_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/fb6c4dd60a9f19f5fcd8265395e11f9e_200x112.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/fb6c4dd60a9f19f5fcd8265395e11f9e_200x112.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/fff25000064308791f739149af611439_is.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/fff25000064308791f739149af611439_is.jpg -------------------------------------------------------------------------------- /src/main/resources/static/images/res/hour.3d371c99.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/hour.3d371c99.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/nacl.656ec1c4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/nacl.656ec1c4.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/nk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/nk.png -------------------------------------------------------------------------------- /src/main/resources/static/images/res/weekly.65279d61.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sjr7/wenda/c603e82ed0d73462f4549df67f2995fdf2da21de/src/main/resources/static/images/res/weekly.65279d61.png -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/base/event.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Event = Base.createClass('main.base.Event'); 3 | $.extend(Event, { 4 | on: fOn, 5 | emit: fEmit, 6 | unbind: fUnbind, 7 | unbindAll: fUnbindAll 8 | }); 9 | 10 | function fOn(sName, fCb) { 11 | var that = this; 12 | if (!Base.isString(sName) || !Base.isFunction(fCb)) { 13 | return; 14 | } 15 | 16 | that._cep = that._cep || {}; 17 | that._cep[sName] = that._cep[sName] || []; 18 | that._cep[sName].push(fCb); 19 | } 20 | 21 | function fEmit(sName) { 22 | var that = this; 23 | if (!that._cep || !that._cep[sName]) { 24 | return; 25 | } 26 | 27 | var aArg = [].slice.call(arguments, 1); 28 | $.each(that._cep[sName], function (_, fCb) { 29 | fCb.apply(that, aArg); 30 | }); 31 | } 32 | 33 | function fUnbind(sName, fCb) { 34 | var that = this; 35 | if (!that._cep || !that._cep[sName]) { 36 | return; 37 | } 38 | 39 | if (!fCb) { 40 | that._cep[sName].length = 0; 41 | delete that._cep[sName]; 42 | return; 43 | } 44 | 45 | var oPoll = that._cep; 46 | var aCb = oPoll[sName]; 47 | for (var i = aCb.length - 1; i >= 0; i--) { 48 | if (aCb[i] === fCb) { 49 | aCb.splice(i, 1); 50 | } 51 | } 52 | aCb.length === 0 && (delete oPoll[sName]); 53 | } 54 | 55 | function fUnbindAll() { 56 | var that = this; 57 | var oPoll = that._cep; 58 | $.each(oPoll, function (sKey) { 59 | delete oPoll[sKey]; 60 | }); 61 | } 62 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/base/util.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Util = Base.createClass('main.base.Util'); 3 | $.extend(Util, { 4 | isEmail: fIsEmail 5 | }); 6 | 7 | function fIsEmail(sEmail) { 8 | sEmail = $.trim(sEmail); 9 | return sEmail && /^([a-zA-Z0-9_\.\-\+])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$/.test(sEmail); 10 | } 11 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/component/component.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Component = Base.createClass('main.component.Component'); 3 | var Event = Base.getClass('main.base.Event'); 4 | $.extend(Component, { 5 | _cIndex: 1, 6 | _domQueue: [], 7 | _tpl: '
', 8 | setEvents: fStaticSetEvents 9 | }); 10 | 11 | $.extend(Component.prototype, Event, { 12 | initialize: fInitialize, 13 | render: fRender, 14 | getEl: fGetEl, 15 | html: fHtml, 16 | destroy: fDestroy, 17 | getData: fGetData, 18 | // 禁止滚动 19 | forbidScroll: fForbidScroll, 20 | // 重写emit 21 | emit: fEmit, 22 | // 内部方法 23 | _setCustomEvent: _fSetCustomEvent, 24 | _setDomEvent: _fSetDomEvent 25 | }); 26 | 27 | function fStaticSetEvents() { 28 | var that = this; 29 | var aQueue = Component._domQueue; 30 | var oQueue; 31 | while (aQueue.length) { 32 | oQueue = aQueue.shift(); 33 | oQueue._setDomEvent(); 34 | oQueue.emit('render'); 35 | } 36 | } 37 | 38 | function fInitialize(oConf) { 39 | var that = this; 40 | that.rawConfig = oConf; 41 | that.domId = 'jsCpn' + (Component._cIndex++); 42 | that._setCustomEvent(); 43 | Component._domQueue.push(that); 44 | oConf.renderTo && that.render(); 45 | } 46 | 47 | function fRender() { 48 | var that = this; 49 | var oConf = that.rawConfig; 50 | var oRenderTo = $(oConf.renderTo); 51 | var sRenderBy = oConf.renderBy || 'append'; 52 | var oEl = that.getEl(); 53 | oRenderTo[sRenderBy](oEl); 54 | that._setDomEvent(); 55 | that.emit('render'); 56 | } 57 | 58 | function fGetEl() { 59 | var that = this; 60 | if (that.$el) { 61 | return that.$el; 62 | } 63 | var oEl = $('#' + that.domId); 64 | if (oEl.get(0)) { 65 | that.$el = oEl; 66 | return oEl; 67 | } 68 | 69 | var sHtml = that.html(); 70 | that.$el = $(sHtml); 71 | return that.$el; 72 | } 73 | 74 | function fHtml() { 75 | var that = this; 76 | var oConf = that.rawConfig; 77 | var oConstructor = that.constructor; 78 | var sTpl = oConstructor._tpl || Component._tpl; 79 | var oData = that.getData(that.rawConfig); 80 | var sHtml = Base.tpl(sTpl, oData); 81 | // id 和 class 82 | /* jshint ignore:start */ 83 | sHtml = sHtml.replace(/^(\<\w+)([ \>])/, '$1' + ' id="' + that.domId + '"$2'); 84 | /* jshint ignore:end */ 85 | sHtml = sHtml.replace('class="', 'class="' + (oConf.cls || '') + ' '); 86 | return sHtml; 87 | } 88 | 89 | function fDestroy() { 90 | var that = this; 91 | var oEl = that.getEl(); 92 | oEl.remove(); 93 | that.emit('destroy'); 94 | that.unbindAll(); 95 | } 96 | 97 | function fGetData(oConf) { 98 | return oConf; 99 | } 100 | 101 | function fForbidScroll(oEl, bForbid) { 102 | $(oEl).css('overflow', bForbid === false ? 'auto' : 'hidden'); 103 | } 104 | 105 | function fEmit(sName) { 106 | var that = this; 107 | if (sName === 'render') { 108 | if (that.rendered) { 109 | return; 110 | } 111 | that.rendered = true; 112 | } 113 | Event.emit.apply(that, arguments); 114 | } 115 | 116 | function _fSetCustomEvent() { 117 | var that = this; 118 | if (that._setedCustomEvent) { 119 | return; 120 | } 121 | that._setedCustomEvent = true; 122 | var oConf = that.rawConfig; 123 | var oConstructor = that.constructor; 124 | $.each(oConstructor.listeners, function (_, oEvent) { 125 | oEvent.type === 'custom' && oEvent.name && oEvent.handler && that.on(oEvent.name, oEvent.handler); 126 | }); 127 | $.each(oConf.listeners, function (sName, fCb) { 128 | Base.isFunction(fCb) && that.on(sName, fCb); 129 | }); 130 | } 131 | 132 | function _fSetDomEvent() { 133 | var that = this; 134 | if (that._setedDomEvent) { 135 | return; 136 | } 137 | that._setedDomEvent = true; 138 | var oConf = that.rawConfig; 139 | var oEl = that.getEl(); 140 | var oConstructor = that.constructor; 141 | // 构造器上的事件 142 | $.each(oConstructor.listeners, function (_, oEvent) { 143 | oEvent.type !== 'custom' && _fBind(oEvent.name, oEvent); 144 | }); 145 | // 配置上面的事件 146 | $.each(oConf.listeners, function (sName, oEvent) { 147 | Base.isObject(oEvent) && _fBind(sName, oEvent); 148 | }); 149 | // 删除dom事件队列 150 | for (var i = Component._domQueue.length - 1; i >= 0; i--) { 151 | if (Component._domQueue[i] === that) { 152 | Component._domQueue.splice(i, 1); 153 | } 154 | } 155 | function _fBind(sName, oEvent) { 156 | var aMatch = sName.match(/^(\S+)\s*(.*)$/); 157 | var sEvent = $.trim(aMatch[1]); 158 | var sSelector = $.trim(aMatch[2]); 159 | var fHandler = oEvent.handler; 160 | if (Base.isFunction(fHandler)) { 161 | if (sSelector) { 162 | oEvent.type === 'bind' && oEl.find(sSelector).on(sEvent, Base.bind(fHandler, that)); 163 | oEvent.type !== 'bind' && oEl.on(sEvent, sSelector, Base.bind(fHandler, that)); 164 | } else { 165 | oEl.on(sEvent, Base.bind(fHandler, that)); 166 | } 167 | } 168 | } 169 | } 170 | 171 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/component/popupAdd.js: -------------------------------------------------------------------------------- 1 | /** 2 | var oPopupAdd = new PopupAdd({ 3 | data: 初始数据 4 | ok: Function, 发布成功后的回调 5 | }); 6 | */ 7 | (function (window) { 8 | var PopupAdd = Base.createClass('main.component.PopupAdd'); 9 | var Popup = Base.getClass('main.component.Popup'); 10 | var Component = Base.getClass('main.component.Component'); 11 | var Util = Base.getClass('main.base.Util'); 12 | 13 | Base.mix(PopupAdd, Component, { 14 | _tpl: [ 15 | '
', 16 | '
', 17 | '
', 18 | '', 19 | '
', 20 | '
', 21 | '
', 22 | '
问题说明(可选):
', 23 | '
', 24 | '
', 25 | '
', 26 | '
', 27 | '', 28 | '
', 29 | '
', 30 | '
', 31 | '
', 32 | '
', 33 | '
'].join(''), 34 | listeners: [{ 35 | name: 'render', 36 | type: 'custom', 37 | handler: function () { 38 | var that = this; 39 | var oConf = that.rawConfig; 40 | var oEl = that.getEl(); 41 | that.titleIpt = oEl.find('.js-title'); 42 | that.contentIpt = oEl.find('.js-content'); 43 | // 还原值 44 | oConf.data && that.val(oConf.data); 45 | } 46 | }], 47 | show: fStaticShow 48 | }, { 49 | initialize: fInitialize, 50 | val: fVal 51 | }); 52 | 53 | function fStaticShow(oConf) { 54 | var that = this; 55 | var oAdd = new PopupAdd(oConf); 56 | var bSubmit = false; 57 | var oPopup = new Popup({ 58 | width: 540, 59 | title: '提问', 60 | okTxt: '发布', 61 | content: oAdd.html(), 62 | ok: function () { 63 | var that = this; 64 | var oData = oAdd.val(); 65 | if (!oData.title) { 66 | that.error('请填写标题'); 67 | return true; 68 | } 69 | // 避免重复提交 70 | if (bSubmit) { 71 | return true; 72 | } 73 | bSubmit = true; 74 | // 提交内容 75 | $.ajax({ 76 | url: '/question/add', 77 | type: 'post', 78 | data: oData, 79 | dataType: 'json' 80 | }).done(function (oResult) { 81 | // 未登陆,跳转到登陆页面 82 | if (oResult.code === 999) { 83 | window.location.href = '/reglogin?next=' + window.encodeURIComponent(window.location.href); 84 | } else { 85 | oConf.ok && oConf.ok.call(that); 86 | oAdd.emit('ok'); 87 | } 88 | }).fail(function () { 89 | alert('出现错误,请重试'); 90 | }).always(function () { 91 | bSubmit = false; 92 | }); 93 | // 先不关闭 94 | return true; 95 | }, 96 | listeners: { 97 | destroy: function () { 98 | oAdd.destroy(); 99 | } 100 | } 101 | }); 102 | oAdd._popup = oPopup; 103 | Component.setEvents(); 104 | } 105 | 106 | function fInitialize(oConf) { 107 | var that = this; 108 | delete oConf.renderTo; 109 | PopupAdd.superClass.initialize.apply(that, arguments); 110 | } 111 | 112 | function fVal(oData) { 113 | var that = this; 114 | if (arguments.length === 0) { 115 | return { 116 | title: $.trim(that.titleIpt.val()), 117 | content: $.trim(that.contentIpt.val()) 118 | }; 119 | } else { 120 | oData = oData || {}; 121 | that.titleIpt.val($.tirm(oData.title)); 122 | that.contentIpt.val($.trim(oData.content)); 123 | } 124 | } 125 | 126 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/component/popupMsg.js: -------------------------------------------------------------------------------- 1 | /** 2 | var oPopupAdd = new PopupAdd({ 3 | data: 初始数据 4 | toName: String, 姓名 5 | content: String, 内容 6 | }); 7 | */ 8 | (function (window) { 9 | var PopupMsg = Base.createClass('main.component.PopupMsg'); 10 | var Popup = Base.getClass('main.component.Popup'); 11 | var Component = Base.getClass('main.component.Component'); 12 | var Util = Base.getClass('main.base.Util'); 13 | 14 | Base.mix(PopupMsg, Component, { 15 | _tpl: [ 16 | '
', 17 | '
', 18 | '
发给:
', 19 | '
', 20 | '', 21 | '
', 22 | '
', 23 | '
', 24 | '
内容:
', 25 | '
', 26 | '
', 27 | '
', 28 | '
', 29 | '', 30 | '
', 31 | '
', 32 | '
', 33 | '
', 34 | '
', 35 | '
'].join(''), 36 | listeners: [{ 37 | name: 'render', 38 | type: 'custom', 39 | handler: function () { 40 | var that = this; 41 | var oConf = that.rawConfig; 42 | var oEl = that.getEl(); 43 | that.nameIpt = oEl.find('.js-name'); 44 | that.contentIpt = oEl.find('.js-content'); 45 | // 还原值 46 | oConf.data && that.val(oConf.data); 47 | } 48 | }], 49 | show: fStaticShow 50 | }, { 51 | initialize: fInitialize, 52 | val: fVal 53 | }); 54 | 55 | function fStaticShow(oConf) { 56 | var that = this; 57 | var oAdd = new PopupMsg(oConf); 58 | var bSubmit = false; 59 | var oPopup = new Popup({ 60 | width: 540, 61 | title: '发送私信', 62 | okTxt: '发送', 63 | content: oAdd.html(), 64 | ok: function () { 65 | var that = this; 66 | var oData = oAdd.val(); 67 | if (!oData.toName) { 68 | that.error('请填写姓名'); 69 | return true; 70 | } 71 | if (!oData.content) { 72 | that.error('请填写私信内容'); 73 | return true; 74 | } 75 | // 避免重复提交 76 | if (bSubmit) { 77 | return true; 78 | } 79 | bSubmit = true; 80 | // 提交内容 81 | $.ajax({ 82 | url: '/msg/addMessage', 83 | type: 'post', 84 | data: oData, 85 | dataType: 'json' 86 | }).done(function (oResult) { 87 | // 未登陆,跳转到登陆页面 88 | if (oResult.code === 999) { 89 | window.location.href = '/reglogin?next=' + window.encodeURIComponent(window.location.href); 90 | } else if (oResult.code !== 0) { 91 | that.error(oResult.msg || '出现错误,请重试'); 92 | } else { 93 | oConf.ok && oConf.ok.call(that); 94 | oAdd.emit('ok'); 95 | } 96 | }).fail(function () { 97 | alert('出现错误,请重试'); 98 | }).always(function () { 99 | bSubmit = false; 100 | }); 101 | // 先不关闭 102 | return true; 103 | }, 104 | listeners: { 105 | destroy: function () { 106 | oAdd.destroy(); 107 | } 108 | } 109 | }); 110 | oAdd._popup = oPopup; 111 | Component.setEvents(); 112 | } 113 | 114 | function fInitialize(oConf) { 115 | var that = this; 116 | delete oConf.renderTo; 117 | PopupMsg.superClass.initialize.apply(that, arguments); 118 | } 119 | 120 | function fVal(oData) { 121 | var that = this; 122 | if (arguments.length === 0) { 123 | return { 124 | toName: $.trim(that.nameIpt.val()), 125 | content: $.trim(that.contentIpt.val()) 126 | }; 127 | } else { 128 | oData = oData || {}; 129 | that.nameIpt.val($.tirm(oData.toName)); 130 | that.contentIpt.val($.trim(oData.content)); 131 | } 132 | } 133 | 134 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/site/detail.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Action = Base.getClass('main.util.Action'); 3 | var Business = Base.getClass('main.util.Business'); 4 | 5 | Base.ready({ 6 | initialize: fInitialize, 7 | // 事件代理 8 | events: { 9 | 'click .js-like': fVote, 10 | 'click .js-dislike': fVote 11 | } 12 | }); 13 | 14 | function fInitialize() { 15 | var that = this; 16 | // 点击关注问题 17 | Business.followQuestion({ 18 | countEl: $('.js-user-count'), 19 | listEl: $('.js-user-list') 20 | }); 21 | } 22 | 23 | function fVote(oEvent) { 24 | var that = this; 25 | var oEl = $(oEvent.currentTarget); 26 | var oDv = oEl.closest('div.js-vote'); 27 | var sId = $.trim(oDv.attr('data-id')); 28 | var bLike = oEl.hasClass('js-like'); 29 | if (!sId) { 30 | return; 31 | } 32 | if (that.isVote) { 33 | return; 34 | } 35 | that.isVote = true; 36 | Action[bLike ? 'like' : 'dislike']({ 37 | commentId: sId, 38 | call: function (oResult) { 39 | // 调整样式 40 | oDv.find('.pressed').removeClass('pressed'); 41 | oDv.find(bLike ? '.js-like' : '.js-dislike').addClass('pressed'); 42 | // 更新数量 43 | oDv.closest('div.js-comment').find('span.js-voteCount').html(oResult.msg); 44 | }, 45 | error: function (oResult) { 46 | if (oResult.code === 999) { 47 | alert('请登录后再操作'); 48 | window.location.href = '/reglogin?next=' + window.decodeURIComponent(window.location.href); 49 | } else { 50 | alert('出现错误,请重试'); 51 | } 52 | }, 53 | always: function () { 54 | that.isVote = false; 55 | } 56 | }); 57 | } 58 | 59 | function fUnlike(oEvent) { 60 | var that = this; 61 | var oEl = $(oEvent.currentTarget); 62 | 63 | } 64 | 65 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/site/follow.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Business = Base.getClass('main.util.Business'); 3 | 4 | Base.ready({ 5 | initialize: fInitialize 6 | }); 7 | 8 | function fInitialize() { 9 | Business.followUser(); 10 | } 11 | })(); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/site/home.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var PopupAdd = Base.getClass('main.component.PopupAdd'); 3 | var PopupMsg = Base.getClass('main.component.PopupMsg'); 4 | 5 | Base.ready({ 6 | initialize: fInitialize, 7 | binds: { 8 | 'click #zu-top-add-question': fClickAdd, 9 | 'click #zh-top-nav-count-wrap': fClickMsg 10 | } 11 | }); 12 | 13 | function fInitialize() { 14 | var that = this; 15 | } 16 | 17 | function fClickAdd() { 18 | var that = this; 19 | PopupAdd.show({ 20 | ok: function () { 21 | window.location.replace("/"); 22 | } 23 | }); 24 | } 25 | 26 | function fClickMsg() { 27 | var that = this; 28 | PopupMsg.show({ 29 | ok: function () { 30 | window.location.replace("/msg/list"); 31 | } 32 | }); 33 | } 34 | 35 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/site/profile.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Business = Base.getClass('main.util.Business'); 3 | 4 | Base.ready({ 5 | initialize: fInitialize 6 | }); 7 | 8 | function fInitialize() { 9 | Business.followUser(); 10 | } 11 | })(); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/util/action.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Action = Base.createClass('main.util.Action'); 3 | $.extend(Action, { 4 | like: fLike, 5 | dislike: fDislike, 6 | followUser: fFollowUser, 7 | unFollowUser: fUnFollowUser, 8 | followQuestion: fFollowQuestion, 9 | unFollowQuestion: fUnFollowQuestion, 10 | post: fPost 11 | }); 12 | 13 | /** 14 | * 喜欢 15 | * @param {Object} oConf 16 | * @param {String} oConf.commentId 对象id 17 | * @param {Function} oConf.call 成功回调 18 | * @param {Function} oConf.error 失败回调 19 | * @param {Function} oConf.always 操作的回调 20 | */ 21 | function fLike(oConf) { 22 | var that = this; 23 | that.post({ 24 | url: '/like', 25 | data: {commentId: oConf.commentId}, 26 | call: oConf.call, 27 | error: oConf.error, 28 | always: oConf.always 29 | }); 30 | } 31 | 32 | /** 33 | * 不喜欢 34 | * @param {Object} oConf 35 | * @param {String} oConf.commentId 对象id 36 | * @param {Function} oConf.call 成功回调 37 | * @param {Function} oConf.error 失败回调 38 | * @param {Function} oConf.always 操作的回调 39 | */ 40 | function fDislike(oConf) { 41 | var that = this; 42 | that.post({ 43 | url: '/dislike', 44 | data: {commentId: oConf.commentId}, 45 | call: oConf.call, 46 | error: oConf.error, 47 | always: oConf.always 48 | }); 49 | } 50 | 51 | /** 52 | * 关注用户 53 | * @param {Object} oConf 54 | * @param {String} oConf.userId 用户id 55 | * @param {Function} oConf.call 成功回调 56 | * @param {Function} oConf.error 失败回调 57 | * @param {Function} oConf.always 操作的回调 58 | */ 59 | function fFollowUser(oConf) { 60 | var that = this; 61 | that.post({ 62 | url: '/followUser', 63 | data: {userId: oConf.userId}, 64 | call: oConf.call, 65 | error: oConf.error, 66 | always: oConf.always 67 | }); 68 | } 69 | 70 | /** 71 | * 取消关注用户 72 | * @param {Object} oConf 73 | * @param {String} oConf.userId 用户id 74 | * @param {Function} oConf.call 成功回调 75 | * @param {Function} oConf.error 失败回调 76 | * @param {Function} oConf.always 操作的回调 77 | */ 78 | function fUnFollowUser(oConf) { 79 | var that = this; 80 | that.post({ 81 | url: '/unfollowUser', 82 | data: {userId: oConf.userId}, 83 | call: oConf.call, 84 | error: oConf.error, 85 | always: oConf.always 86 | }); 87 | } 88 | 89 | /** 90 | * 关注问题 91 | * @param {Object} oConf 92 | * @param {String} oConf.questionId 问题id 93 | * @param {Function} oConf.call 成功回调 94 | * @param {Function} oConf.error 失败回调 95 | * @param {Function} oConf.always 操作的回调 96 | */ 97 | function fFollowQuestion(oConf) { 98 | var that = this; 99 | that.post({ 100 | url: '/followQuestion', 101 | data: {questionId: oConf.questionId}, 102 | call: oConf.call, 103 | error: oConf.error, 104 | always: oConf.always 105 | }); 106 | } 107 | 108 | /** 109 | * 取消关注问题 110 | * @param {Object} oConf 111 | * @param {String} oConf.questionId 问题id 112 | * @param {Function} oConf.call 成功回调 113 | * @param {Function} oConf.error 失败回调 114 | * @param {Function} oConf.always 操作的回调 115 | */ 116 | function fUnFollowQuestion(oConf) { 117 | var that = this; 118 | that.post({ 119 | url: '/unfollowQuestion', 120 | data: {questionId: oConf.questionId}, 121 | call: oConf.call, 122 | error: oConf.error, 123 | always: oConf.always 124 | }); 125 | } 126 | 127 | /** 128 | * 简单的 ajax 请求封装 129 | * @param {Object} oConf 130 | * @param {String} oConf.method 请求类型 131 | * @param {String} oConf.url 请求连接 132 | * @param {Object} oConf.data 发送参数 133 | * @param {Function} oConf.call 成功回调 134 | * @param {Function} oConf.error 失败回调 135 | * @param {Function} oConf.always 操作的回调 136 | */ 137 | function fPost(oConf) { 138 | var that = this; 139 | $.ajax({ 140 | method: oConf.method || 'POST', 141 | url: oConf.url, 142 | dataType: 'json', 143 | data: oConf.data 144 | }).done(function (oResult) { 145 | var nCode = oResult.code; 146 | if (oResult.code === 999) { 147 | // 未登录 148 | alert('未登录'); 149 | window.location.href = '/reglogin?next=' + window.encodeURI(window.location.href); 150 | return; 151 | } 152 | nCode === 0 && oConf.call && oConf.call(oResult); 153 | nCode !== 0 && oConf.error && oConf.error(oResult); 154 | }).fail(oConf.error).always(oConf.always); 155 | } 156 | 157 | })(window); -------------------------------------------------------------------------------- /src/main/resources/static/scripts/main/util/business.js: -------------------------------------------------------------------------------- 1 | (function (window, undefined) { 2 | var Business = Base.createClass('main.util.Business'); 3 | var Action = Base.getClass('main.util.Action'); 4 | 5 | $.extend(Business, { 6 | followUser: fFollowUser, 7 | followQuestion: fFollowQuestion 8 | }); 9 | 10 | function fFollowUser() { 11 | $(document).on('click', '.js-follow-user', function (oEvent) { 12 | var oEl = $(oEvent.currentTarget); 13 | var sId = $.trim(oEl.attr('data-id')); 14 | if (!sId) { 15 | return; 16 | } 17 | // 禁止频繁点击 18 | if (oEl.attr('data-limit')) { 19 | return; 20 | } 21 | oEl.attr('data-limit', '1'); 22 | var bFollow = oEl.attr('data-status') === '1'; 23 | Action[bFollow ? 'unFollowUser' : 'followUser']({ 24 | userId: sId, 25 | call: function (oResult) { 26 | // 修改标记位 27 | oEl.attr('data-status', bFollow ? '0' : '1'); 28 | // 按钮颜色 29 | oEl.removeClass('zg-btn-follow zg-btn-unfollow').addClass(bFollow ? 'zg-btn-follow' : 'zg-btn-unfollow'); 30 | // 文字 31 | oEl.html(bFollow ? '关注' : '取消关注'); 32 | }, 33 | error: function (oResult) { 34 | alert('出现错误,请重试'); 35 | }, 36 | always: function () { 37 | oEl.removeAttr('data-limit'); 38 | } 39 | }); 40 | }); 41 | } 42 | 43 | function fFollowQuestion(oConf) { 44 | var that = this; 45 | var oCountEl = $(oConf.countEl); 46 | var oListEl = $(oConf.listEl); 47 | $(document).on('click', '.js-follow-question', function (oEvent) { 48 | var oEl = $(oEvent.currentTarget); 49 | var sId = $.trim(oEl.attr('data-id')); 50 | if (!sId) { 51 | return; 52 | } 53 | // 禁止频繁点击 54 | if (oEl.attr('data-limit')) { 55 | return; 56 | } 57 | oEl.attr('data-limit', '1'); 58 | var bFollow = oEl.attr('data-status') === '1'; 59 | Action[bFollow ? 'unFollowQuestion' : 'followQuestion']({ 60 | questionId: sId, 61 | call: function (oResult) { 62 | // 修改标记位 63 | oEl.attr('data-status', bFollow ? '0' : '1'); 64 | // 按钮颜色 65 | oEl.removeClass('zg-btn-white zg-btn-green').addClass(bFollow ? 'zg-btn-green' : 'zg-btn-white'); 66 | // 文字 67 | oEl.html(bFollow ? '关注问题' : '取消关注'); 68 | // 修改数量 69 | oCountEl.html(oResult.count); 70 | if (bFollow) { 71 | // 移除用户 72 | oListEl.find('.js-user-' + oResult.id).remove(); 73 | } else { 74 | // 显示用户 75 | oListEl.prepend(''); 76 | } 77 | }, 78 | error: function (oResult) { 79 | alert('出现错误,请重试'); 80 | }, 81 | always: function () { 82 | oEl.removeAttr('data-limit'); 83 | } 84 | }); 85 | }); 86 | } 87 | })(window); -------------------------------------------------------------------------------- /src/main/resources/templates/error.html: -------------------------------------------------------------------------------- 1 | 错误页面 -------------------------------------------------------------------------------- /src/main/resources/templates/followees.html: -------------------------------------------------------------------------------- 1 | #parse("header.html") 2 | 3 | 4 |
5 |
6 |
7 |
8 | 9 | ${curUser.name} 关注了 ${followeeCount} 人 10 | 11 |
12 |
13 |
14 |
15 | #foreach($vo in $followees) 16 |
17 | #if($vo.followed) 18 |
19 | 21 |
22 | #else 23 |
24 | 26 |
27 | #end 28 | 29 | 30 | 31 |
32 |

${vo.user.name}

33 | 34 | 35 | 44 |
45 |
46 | #end 47 |
48 | 更多 49 |
50 |
51 |
52 |
53 |
54 | #parse("js.html") 55 | 56 | #parse("footer.html") -------------------------------------------------------------------------------- /src/main/resources/templates/followers.html: -------------------------------------------------------------------------------- 1 | #parse("header.html") 2 | 3 | 4 |
5 |
6 |
7 |
8 | 9 | ${curUser.name} 粉丝 ${followerCount} 人 10 | 11 |
12 |
13 |
14 |
15 | #foreach($vo in $followers) 16 |
17 | #if($vo.followed) 18 |
19 | 21 |
22 | #else 23 |
24 | 26 |
27 | #end 28 | 29 | 30 | 31 |
32 |

${vo.user.name}

33 | 34 | 35 | 44 |
45 |
46 | #end 47 |
48 | 更多 49 |
50 |
51 |
52 |
53 |
54 | #parse("js.html") 55 | 56 | #parse("footer.html") -------------------------------------------------------------------------------- /src/main/resources/templates/footer.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/templates/js.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/resources/templates/letter.html: -------------------------------------------------------------------------------- 1 | #parse("header.html") 2 | 3 |
4 |
5 | 46 | 47 |
48 |
49 | #parse("footer.html") -------------------------------------------------------------------------------- /src/main/resources/templates/letterDetail.html: -------------------------------------------------------------------------------- 1 | #parse("header.html") 2 | 3 | 4 |
5 |
6 |
    7 | #foreach($message in $messages) 8 |
  • 9 | 10 | 头像 11 | 12 |
    13 |
    14 |
    15 |
    16 |

    $date.format('yyyy-MM-dd HH:mm:ss', $!message.message.createDate)

    17 | 18 |
    19 |

    20 | $!message.message.content 21 |

    22 |
    23 |
    24 |
  • 25 | #end 26 |
27 | 28 |
29 |
30 | 31 | 32 | #parse("footer.html") -------------------------------------------------------------------------------- /src/main/resources/templates/login.html: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 问答 - 与世界分享你的知识、经验和见解 11 | 12 | 13 | 14 | 15 | 16 |
17 |
18 |
19 |

20 |

21 | #if($msg) 22 | $!{msg} 23 | #else 24 | 与世界分享你的知识、经验和见解 25 | #end 26 |

27 |
28 |
29 | 53 |
54 |
55 |
56 | 57 | -------------------------------------------------------------------------------- /src/main/resources/templates/mails/login_exception.html: -------------------------------------------------------------------------------- 1 | 你好$username,你的登陆有问题! 2 | -------------------------------------------------------------------------------- /src/main/resources/toolbox.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | date 4 | application 5 | org.apache.velocity.tools.generic.DateTool 6 | 7 | --------------------------------------------------------------------------------