├── src
├── main
│ ├── resources
│ │ ├── sensitive-words.txt
│ │ ├── static
│ │ │ ├── css
│ │ │ │ ├── login.css
│ │ │ │ ├── letter.css
│ │ │ │ ├── discuss-detail.css
│ │ │ │ └── global.css
│ │ │ ├── img
│ │ │ │ ├── 404.png
│ │ │ │ ├── error.png
│ │ │ │ └── captcha.png
│ │ │ ├── js
│ │ │ │ ├── register.js
│ │ │ │ ├── letter.js
│ │ │ │ ├── profile.js
│ │ │ │ ├── index.js
│ │ │ │ ├── setting.js
│ │ │ │ ├── global.js
│ │ │ │ └── discuss.js
│ │ │ └── html
│ │ │ │ └── student.html
│ │ ├── document
│ │ │ └── image
│ │ │ │ └── index.jpg
│ │ ├── application-develop.properties
│ │ ├── application-produce.properties
│ │ ├── application.properties
│ │ ├── templates
│ │ │ ├── demo
│ │ │ │ └── view.html
│ │ │ ├── mail
│ │ │ │ ├── demo.html
│ │ │ │ ├── forget.html
│ │ │ │ └── activation.html
│ │ │ └── error
│ │ │ │ ├── 404.html
│ │ │ │ └── 500.html
│ │ ├── mapper
│ │ │ ├── comment-mapper.xml
│ │ │ ├── user-mapper.xml
│ │ │ ├── discusspost-mapper.xml
│ │ │ └── message-mapper.xml
│ │ ├── logback-spring-produce.xml
│ │ ├── logback-spring.xml
│ │ └── logback-spring-develop.xml
│ └── java
│ │ └── com
│ │ └── nowcoder
│ │ └── community
│ │ ├── dao
│ │ ├── AlphaDao.java
│ │ ├── AlphaDaoHibernateImpl.java
│ │ ├── AlphaDaoMyBatisImpl.java
│ │ ├── CommentMapper.java
│ │ ├── elasticsearch
│ │ │ └── DiscussPostRepository.java
│ │ ├── UserMapper.java
│ │ ├── DiscussPostMapper.java
│ │ ├── LoginTicketMapper.java
│ │ └── MessageMapper.java
│ │ ├── config
│ │ ├── AlphaConfig.java
│ │ ├── ThreadPoolConfig.java
│ │ ├── WkConfig.java
│ │ ├── RedisConfig.java
│ │ ├── KaptchaConfig.java
│ │ ├── WebMvcConfig.java
│ │ ├── QuartzConfig.java
│ │ └── SecurityConfig.java
│ │ ├── annotation
│ │ └── LoginRequired.java
│ │ ├── util
│ │ ├── HostHolder.java
│ │ ├── CookieUtil.java
│ │ ├── MailClient.java
│ │ ├── CommunityUtil.java
│ │ ├── CommunityConstant.java
│ │ ├── RedisKeyUtil.java
│ │ └── SensitiveFilter.java
│ │ ├── quartz
│ │ ├── AlphaJob.java
│ │ └── PostScoreRefreshJob.java
│ │ ├── CommunityApplication.java
│ │ ├── CommunityServletInitializer.java
│ │ ├── event
│ │ └── EventProducer.java
│ │ ├── actuator
│ │ └── DatabaseEndpoint.java
│ │ ├── controller
│ │ ├── interceptor
│ │ │ ├── AlphaInterceptor.java
│ │ │ ├── DataInterceptor.java
│ │ │ ├── LoginRequiredInterceptor.java
│ │ │ ├── MessageInterceptor.java
│ │ │ └── LoginTicketInterceptor.java
│ │ ├── advice
│ │ │ └── ExceptionAdvice.java
│ │ ├── DataController.java
│ │ ├── SearchController.java
│ │ ├── HomeController.java
│ │ ├── LikeController.java
│ │ ├── CommentController.java
│ │ ├── ShareController.java
│ │ └── FollowController.java
│ │ ├── entity
│ │ ├── LoginTicket.java
│ │ ├── Event.java
│ │ ├── Message.java
│ │ ├── Page.java
│ │ ├── Comment.java
│ │ ├── User.java
│ │ └── DiscussPost.java
│ │ ├── aspect
│ │ └── ServiceLogAspect.java
│ │ └── service
│ │ ├── CommentService.java
│ │ ├── MessageService.java
│ │ ├── LikeService.java
│ │ ├── DataService.java
│ │ ├── AlphaService.java
│ │ └── DiscussPostService.java
└── test
│ └── java
│ └── com
│ └── nowcoder
│ └── community
│ ├── LoggerTests.java
│ ├── QuartzTests.java
│ ├── MailTests.java
│ ├── KafkaTests.java
│ ├── CaffeineTests.java
│ ├── SpringBootTests.java
│ ├── CommunityApplicationTests.java
│ ├── ThreadPoolTests.java
│ ├── MapperTests.java
│ └── RedisTest.java
├── .images
├── arc.png
├── index.png
└── message.png
├── .gitignore
├── README.md
├── sql
└── init_schema.sql
└── pom.xml
/src/main/resources/sensitive-words.txt:
--------------------------------------------------------------------------------
1 | 赌博
2 | 嫖娼
3 | 吸毒
4 | 开票
--------------------------------------------------------------------------------
/.images/arc.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/.images/arc.png
--------------------------------------------------------------------------------
/.images/index.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/.images/index.png
--------------------------------------------------------------------------------
/.images/message.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/.images/message.png
--------------------------------------------------------------------------------
/src/main/resources/static/css/login.css:
--------------------------------------------------------------------------------
1 | .main .container {
2 | width: 720px;
3 | }
4 |
--------------------------------------------------------------------------------
/src/main/resources/static/img/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/static/img/404.png
--------------------------------------------------------------------------------
/src/main/resources/static/img/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/static/img/error.png
--------------------------------------------------------------------------------
/src/main/resources/static/img/captcha.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/static/img/captcha.png
--------------------------------------------------------------------------------
/src/main/resources/document/image/index.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/document/image/index.jpg
--------------------------------------------------------------------------------
/src/main/resources/application-develop.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/application-develop.properties
--------------------------------------------------------------------------------
/src/main/resources/application-produce.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/cosen1024/community/HEAD/src/main/resources/application-produce.properties
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/AlphaDao.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | public interface AlphaDao {
4 |
5 | String select();
6 |
7 | }
8 |
--------------------------------------------------------------------------------
/src/main/resources/application.properties:
--------------------------------------------------------------------------------
1 | #profile
2 | spring.profiles.active=develop
3 |
4 | # logback
5 | logging.cofig=classpath:logback-spring-${spring.profiles.active}.xml
--------------------------------------------------------------------------------
/src/main/resources/static/css/letter.css:
--------------------------------------------------------------------------------
1 | .main .nav .badge {
2 | position: absolute;
3 | top: -3px;
4 | left: 68px;
5 | }
6 |
7 | .main .media .badge {
8 | position: absolute;
9 | top: 12px;
10 | left: -3px;
11 | }
12 |
13 | .toast {
14 | max-width: 100%;
15 | width: 80%;
16 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/demo/view.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Teacher
6 |
7 |
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/src/main/resources/templates/mail/demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 邮件示例
6 |
7 |
8 | 欢迎你, !
9 |
10 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/AlphaDaoHibernateImpl.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import org.springframework.stereotype.Repository;
4 |
5 | @Repository("alphaHibernate")
6 | public class AlphaDaoHibernateImpl implements AlphaDao {
7 | @Override
8 | public String select() {
9 | return "Hibernate";
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/resources/static/css/discuss-detail.css:
--------------------------------------------------------------------------------
1 | .content {
2 | font-size: 16px;
3 | line-height: 2em;
4 | }
5 |
6 | .replyform textarea {
7 | width: 100%;
8 | height: 200px;
9 | }
10 |
11 | .floor {
12 | background: #dcdadc;
13 | padding: 4px 12px;
14 | border-radius: 3px;
15 | font-size: 14px;
16 | }
17 |
18 | .input-size {
19 | width: 100%;
20 | height: 35px;
21 | }
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/AlphaDaoMyBatisImpl.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import org.springframework.context.annotation.Primary;
4 | import org.springframework.stereotype.Repository;
5 |
6 | @Repository
7 | @Primary
8 | public class AlphaDaoMyBatisImpl implements AlphaDao{
9 | @Override
10 | public String select() {
11 | return "MyBatis";
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | HELP.md
2 | /target/
3 | !.mvn/wrapper/maven-wrapper.jar
4 | ### STS ###
5 | .apt_generated
6 | .classpath
7 | .factorypath
8 | .project
9 | .settings
10 | .springBeans
11 | .sts4-cache
12 | ### IntelliJ IDEA ###
13 | .idea
14 | *.iws
15 | *.iml
16 | *.ipr
17 |
18 | ### NetBeans ###
19 | /nbproject/private/
20 | /nbbuild/
21 | /dist/
22 | /nbdist/
23 | /.nb-gradle/
24 | /build/
25 |
26 | ### VS Code ###
27 | .vscode/
28 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/register.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("form").submit(check_data);
3 | $("input").focus(clear_error);
4 | });
5 |
6 | function check_data() {
7 | var pwd1 = $("#password").val();
8 | var pwd2 = $("#confirm-password").val();
9 | if(pwd1 != pwd2) {
10 | $("#confirm-password").addClass("is-invalid");
11 | return false;
12 | }
13 | return true;
14 | }
15 |
16 | function clear_error() {
17 | $(this).removeClass("is-invalid");
18 | }
--------------------------------------------------------------------------------
/src/main/resources/templates/mail/forget.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 牛客网-忘记密码
7 |
8 |
9 |
10 |
11 | xxx@xxx.com, 您好!
12 |
13 |
14 | 您正在找回牛客账号的密码, 本次操作的验证码为 u5s6dt ,
15 | 有效时间5分钟, 请您及时进行操作!
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/AlphaConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 |
6 | import java.text.SimpleDateFormat;
7 |
8 | @Configuration
9 | public class AlphaConfig {
10 |
11 | @Bean
12 | public SimpleDateFormat simpleDateFormat() {
13 | return new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
14 | }
15 |
16 | }
17 |
--------------------------------------------------------------------------------
/src/main/resources/templates/mail/activation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | 牛客网-激活账号
7 |
8 |
9 |
10 |
11 | xxx@xxx.com, 您好!
12 |
13 |
14 | 您正在注册牛客网, 这是一封激活邮件, 请点击
15 | 此链接,
16 | 激活您的牛客账号!
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/main/resources/static/html/student.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 增加学生
6 |
7 |
8 |
9 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/CommentMapper.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import com.nowcoder.community.entity.Comment;
4 | import org.apache.ibatis.annotations.Mapper;
5 |
6 | import java.util.List;
7 |
8 | @Mapper
9 | public interface CommentMapper {
10 |
11 | List selectCommentsByEntity(int entityType, int entityId, int offset, int limit);
12 |
13 | int selectCountByEntity(int entityType, int entityId);
14 |
15 | int insertComment(Comment comment);
16 |
17 | Comment selectCommentById(int id);
18 | }
19 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/annotation/LoginRequired.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.annotation;
2 |
3 | import java.lang.annotation.ElementType;
4 | import java.lang.annotation.Retention;
5 | import java.lang.annotation.RetentionPolicy;
6 | import java.lang.annotation.Target;
7 |
8 | /**
9 | * @author coolsen
10 | * @version 1.0.0
11 | * @ClassName LoginRequired.java
12 | * @Description 拦截器注解
13 | * @createTime 4/30/2020 7:24 PM
14 | */
15 |
16 | @Target(ElementType.METHOD)
17 | @Retention(RetentionPolicy.RUNTIME)
18 | public @interface LoginRequired {
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/ThreadPoolConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import org.springframework.context.annotation.Configuration;
4 | import org.springframework.scheduling.annotation.EnableAsync;
5 | import org.springframework.scheduling.annotation.EnableScheduling;
6 |
7 | /**
8 | * @author coolsen
9 | * @version 1.0.0
10 | * @ClassName ThreadPoolConfig.java
11 | * @Description Spring 线程池 Config
12 | * @createTime 2020/5/21 10:21
13 | */
14 |
15 | @Configuration
16 | @EnableScheduling
17 | @EnableAsync
18 | public class ThreadPoolConfig {
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/util/HostHolder.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | import com.nowcoder.community.entity.User;
4 | import org.springframework.stereotype.Component;
5 |
6 | /**
7 | * 持有用户信息,用于代替session对象.
8 | */
9 | @Component
10 | public class HostHolder {
11 |
12 | private ThreadLocal users = new ThreadLocal<>();
13 |
14 | public void setUser(User user) {
15 | users.set(user);
16 | }
17 |
18 | public User getUser() {
19 | return users.get();
20 | }
21 |
22 | public void clear() {
23 | users.remove();
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/elasticsearch/DiscussPostRepository.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao.elasticsearch;
2 |
3 | import com.nowcoder.community.entity.DiscussPost;
4 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository;
5 | import org.springframework.stereotype.Repository;
6 |
7 | /**
8 | * @author coolsen
9 | * @version 1.0.0
10 | * @ClassName DiscussPostRepository.java
11 | * @Description DiscussPost Repository
12 | * @createTime 2020/5/19 14:14
13 | */
14 |
15 | @Repository
16 | public interface DiscussPostRepository extends ElasticsearchRepository {
17 | }
18 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/quartz/AlphaJob.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.quartz;
2 |
3 | import org.quartz.Job;
4 | import org.quartz.JobExecutionContext;
5 | import org.quartz.JobExecutionException;
6 |
7 | /**
8 | * @author coolsen
9 | * @version 1.0.0
10 | * @ClassName AlphaJob.java
11 | * @Description demo job
12 | * @createTime 2020/5/21 10:44
13 | */
14 | public class AlphaJob implements Job {
15 | @Override
16 | public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
17 | System.out.println(Thread.currentThread().getName() + ": execute a quartz job.");
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/UserMapper.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import com.nowcoder.community.entity.User;
4 | import org.apache.ibatis.annotations.Mapper;
5 | import org.springframework.stereotype.Repository;
6 |
7 |
8 | @Mapper
9 | public interface UserMapper {
10 |
11 | User selectById(int id);
12 |
13 | User selectByName(String username);
14 |
15 | User selectByEmail(String email);
16 |
17 | int insertUser(User user);
18 |
19 | int updateStatus(int id, int status);
20 |
21 | int updateHeader(int id, String headerUrl);
22 |
23 | int updatePassword(int id, String password);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/CommunityApplication.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import org.springframework.boot.SpringApplication;
4 | import org.springframework.boot.autoconfigure.SpringBootApplication;
5 |
6 | import javax.annotation.PostConstruct;
7 |
8 | @SpringBootApplication
9 | public class CommunityApplication {
10 |
11 | @PostConstruct
12 | public void init(){
13 | // 解决netty启动冲突问题
14 | // see Netty4Utils.setAvailableProcessors()
15 | System.setProperty("es.set.netty.runtime.available.processors", "false");
16 | }
17 | public static void main(String[] args) {
18 | SpringApplication.run(CommunityApplication.class, args);
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/CommunityServletInitializer.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import org.springframework.boot.builder.SpringApplicationBuilder;
4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
5 |
6 | /**
7 | * @author coolsen
8 | * @version 1.0.0
9 | * @ClassName CommunityServeltInitalizer.java
10 | * @Description 自定义tomcat 启动入口
11 | * @createTime 2020/6/25 20:39
12 | */
13 | public class CommunityServletInitializer extends SpringBootServletInitializer {
14 |
15 | @Override
16 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
17 | return builder.sources(CommunityApplication.class);
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/util/CookieUtil.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | import javax.servlet.http.Cookie;
4 | import javax.servlet.http.HttpServletRequest;
5 |
6 | public class CookieUtil {
7 |
8 | public static String getValue(HttpServletRequest request, String name) {
9 | if (request == null || name == null) {
10 | throw new IllegalArgumentException("参数为空!");
11 | }
12 |
13 | Cookie[] cookies = request.getCookies();
14 | if (cookies != null) {
15 | for (Cookie cookie : cookies) {
16 | if (cookie.getName().equals(name)) {
17 | return cookie.getValue();
18 | }
19 | }
20 | }
21 |
22 | return null;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/letter.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("#sendBtn").click(send_letter);
3 | $(".close").click(delete_msg);
4 | });
5 |
6 | function send_letter() {
7 | $("#sendModal").modal("hide");
8 |
9 | var toName = $("#recipient-name").val();
10 | var content = $("#message-text").val();
11 | $.post(
12 | CONTEXT_PATH + "/letter/send",
13 | {"toName":toName,"content":content},
14 | function(data) {
15 | data = $.parseJSON(data);
16 | if(data.code == 0) {
17 | $("#hintBody").text("发送成功!");
18 | } else {
19 | $("#hintBody").text(data.msg);
20 | }
21 |
22 | $("#hintModal").modal("show");
23 | setTimeout(function(){
24 | $("#hintModal").modal("hide");
25 | location.reload();
26 | }, 2000);
27 | }
28 | );
29 | }
30 |
31 | function delete_msg() {
32 | // TODO 删除数据
33 | $(this).parents(".media").remove();
34 | }
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/event/EventProducer.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.event;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import com.nowcoder.community.entity.Event;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.kafka.core.KafkaTemplate;
7 | import org.springframework.stereotype.Component;
8 |
9 | /**
10 | * @author coolsen
11 | * @version 1.0.0
12 | * @ClassName EventProducer.java
13 | * @Description 事件生产者
14 | * @createTime 2020/5/14 21:32
15 | */
16 |
17 | @Component
18 | public class EventProducer {
19 |
20 | @Autowired
21 | private KafkaTemplate kafkaTemplate;
22 |
23 | // 处理事件
24 | public void fireEvent(Event event){
25 | // 将事件发送到指定主题,其中把内容转换为json对象,消费者受到json后,可以将json转换成Event
26 | kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
27 | }
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/DiscussPostMapper.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import com.nowcoder.community.entity.DiscussPost;
4 | import org.apache.ibatis.annotations.Mapper;
5 | import org.apache.ibatis.annotations.Param;
6 |
7 | import java.util.List;
8 |
9 | @Mapper
10 | public interface DiscussPostMapper {
11 |
12 | List selectDiscussPosts(int userId, int offset, int limit,int orderMode);
13 |
14 | // @Param注解用于给参数取别名,
15 | // 如果只有一个参数,并且在里使用,则必须加别名.
16 | int selectDiscussPostRows(@Param("userId") int userId);
17 |
18 | int insertDiscussPost(DiscussPost discussPost);
19 |
20 | DiscussPost selectDiscussPostById(int id);
21 |
22 | int updateCommentCount(int id, int commentCount);
23 |
24 | int updateType(int id, int type);
25 |
26 | int updateStatus(int id, int status);
27 |
28 | int updateScore(int id, double score);
29 | }
30 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/profile.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $(".follow-btn").click(follow);
3 | });
4 |
5 | function follow() {
6 | var btn = this;
7 | if($(btn).hasClass("btn-info")) {
8 | // 关注TA
9 | $.post(
10 | CONTEXT_PATH + "/follow",
11 | {"entityType":3,"entityId":$(btn).prev().val()},
12 | function(data) {
13 | data = $.parseJSON(data);
14 | if(data.code == 0) {
15 | window.location.reload();
16 | } else {
17 | alert(data.msg);
18 | }
19 | }
20 | );
21 | // $(btn).text("已关注").removeClass("btn-info").addClass("btn-secondary");
22 | } else {
23 | // 取消关注
24 | $.post(
25 | CONTEXT_PATH + "/unfollow",
26 | {"entityType":3,"entityId":$(btn).prev().val()},
27 | function(data) {
28 | data = $.parseJSON(data);
29 | if(data.code == 0) {
30 | window.location.reload();
31 | } else {
32 | alert(data.msg);
33 | }
34 | }
35 | );
36 | //$(btn).text("关注TA").removeClass("btn-secondary").addClass("btn-info");
37 | }
38 | }
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/LoggerTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.slf4j.Logger;
6 | import org.slf4j.LoggerFactory;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 |
11 | /**
12 | * @author shuaisen ma
13 | * @version 1.0.0
14 | * @ClassName LoggerTests.java
15 | * @Description Log test
16 | * @createTime 2020年04月28日 13:14:00
17 | */
18 | @RunWith(SpringRunner.class)
19 | @SpringBootTest
20 | @ContextConfiguration(classes = CommunityApplication.class)
21 | public class LoggerTests {
22 | private static final Logger logger= LoggerFactory.getLogger(LoggerTests.class);
23 |
24 | @Test
25 | public void testLogger(){
26 | logger.debug("debug log");
27 | logger.info("info log");
28 | logger.warn("warn log");
29 | logger.error("error log");
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/WkConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Value;
6 | import org.springframework.context.annotation.Configuration;
7 |
8 | import javax.annotation.PostConstruct;
9 | import java.io.File;
10 |
11 | /**
12 | * @author coolsen
13 | * @version 1.0.0
14 | * @ClassName WkConfig.java
15 | * @Description wkhtmltopdf Config,生成图片
16 | * @createTime 2020/5/21 19:43
17 | */
18 |
19 | @Configuration
20 | public class WkConfig {
21 |
22 | private static final Logger logger = LoggerFactory.getLogger(WkConfig.class);
23 |
24 | @Value("${wk.image.storage}")
25 | private String wkImageStorage;
26 |
27 | @PostConstruct
28 | public void init() {
29 | // 创建WK图片目录
30 | File file = new File(wkImageStorage);
31 | if (!file.exists()) {
32 | file.mkdir();
33 | logger.info("创建WK图片目录: " + wkImageStorage);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/QuartzTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import org.junit.Test;
4 | import org.junit.runner.RunWith;
5 | import org.quartz.JobKey;
6 | import org.quartz.Scheduler;
7 | import org.quartz.SchedulerException;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.boot.test.context.SpringBootTest;
10 | import org.springframework.test.context.ContextConfiguration;
11 | import org.springframework.test.context.junit4.SpringRunner;
12 |
13 | @RunWith(SpringRunner.class)
14 | @SpringBootTest
15 | @ContextConfiguration(classes = CommunityApplication.class)
16 | public class QuartzTests {
17 |
18 | @Autowired
19 | private Scheduler scheduler;
20 |
21 | // 删除任务
22 | @Test
23 | public void testDeleteJob() {
24 | try {
25 | boolean result = scheduler.deleteJob(new JobKey("alphaJob", "alphaJobGroup"));
26 | System.out.println(result);
27 | } catch (SchedulerException e) {
28 | e.printStackTrace();
29 | }
30 | }
31 |
32 | }
33 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/index.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("#publishBtn").click(publish);
3 | });
4 |
5 | function publish() {
6 | $("#publishModal").modal("hide");
7 |
8 | // 发送AJAX请求之前,将CSRF令牌设置到请求的消息头中.
9 | // var token = $("meta[name='_csrf']").attr("content");
10 | // var header = $("meta[name='_csrf_header']").attr("content");
11 | // $(document).ajaxSend(function(e, xhr, options){
12 | // xhr.setRequestHeader(header, token);
13 | // });
14 |
15 | // 获取标题和内容
16 | var title = $("#recipient-name").val();
17 | var content = $("#message-text").val();
18 | // 发送异步请求(POST)
19 | $.post(
20 | CONTEXT_PATH + "/discuss/add",
21 | {"title":title,"content":content},
22 | function(data) {
23 | data = $.parseJSON(data);
24 | // 在提示框中显示返回消息
25 | $("#hintBody").text(data.msg);
26 | // 显示提示框
27 | $("#hintModal").modal("show");
28 | // 2秒后,自动隐藏提示框
29 | setTimeout(function(){
30 | $("#hintModal").modal("hide");
31 | // 刷新页面
32 | if(data.code == 0) {
33 | window.location.reload();
34 | }
35 | }, 2000);
36 | }
37 | );
38 |
39 | }
--------------------------------------------------------------------------------
/src/main/resources/static/js/setting.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("#uploadForm").submit(upload);
3 | });
4 |
5 | function upload() {
6 | $.ajax({
7 | url: "http://upload-z1.qiniup.com",
8 | method: "post",
9 | processData: false,
10 | contentType: false,
11 | data: new FormData($("#uploadForm")[0]),
12 | success: function(data) {
13 | if(data && data.code == 0) {
14 | // 更新头像访问路径
15 | $.post(
16 | CONTEXT_PATH + "/user/header/url",
17 | {"fileName":$("input[name='key']").val()},
18 | function(data) {
19 | data = $.parseJSON(data);
20 | if(data.code == 0) {
21 | window.location.reload();
22 | } else {
23 | alert(data.msg);
24 | }
25 | }
26 | );
27 | } else {
28 | alert("上传失败!");
29 | }
30 | }
31 | });
32 | return false;
33 | }
--------------------------------------------------------------------------------
/src/main/resources/static/js/global.js:
--------------------------------------------------------------------------------
1 | var CONTEXT_PATH = "";
2 |
3 | window.alert = function(message) {
4 | if(!$(".alert-box").length) {
5 | $("body").append(
6 | ''+
7 | '
'+
8 | '
'+
9 | ''+
15 | '
'+
18 | ''+
21 | '
'+
22 | '
'+
23 | '
'
24 | );
25 | }
26 |
27 | var h = $(".alert-box").height();
28 | var y = h / 2 - 100;
29 | if(h > 600) y -= 100;
30 | $(".alert-box .modal-dialog").css("margin", (y < 0 ? 0 : y) + "px auto");
31 |
32 | $(".alert-box .modal-body p").text(message);
33 | $(".alert-box").modal("show");
34 | }
35 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/LoginTicketMapper.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import com.nowcoder.community.entity.LoginTicket;
4 | import org.apache.ibatis.annotations.*;
5 |
6 | /**
7 | * @author coolsen
8 | * @version 1.0.0
9 | * @ClassName LoginTicketMapper.java
10 | * @Description 登录mapper,不推荐使用,目前使用redis存储ticket
11 | * @createTime 4/29/2020 6:16 PM
12 | */
13 |
14 | @Mapper
15 | @Deprecated
16 | public interface LoginTicketMapper {
17 |
18 | @Insert({
19 | "insert into login_ticket(user_id,ticket,status,expired) ",
20 | "values(#{userId},#{ticket},#{status},#{expired})"
21 | })
22 | @Options(useGeneratedKeys = true, keyProperty = "id")
23 | int insertLoginTicket(LoginTicket LoginTicket);
24 |
25 | @Select({
26 | "select id,user_id,ticket,status,expired ",
27 | "from login_ticket where ticket=#{ticket}"
28 | })
29 | LoginTicket selectByTicket(String ticket);
30 |
31 | @Update({
32 | ""
38 | })
39 | int updateStatus(String ticket, int status);
40 | }
41 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/MailTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import com.nowcoder.community.util.MailClient;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.context.SpringBootTest;
8 | import org.springframework.test.context.ContextConfiguration;
9 | import org.springframework.test.context.junit4.SpringRunner;
10 | import org.thymeleaf.TemplateEngine;
11 | import org.thymeleaf.context.Context;
12 |
13 | @RunWith(SpringRunner.class)
14 | @SpringBootTest
15 | @ContextConfiguration(classes = CommunityApplication.class)
16 | public class MailTests {
17 |
18 | @Autowired
19 | private MailClient mailClient;
20 |
21 | @Autowired
22 | private TemplateEngine templateEngine;
23 |
24 | @Test
25 | public void testTextMail() {
26 | mailClient.sendMail("anotherpony@163.com", "TEST", "Welcome.");
27 | }
28 |
29 | @Test
30 | public void testHtmlMail() {
31 | Context context = new Context();
32 | context.setVariable("username", "sunday");
33 |
34 | String content = templateEngine.process("/mail/demo", context);
35 | System.out.println(content);
36 |
37 | mailClient.sendMail("anotherpony@163.com", "HTML", content);
38 | }
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/actuator/DatabaseEndpoint.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.actuator;
2 |
3 | import com.nowcoder.community.util.CommunityUtil;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.actuate.endpoint.annotation.Endpoint;
8 | import org.springframework.boot.actuate.endpoint.annotation.ReadOperation;
9 | import org.springframework.stereotype.Component;
10 |
11 | import javax.sql.DataSource;
12 | import java.sql.Connection;
13 | import java.sql.SQLException;
14 |
15 | /**
16 | * @author coolsen
17 | * @version 1.0.0
18 | * @ClassName DatabaseEndpoint.java
19 | * @Description Database Endpoint for actuator
20 | * @createTime 2020/5/22 16:37
21 | */
22 |
23 | @Component
24 | @Endpoint(id = "database")
25 | public class DatabaseEndpoint {
26 |
27 | private static final Logger logger = LoggerFactory.getLogger(DatabaseEndpoint.class);
28 |
29 | @Autowired
30 | private DataSource dataSource;
31 |
32 | @ReadOperation
33 | public String checkConnection(){
34 | try (
35 | Connection conn = dataSource.getConnection();
36 | ) {
37 | return CommunityUtil.getJSONString(0, "获取连接成功!");
38 | } catch (SQLException e) {
39 | logger.error("获取连接失败:" + e.getMessage());
40 | return CommunityUtil.getJSONString(1, "获取连接失败!");
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/AlphaInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
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 | @Component
13 | public class AlphaInterceptor implements HandlerInterceptor {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(AlphaInterceptor.class);
16 |
17 | // 在Controller之前执行
18 | @Override
19 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
20 | logger.debug("preHandle: " + handler.toString());
21 | return true;
22 | }
23 |
24 | // 在Controller之后执行
25 | @Override
26 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
27 | logger.debug("postHandle: " + handler.toString());
28 | }
29 |
30 | // 在TemplateEngine之后执行
31 | @Override
32 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
33 | logger.debug("afterCompletion: " + handler.toString());
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/RedisConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import org.springframework.context.annotation.Bean;
4 | import org.springframework.context.annotation.Configuration;
5 | import org.springframework.data.redis.connection.RedisConnectionFactory;
6 | import org.springframework.data.redis.core.RedisTemplate;
7 | import org.springframework.data.redis.serializer.RedisSerializer;
8 | import org.springframework.data.redis.serializer.StringRedisSerializer;
9 |
10 | /**
11 | * @author coolsen
12 | * @version 1.0.0
13 | * @ClassName RedisConfig.java
14 | * @Description Redis Config
15 | * @createTime 5/9/2020 12:04 PM
16 | */
17 |
18 | @Configuration
19 | public class RedisConfig {
20 |
21 | @Bean
22 | public RedisTemplate redisTemplate(RedisConnectionFactory factory){
23 | //参考spring的RedisAutoConfiguration进行修改
24 | RedisTemplate template = new RedisTemplate();
25 | template.setConnectionFactory(factory);
26 |
27 | // 设置key的序列化方式
28 | template.setKeySerializer(RedisSerializer.string());
29 | // 设置value的序列化方式
30 | template.setValueSerializer(RedisSerializer.json());
31 | // 设置hash的key的序列化方式
32 | template.setHashKeySerializer(RedisSerializer.string());
33 | // 设置hash的value的序列化方式
34 | template.setHashValueSerializer(RedisSerializer.json());
35 |
36 | template.afterPropertiesSet();
37 | return template;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/dao/MessageMapper.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.dao;
2 |
3 | import com.nowcoder.community.entity.Message;
4 | import org.apache.ibatis.annotations.Mapper;
5 |
6 | import java.util.List;
7 |
8 | /**
9 | * @author coolsen
10 | * @version 1.0.0
11 | * @ClassName MessageMapper.java
12 | * @Description MessageMapper
13 | * @createTime 5/8/2020 7:50 PM
14 | */
15 | @Mapper
16 | public interface MessageMapper {
17 |
18 | // 查询当前用户的会话列表,针对每个会话只返回一条最新的私信.
19 | List selectConversations(int userId, int offset, int limit);
20 |
21 | // 查询当前用户的会话数量.
22 | int selectConversationCount(int userId);
23 |
24 | // 查询某个会话所包含的私信列表.
25 | List selectLetters(String conversationId, int offset, int limit);
26 |
27 | // 查询某个会话所包含的私信数量.
28 | int selectLetterCount(String conversationId);
29 |
30 | // 查询未读私信的数量
31 | int selectLetterUnreadCount(int userId, String conversationId);
32 |
33 | // 新增消息
34 | int insertMessage(Message message);
35 |
36 | // 修改消息的状态
37 | int updateStatus(List ids, int status);
38 |
39 | // 查询某个主题下最新的通知
40 | Message selectLatestNotice(int userId,String topic);
41 |
42 | // 查询某个主题下包含的通知数量
43 | int selectNoticeCount(int userId,String topic);
44 |
45 | // 查询未读通知的消息数量
46 | int selectNoticeUnreadCount(int userId,String topic);
47 |
48 | // 查询某个主题包含的通知列表
49 | List selectNotices(int userId,String topic,int offset,int limit);
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/DataInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import ch.qos.logback.core.net.HardenedObjectInputStream;
4 | import com.nowcoder.community.entity.User;
5 | import com.nowcoder.community.service.DataService;
6 | import com.nowcoder.community.util.HostHolder;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.web.servlet.HandlerInterceptor;
10 |
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 |
14 | /**
15 | * @author coolsen
16 | * @version 1.0.0
17 | * @ClassName DataInterceptor.java
18 | * @Description 数据统计 Interceptor
19 | * @createTime 2020/5/20 21:35
20 | */
21 |
22 | @Component
23 | public class DataInterceptor implements HandlerInterceptor {
24 |
25 | @Autowired
26 | private DataService dataService;
27 |
28 | @Autowired
29 | private HostHolder hostHolder;
30 |
31 |
32 | @Override
33 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
34 | // 统计UV
35 | String ip = request.getRemoteHost();
36 | dataService.recordUV(ip);
37 |
38 | // 统计DAU
39 | User user = hostHolder.getUser();
40 | if(user!=null){
41 | dataService.recordDAU(user.getId());
42 | }
43 | return true;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/KaptchaConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import com.google.code.kaptcha.Producer;
4 | import com.google.code.kaptcha.impl.DefaultKaptcha;
5 | import com.google.code.kaptcha.util.Config;
6 | import org.springframework.context.annotation.Bean;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import java.util.Properties;
10 |
11 | /**
12 | * @author coolsen
13 | * @version 1.0.0
14 | * @ClassName KaptchaConfig.java
15 | * @Description 验证码插件Kaptcha配置
16 | * @createTime 4/29/2020 5:26 PM
17 | */
18 | @Configuration
19 | public class KaptchaConfig {
20 |
21 | @Bean
22 | public Producer kaptchaProducer(){
23 | Properties properties=new Properties();
24 | properties.setProperty("kaptcha.image.width", "100");
25 | properties.setProperty("kaptcha.image.height", "40");
26 | properties.setProperty("kaptcha.textproducer.font.size", "32");
27 | properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
28 | properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
29 | properties.setProperty("kaptcha.textproducer.char.length", "4");
30 | properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
31 |
32 | DefaultKaptcha kaptcha = new DefaultKaptcha();
33 | Config config = new Config(properties);
34 | kaptcha.setConfig(config);
35 | return kaptcha;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/util/MailClient.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | import org.slf4j.Logger;
4 | import org.slf4j.LoggerFactory;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.mail.javamail.JavaMailSender;
8 | import org.springframework.mail.javamail.MimeMessageHelper;
9 | import org.springframework.stereotype.Component;
10 |
11 | import javax.mail.MessagingException;
12 | import javax.mail.internet.MimeMessage;
13 |
14 | /**
15 | * @author coolsen
16 | * @version 1.0.0
17 | * MailClient.java
18 | * @Description 邮箱客户端
19 | * @createTime 4/28/2020 8:36 PM
20 | */
21 |
22 | @Component
23 | public class MailClient {
24 | private static final Logger logger = LoggerFactory.getLogger(MailClient.class);
25 |
26 | @Autowired
27 | private JavaMailSender mailSender;
28 |
29 | @Value("${spring.mail.username}")
30 | private String from;
31 |
32 | public void sendMail(String to, String subject, String content) {
33 | try {
34 | MimeMessage message = mailSender.createMimeMessage();
35 | MimeMessageHelper helper = new MimeMessageHelper(message);
36 | helper.setFrom(from);
37 | helper.setTo(to);
38 | helper.setSubject(subject);
39 | helper.setText(content, true);
40 | mailSender.send(helper.getMimeMessage());
41 | } catch (MessagingException e) {
42 | logger.error("发送邮件失败:" + e.getMessage());
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/resources/mapper/comment-mapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | id, user_id, entity_type, entity_id, target_id, content, status, create_time
9 |
10 |
11 |
12 | user_id, entity_type, entity_id, target_id, content, status, create_time
13 |
14 |
15 |
24 |
25 |
32 |
33 |
37 |
38 |
43 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/LoginRequiredInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import com.nowcoder.community.annotation.LoginRequired;
4 | import com.nowcoder.community.util.HostHolder;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.stereotype.Component;
7 | import org.springframework.web.method.HandlerMethod;
8 | import org.springframework.web.servlet.HandlerInterceptor;
9 |
10 | import javax.servlet.http.HttpServletRequest;
11 | import javax.servlet.http.HttpServletResponse;
12 | import java.lang.reflect.Method;
13 |
14 | /**
15 | * @author coolsen
16 | * @version 1.0.0
17 | * @ClassName LoginRequiredInterceptor.java
18 | * @Description 登录拦截器
19 | * @createTime 4/30/2020 7:29 PM
20 | */
21 | @Component
22 | public class LoginRequiredInterceptor implements HandlerInterceptor {
23 |
24 | @Autowired
25 | private HostHolder hostHolder;
26 |
27 | @Override
28 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
29 | // 拦截 Method
30 | if (handler instanceof HandlerMethod) {
31 | HandlerMethod handlerMethod = (HandlerMethod) handler;
32 | Method method = handlerMethod.getMethod();
33 | LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
34 | if (loginRequired != null && hostHolder.getUser() == null) {
35 | response.sendRedirect(request.getContextPath() + "/login");
36 | return false;
37 | }
38 | }
39 | return true;
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/LoginTicket.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * @author coolsen
7 | * @version 1.0.0
8 | * @ClassName LoginTicket.java
9 | * @Description 登录凭证
10 | * @createTime 4/29/2020 6:14 PM
11 | */
12 | public class LoginTicket {
13 | private int id;
14 | private int userId;
15 | private String ticket;
16 | private int status;
17 | private Date expired;
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 String getTicket() {
36 | return ticket;
37 | }
38 |
39 | public void setTicket(String ticket) {
40 | this.ticket = ticket;
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 Date getExpired() {
52 | return expired;
53 | }
54 |
55 | public void setExpired(Date expired) {
56 | this.expired = expired;
57 | }
58 |
59 | @Override
60 | public String toString() {
61 | return "LoginTicket{" +
62 | "id=" + id +
63 | ", userId=" + userId +
64 | ", ticket='" + ticket + '\'' +
65 | ", status=" + status +
66 | ", expired=" + expired +
67 | '}';
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/util/CommunityUtil.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | import com.alibaba.fastjson.JSONObject;
4 | import org.apache.commons.lang3.StringUtils;
5 | import org.springframework.util.DigestUtils;
6 |
7 | import java.util.HashMap;
8 | import java.util.Map;
9 | import java.util.UUID;
10 |
11 | public class CommunityUtil {
12 |
13 | // 生成随机字符串
14 | public static String generateUUID() {
15 | return UUID.randomUUID().toString().replaceAll("-", "");
16 | }
17 |
18 | // MD5加密
19 | public static String md5(String key) {
20 | if (StringUtils.isBlank(key)) {
21 | return null;
22 | }
23 | return DigestUtils.md5DigestAsHex(key.getBytes());
24 | }
25 |
26 | public static String getJSONString(int code, String msg, Map map) {
27 | JSONObject json = new JSONObject();
28 | json.put("code", code);
29 | json.put("msg", msg);
30 | if (map != null) {
31 | for (String key : map.keySet()) {
32 | json.put(key, map.get(key));
33 | }
34 | }
35 | return json.toJSONString();
36 | }
37 |
38 | public static String getJSONString(int code, String msg) {
39 | return getJSONString(code, msg, null);
40 | }
41 |
42 | public static String getJSONString(int code) {
43 | return getJSONString(code, null, null);
44 | }
45 |
46 | public static void main(String[] args) {
47 | Map map = new HashMap<>();
48 | map.put("name", "zhangsan");
49 | map.put("age", 25);
50 | System.out.println(getJSONString(0, "ok", map));
51 | }
52 |
53 | }
54 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/MessageInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import com.nowcoder.community.entity.User;
4 | import com.nowcoder.community.service.MessageService;
5 | import com.nowcoder.community.util.HostHolder;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.data.redis.core.HashOperations;
8 | import org.springframework.stereotype.Component;
9 | import org.springframework.ui.Model;
10 | import org.springframework.web.servlet.HandlerInterceptor;
11 | import org.springframework.web.servlet.ModelAndView;
12 |
13 | import javax.servlet.http.HttpServletRequest;
14 | import javax.servlet.http.HttpServletResponse;
15 |
16 | /**
17 | * @author coolsen
18 | * @version 1.0.0
19 | * @ClassName MessageInterceptor.java
20 | * @Description 消息拦截器,为了在首页显示未读消息数量
21 | * @createTime 2020/5/15 15:28
22 | */
23 |
24 | @Component
25 | public class MessageInterceptor implements HandlerInterceptor {
26 |
27 | @Autowired
28 | private HostHolder hostHolder;
29 |
30 | @Autowired
31 | private MessageService messageService;
32 |
33 | @Override
34 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
35 | User user = hostHolder.getUser();
36 | if (user != null && modelAndView != null) {
37 | int noticeUnreadCount = messageService.findNoticeUnreadCount(user.getId(), null);
38 | int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
39 | modelAndView.addObject("allUnreadCount", noticeUnreadCount + letterUnreadCount);
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | ## 项目介绍
2 | 一个仿照牛客网实现的讨论社区,不仅实现了基本的注册,登录,发帖,评论,点赞,回复功能,同时使用前缀树实现敏感词过滤,使用wkhtmltopdf生成长图和pdf,实现网站UV和DAU统计,并将用户头像等信息存于七牛云服务器。
3 | ## 其他项目
4 | * 计算机类电子书仓库:https://github.com/cosen1024/awesome-cs-books
5 | * Java面试题仓库:https://github.com/cosen1024/Java-Interview
6 | ## 项目演示
7 | 演示地址: http://coolsen.cn/
8 | 账号密码: aaa/aaa
9 | ## 技术选型
10 | 
11 |
12 | ## 功能简介
13 | * 使用Spring Security 做权限控制,替代拦截器的拦截控制,并使用自己的认证方案替代Security 认证流程,使权限认证和控制更加方便灵活。
14 | * 使用Redis的set实现点赞,zset实现关注,并使用Redis存储登录ticket和验证码,解决分布式session问题。
15 | * 使用Redis高级数据类型HyperLogLog统计UV(Unique Visitor),使用Bitmap统计DAU(Daily Active User)。
16 | * 使用Kafka处理发送评论、点赞和关注等系统通知,并使用事件进行封装,构建了强大的异步消息系统。
17 | * 使用Elasticsearch做全局搜索,并通过事件封装,增加关键词高亮显示等功能。
18 | * 对热帖排行模块,使用分布式缓存Redis和本地缓存Caffeine作为多级缓存,避免了缓存雪崩,将QPS提升了20倍(10-200),大大提升了网站访问速度。并使用Quartz定时更新热帖排行。
19 |
20 |
21 | ## 开发环境
22 |
23 | | 工具 | 版本号 | 下载 |
24 | | ------------- | ------ | --------------------------------- |
25 | | JDK | 11 | https://openjdk.java.net/install/ |
26 | | Mysql | 5.7 | https://www.mysql.com/ |
27 | | Redis | 3.2 | https://redis.io/download |
28 | | Elasticsearch | 6.4.3 | https://www.elastic.co/downloads |
29 | | Kafka | 2.3.0 | https://kafka.apache.org/downloads |
30 | | nginx | 1.10 | http://nginx.org/en/download.html |
31 | ## 运行效果展示
32 | * 首页
33 | 
34 | * 消息
35 | 
36 | ## 后续更新点
37 | * 增加收藏功能
38 | * 增强对话框功能
39 | ## 相关资源
40 | [相关的教程](https://pan.baidu.com/s/1LjYYwJVsqNBxq69udsXMvA):网盘提取码:wxdd
41 |
42 | 有项目相关、求职等问题,可以加我微信交流,个人微信:kusengo,
43 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/advice/ExceptionAdvice.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.advice;
2 |
3 | import com.nowcoder.community.util.CommunityUtil;
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.stereotype.Controller;
7 | import org.springframework.web.bind.annotation.ControllerAdvice;
8 | import org.springframework.web.bind.annotation.ExceptionHandler;
9 |
10 | import javax.servlet.http.HttpServletRequest;
11 | import javax.servlet.http.HttpServletResponse;
12 | import java.io.IOException;
13 | import java.io.PrintWriter;
14 |
15 | /**
16 | * @author coolsen
17 | * @version 1.0.0
18 | * @ClassName ExceptionAdvice.java
19 | * @Description 统一处理异常的Controller配置类
20 | * @createTime 5/8/2020 9:34 PM
21 | */
22 |
23 | @ControllerAdvice(annotations = Controller.class)
24 | public class ExceptionAdvice {
25 |
26 | private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
27 |
28 | @ExceptionHandler({Exception.class})
29 | public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
30 | logger.error("服务器发生异常: " + e.getMessage());
31 | for (StackTraceElement element : e.getStackTrace()) {
32 | logger.error(element.toString());
33 | }
34 |
35 | String xRequestedWith = request.getHeader("x-requested-with");
36 | //处理异步请求的方式
37 | if ("XMLHttpRequest".equals(xRequestedWith)) {
38 | response.setContentType("application/plain;charset=utf-8");//普通文本,浏览器会自动转换为json格式
39 | PrintWriter writer = response.getWriter();
40 | writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
41 | } else {
42 | response.sendRedirect(request.getContextPath() + "/error");
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/src/main/resources/mapper/user-mapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | username, password, salt, email, type, status, activation_code, header_url, create_time
9 |
10 |
11 |
12 | id, username, password, salt, email, type, status, activation_code, header_url, create_time
13 |
14 |
15 |
20 |
21 |
26 |
27 |
32 |
33 |
34 | insert into user ()
35 | values(#{username}, #{password}, #{salt}, #{email}, #{type}, #{status}, #{activationCode}, #{headerUrl}, #{createTime})
36 |
37 |
38 |
39 | update user set status = #{status} where id = #{id}
40 |
41 |
42 |
45 |
46 |
47 | update user set password = #{password} where id = #{id}
48 |
49 |
50 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/aspect/ServiceLogAspect.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.aspect;
2 |
3 | import org.aspectj.lang.JoinPoint;
4 | import org.aspectj.lang.annotation.Aspect;
5 | import org.aspectj.lang.annotation.Before;
6 | import org.aspectj.lang.annotation.Pointcut;
7 | import org.slf4j.Logger;
8 | import org.slf4j.LoggerFactory;
9 | import org.springframework.stereotype.Component;
10 | import org.springframework.web.context.request.RequestContextHolder;
11 | import org.springframework.web.context.request.ServletRequestAttributes;
12 |
13 | import javax.servlet.http.HttpServletRequest;
14 | import java.text.SimpleDateFormat;
15 | import java.util.Date;
16 |
17 | /**
18 | * @author coolsen
19 | * @version 1.0.0
20 | * @ClassName ServiceLogAspect.java
21 | * @Description 使用AOP记录日志
22 | * @createTime 5/8/2020 10:09 PM
23 | */
24 |
25 | @Component
26 | @Aspect
27 | public class ServiceLogAspect {
28 |
29 | private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
30 |
31 | @Pointcut("execution(* com.nowcoder.community.service.*.*(..))")
32 | public void pointcut() {
33 |
34 | }
35 |
36 | @Before("pointcut()")
37 | public void before(JoinPoint joinPoint) {
38 | // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()].
39 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
40 |
41 | //TODO:此处逻辑处理实现简单,需要详细实现。
42 | if(attributes==null){
43 | return;
44 | }
45 | HttpServletRequest request = attributes.getRequest();
46 | String ip = request.getRemoteHost();
47 | String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
48 | String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
49 | logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/KafkaTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import org.apache.kafka.clients.consumer.ConsumerRecord;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.boot.test.autoconfigure.core.AutoConfigureCache;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.kafka.annotation.KafkaHandler;
10 | import org.springframework.kafka.annotation.KafkaListener;
11 | import org.springframework.kafka.core.KafkaTemplate;
12 | import org.springframework.stereotype.Component;
13 | import org.springframework.test.context.ContextConfiguration;
14 | import org.springframework.test.context.junit4.SpringRunner;
15 |
16 | /**
17 | * @author coolsen
18 | * @version 1.0.0
19 | * @ClassName KafkaTests.java
20 | * @Description Kafka Test
21 | * @createTime 2020/5/14 13:14
22 | */
23 |
24 | @RunWith(SpringRunner.class)
25 | @SpringBootTest
26 | @ContextConfiguration(classes = CommunityApplication.class)
27 | public class KafkaTests {
28 |
29 | @Autowired
30 | private KafkaProducer kafkaProducer;
31 |
32 | @Test
33 | public void testKafka() {
34 | kafkaProducer.sendMessage("test", "你好");
35 | kafkaProducer.sendMessage("test", "在吗");
36 |
37 | try {
38 | Thread.sleep(1000 * 10);
39 | } catch (InterruptedException e) {
40 | e.printStackTrace();
41 | }
42 | }
43 |
44 | }
45 |
46 | @Component
47 | class KafkaProducer {
48 |
49 | @Autowired
50 | private KafkaTemplate kafkaTemplate;
51 |
52 | public void sendMessage(String topic, String content) {
53 | kafkaTemplate.send(topic, content);
54 | }
55 |
56 | }
57 |
58 | @Component
59 | class KafkaConsumer {
60 |
61 | @KafkaListener(topics = {"test"})
62 | public void handleMessage(ConsumerRecord record) {
63 | System.out.println(record.value());
64 | }
65 |
66 |
67 | }
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/CaffeineTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import com.nowcoder.community.entity.DiscussPost;
4 | import com.nowcoder.community.service.DiscussPostService;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.test.context.ContextConfiguration;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | import java.util.Date;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest
16 | @ContextConfiguration(classes = CommunityApplication.class)
17 | public class CaffeineTests {
18 |
19 | @Autowired
20 | private DiscussPostService postService;
21 |
22 | // 自造数据,进行压力测试
23 | @Test
24 | public void initDataForTest() {
25 | long startTime = System.currentTimeMillis();
26 | for (int i = 0; i < 300000; i++) {
27 | DiscussPost post = new DiscussPost();
28 | post.setUserId(111);
29 | post.setTitle("互联网求职暖春计划");
30 | post.setContent("今年的就业形势,确实不容乐观。过了个年,仿佛跳水一般,整个讨论区哀鸿遍野!19届真的没人要了吗?!18届被优化真的没有出路了吗?!大家的“哀嚎”与“悲惨遭遇”牵动了每日潜伏于讨论区的牛客小哥哥小姐姐们的心,于是牛客决定:是时候为大家做点什么了!为了帮助大家度过“寒冬”,牛客网特别联合60+家企业,开启互联网求职暖春计划,面向18届&19届,拯救0 offer!");
31 | post.setCreateTime(new Date());
32 | post.setScore(Math.random() * 2000);
33 | postService.addDiscussPost(post);
34 | }
35 | long endTime = System.currentTimeMillis();
36 | long totalTime=endTime-startTime;
37 | System.out.println(String.format("总时间%dmin",totalTime/(1000*60)));//39min
38 | }
39 |
40 | @Test
41 | public void testCache() {
42 |
43 | System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
44 | System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
45 | System.out.println(postService.findDiscussPosts(0, 0, 10, 1));
46 | System.out.println(postService.findDiscussPosts(0, 0, 10, 0));
47 | }
48 |
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Event.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.HashMap;
4 | import java.util.Map;
5 |
6 | /**
7 | * @author coolsen
8 | * @version 1.0.0
9 | * @ClassName Event.java
10 | * @Description 事件,用于消息队列
11 | * @createTime 2020/5/14 21:19
12 | */
13 | public class Event {
14 |
15 | private String topic;
16 | private int userId;
17 | private int entityType;
18 | private int entityId;
19 | //实体的作者
20 | private int entityUserId;
21 |
22 | /*
23 | 把其他额外的数据,存入map中,具有扩展性
24 | 目的:为了后期动态扩展,有些字段没有办法做出预判
25 | */
26 | private Map data=new HashMap<>();
27 |
28 | public String getTopic() {
29 | return topic;
30 | }
31 |
32 | /*
33 | 此处这样设计,是为了更灵活的设置属性,避免使用多个构造函数。
34 | 这样设计很灵活和方便
35 | */
36 | public Event setTopic(String topic) {
37 | this.topic = topic;
38 | return this;
39 | }
40 |
41 | public int getUserId() {
42 | return userId;
43 | }
44 |
45 | public Event setUserId(int userId) {
46 | this.userId = userId;
47 | return this;
48 | }
49 |
50 | public int getEntityType() {
51 | return entityType;
52 | }
53 |
54 | public Event setEntityType(int entityType) {
55 | this.entityType = entityType;
56 | return this;
57 | }
58 |
59 | public int getEntityId() {
60 | return entityId;
61 | }
62 |
63 | public Event setEntityId(int entityId) {
64 | this.entityId = entityId;
65 | return this;
66 | }
67 |
68 | public int getEntityUserId() {
69 | return entityUserId;
70 | }
71 |
72 | public Event setEntityUserId(int entityUserId) {
73 | this.entityUserId = entityUserId;
74 | return this;
75 | }
76 |
77 | public Map getData() {
78 | return data;
79 | }
80 |
81 | public Event setData(String key,Object value) {
82 | this.data.put(key,value);
83 | return this;
84 | }
85 | }
86 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/WebMvcConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import com.nowcoder.community.annotation.LoginRequired;
4 | import com.nowcoder.community.controller.interceptor.*;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.context.annotation.Configuration;
7 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
8 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
9 |
10 | @Configuration
11 | public class WebMvcConfig implements WebMvcConfigurer {
12 |
13 | @Autowired
14 | private AlphaInterceptor alphaInterceptor;
15 |
16 | @Autowired
17 | private LoginTicketInterceptor loginTicketInterceptor;
18 |
19 | /*
20 | 已废弃使用,现在使用spring security来做权限认证
21 | @Autowired
22 | private LoginRequiredInterceptor loginRequiredInterceptor;
23 | */
24 |
25 | @Autowired
26 | private MessageInterceptor messageInterceptor;
27 |
28 | @Autowired
29 | private DataInterceptor dataInterceptor;
30 |
31 | @Override
32 | public void addInterceptors(InterceptorRegistry registry) {
33 | registry.addInterceptor(alphaInterceptor)
34 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg")
35 | .addPathPatterns("/register", "/login");
36 |
37 | registry.addInterceptor(loginTicketInterceptor)
38 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
39 |
40 | // registry.addInterceptor(loginRequiredInterceptor)
41 | // .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
42 |
43 | registry.addInterceptor(messageInterceptor)
44 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
45 |
46 | registry.addInterceptor(dataInterceptor)
47 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
48 | }
49 |
50 | }
51 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/discuss.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("#topBtn").click(setTop);
3 | $("#wonderfulBtn").click(setWonderful);
4 | $("#deleteBtn").click(setDelete);
5 | });
6 |
7 | function like(btn, entityType, entityId, entityUserId, postId) {
8 | $.post(
9 | CONTEXT_PATH + "/like",
10 | {"entityType":entityType,"entityId":entityId,"entityUserId":entityUserId,"postId":postId},
11 | function(data) {
12 | data = $.parseJSON(data);
13 | if(data.code == 0) {
14 | $(btn).children("i").text(data.likeCount);
15 | $(btn).children("b").text(data.likeStatus==1?'已赞':"赞");
16 | } else {
17 | alert(data.msg);
18 | }
19 | }
20 | );
21 | }
22 |
23 | // 置顶
24 | function setTop() {
25 | $.post(
26 | CONTEXT_PATH + "/discuss/top",
27 | {"id":$("#postId").val()},
28 | function(data) {
29 | data = $.parseJSON(data);
30 | if(data.code == 0) {
31 | $("#topBtn").attr("disabled", "disabled");
32 | } else {
33 | alert(data.msg);
34 | }
35 | }
36 | );
37 | }
38 |
39 | // 加精
40 | function setWonderful() {
41 | $.post(
42 | CONTEXT_PATH + "/discuss/wonderful",
43 | {"id":$("#postId").val()},
44 | function(data) {
45 | data = $.parseJSON(data);
46 | if(data.code == 0) {
47 | $("#wonderfulBtn").attr("disabled", "disabled");
48 | } else {
49 | alert(data.msg);
50 | }
51 | }
52 | );
53 | }
54 |
55 | // 删除
56 | function setDelete() {
57 | $.post(
58 | CONTEXT_PATH + "/discuss/delete",
59 | {"id":$("#postId").val()},
60 | function(data) {
61 | data = $.parseJSON(data);
62 | if(data.code == 0) {
63 | location.href = CONTEXT_PATH + "/index";
64 | } else {
65 | alert(data.msg);
66 | }
67 | }
68 | );
69 | }
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/util/CommunityConstant.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | /**
4 | * @author coolsen
5 | * @version 1.0.0
6 | * @ClassName CommunityConstant.java
7 | * @Description 常量
8 | * @createTime 4/29/2020 3:33 PM
9 | */
10 | public interface CommunityConstant {
11 |
12 | /**
13 | * 激活成功
14 | */
15 | int ACTIVATION_SUCCESS = 0;
16 |
17 | /**
18 | * 重复激活
19 | */
20 | int ACTIVATION_REPEAT = 1;
21 |
22 | /**
23 | * 激活失败
24 | */
25 | int ACTIVATION_FAILURE = 2;
26 |
27 | /**
28 | * 默认状态的登录凭证的超时时间
29 | */
30 | int DEFAULT_EXPIRED_SECONDS = 3600 * 12;
31 |
32 | /**
33 | * 记住状态的登录凭证超时时间
34 | */
35 | int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
36 |
37 | /**
38 | * 实体类型: 帖子
39 | */
40 | int ENTITY_TYPE_POST = 1;
41 |
42 | /**
43 | * 实体类型: 评论
44 | */
45 | int ENTITY_TYPE_COMMENT = 2;
46 |
47 | /**
48 | * 实体类型: 用户
49 | */
50 | int ENTITY_TYPE_USER = 3;
51 |
52 | /**
53 | * 主题: 评论
54 | */
55 | String TOPIC_COMMENT = "comment";
56 |
57 | /**
58 | * 主题: 点赞
59 | */
60 | String TOPIC_LIKE = "like";
61 |
62 | /**
63 | * 主题: 关注
64 | */
65 | String TOPIC_FOLLOW = "follow";
66 |
67 | /**
68 | * 主题: 发帖
69 | */
70 | String TOPIC_PUBLISH = "publish";
71 |
72 | /**
73 | * 主题: 删帖
74 | */
75 | String TOPIC_DELETE = "delete";
76 |
77 | /**
78 | * 主题: 分享
79 | */
80 | String TOPIC_SHARE = "share";
81 |
82 | /**
83 | * 系统用户ID
84 | */
85 | int SYSTEM_USER_ID = 1;
86 |
87 | /**
88 | * 权限: 普通用户
89 | */
90 | String AUTHORITY_USER = "user";
91 |
92 | /**
93 | * 权限: 管理员
94 | */
95 | String AUTHORITY_ADMIN = "admin";
96 |
97 | /**
98 | * 权限: 版主
99 | */
100 | String AUTHORITY_MODERATOR = "moderator";
101 |
102 | }
103 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Message.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * @author coolsen
7 | * @version 1.0.0
8 | * @ClassName Message.java
9 | * @Description 私信实体类
10 | * @createTime 5/8/2020 7:49 PM
11 | */
12 | public class Message {
13 | private int id;
14 | private int fromId;
15 | private int toId;
16 | private String conversationId;
17 | private String content;
18 | private int status;
19 | private Date createTime;
20 |
21 | public int getId() {
22 | return id;
23 | }
24 |
25 | public void setId(int id) {
26 | this.id = id;
27 | }
28 |
29 | public int getFromId() {
30 | return fromId;
31 | }
32 |
33 | public void setFromId(int fromId) {
34 | this.fromId = fromId;
35 | }
36 |
37 | public int getToId() {
38 | return toId;
39 | }
40 |
41 | public void setToId(int toId) {
42 | this.toId = toId;
43 | }
44 |
45 | public String getConversationId() {
46 | return conversationId;
47 | }
48 |
49 | public void setConversationId(String conversationId) {
50 | this.conversationId = conversationId;
51 | }
52 |
53 | public String getContent() {
54 | return content;
55 | }
56 |
57 | public void setContent(String content) {
58 | this.content = content;
59 | }
60 |
61 | public int getStatus() {
62 | return status;
63 | }
64 |
65 | public void setStatus(int status) {
66 | this.status = status;
67 | }
68 |
69 | public Date getCreateTime() {
70 | return createTime;
71 | }
72 |
73 | public void setCreateTime(Date createTime) {
74 | this.createTime = createTime;
75 | }
76 |
77 | @Override
78 | public String toString() {
79 | return "Message{" +
80 | "id=" + id +
81 | ", fromId=" + fromId +
82 | ", toId=" + toId +
83 | ", conversationId='" + conversationId + '\'' +
84 | ", content='" + content + '\'' +
85 | ", status=" + status +
86 | ", createTime=" + createTime +
87 | '}';
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Page.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | /**
4 | * 封装分页相关的信息.
5 | */
6 | public class Page {
7 |
8 | // 当前页码
9 | private int current = 1;
10 | // 显示上限
11 | private int limit = 10;
12 | // 数据总数(用于计算总页数)
13 | private int rows;
14 | // 查询路径(用于复用分页链接)
15 | private String path;
16 |
17 | public int getCurrent() {
18 | return current;
19 | }
20 |
21 | public void setCurrent(int current) {
22 | if (current >= 1) {
23 | this.current = current;
24 | }
25 | }
26 |
27 | public int getLimit() {
28 | return limit;
29 | }
30 |
31 | public void setLimit(int limit) {
32 | if (limit >= 1 && limit <= 100) {
33 | this.limit = limit;
34 | }
35 | }
36 |
37 | public int getRows() {
38 | return rows;
39 | }
40 |
41 | public void setRows(int rows) {
42 | if (rows >= 0) {
43 | this.rows = rows;
44 | }
45 | }
46 |
47 | public String getPath() {
48 | return path;
49 | }
50 |
51 | public void setPath(String path) {
52 | this.path = path;
53 | }
54 |
55 | /**
56 | * 获取当前页的起始行
57 | *
58 | * @return
59 | */
60 | public int getOffset() {
61 | // current * limit - limit
62 | return (current - 1) * limit;
63 | }
64 |
65 | /**
66 | * 获取总页数
67 | *
68 | * @return
69 | */
70 | public int getTotal() {
71 | // rows / limit [+1]
72 | if (rows % limit == 0) {
73 | return rows / limit;
74 | } else {
75 | return rows / limit + 1;
76 | }
77 | }
78 |
79 | /**
80 | * 获取起始页码
81 | *
82 | * @return
83 | */
84 | public int getFrom() {
85 | int from = current - 2;
86 | return from < 1 ? 1 : from;
87 | }
88 |
89 | /**
90 | * 获取结束页码
91 | *
92 | * @return
93 | */
94 | public int getTo() {
95 | int to = current + 2;
96 | int total = getTotal();
97 | return to > total ? total : to;
98 | }
99 |
100 | }
101 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/DataController.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller;
2 |
3 | import com.nowcoder.community.service.DataService;
4 | import org.apache.kafka.common.network.Mode;
5 | import org.springframework.beans.factory.annotation.Autowired;
6 | import org.springframework.format.annotation.DateTimeFormat;
7 | import org.springframework.stereotype.Controller;
8 | import org.springframework.ui.Model;
9 | import org.springframework.web.bind.annotation.RequestMapping;
10 | import org.springframework.web.bind.annotation.RequestMethod;
11 |
12 | import java.util.Date;
13 |
14 | /**
15 | * @author coolsen
16 | * @version 1.0.0
17 | * @ClassName DataController.java
18 | * @Description 数据统计 Controller
19 | * @createTime 2020/5/20 21:41
20 | */
21 |
22 | @Controller
23 | public class DataController {
24 |
25 | @Autowired
26 | private DataService dataService;
27 |
28 | // 统计页面
29 | @RequestMapping(path = "/data",method = {RequestMethod.GET,RequestMethod.POST})
30 | public String getDataPage(){
31 | return "/site/admin/data";
32 | }
33 |
34 | // 统计网站UV
35 | @RequestMapping(path = "/data/uv",method = RequestMethod.POST)
36 | public String getUV(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,
37 | @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model){
38 | long uv = dataService.calculateUV(start, end);
39 | model.addAttribute("uvResult",uv);
40 | model.addAttribute("uvStartDate", start);
41 | model.addAttribute("uvEndDate", end);
42 |
43 | // 此处与return "/site/admin/data"一样,只不过目前通过转发,可以复用getDataPage方法。这个也是getDataPage方法的method = {RequestMethod.GET,RequestMethod.POST}的原因,因为要接受来自getUV的request(为post方式)
44 | return "forward:/data";
45 | }
46 |
47 | // 统计活跃用户
48 | @RequestMapping(path = "/data/dau", method = RequestMethod.POST)
49 | public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,
50 | @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {
51 | long dau = dataService.calculateDAU(start, end);
52 | model.addAttribute("dauResult", dau);
53 | model.addAttribute("dauStartDate", start);
54 | model.addAttribute("dauEndDate", end);
55 | return "forward:/data";
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/service/CommentService.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.service;
2 |
3 | import com.nowcoder.community.dao.CommentMapper;
4 | import com.nowcoder.community.entity.Comment;
5 | import com.nowcoder.community.util.CommunityConstant;
6 | import com.nowcoder.community.util.SensitiveFilter;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.stereotype.Service;
9 | import org.springframework.transaction.annotation.Isolation;
10 | import org.springframework.transaction.annotation.Propagation;
11 | import org.springframework.transaction.annotation.Transactional;
12 | import org.springframework.web.util.HtmlUtils;
13 |
14 | import java.util.List;
15 |
16 | @Service
17 | public class CommentService implements CommunityConstant {
18 |
19 | @Autowired
20 | private CommentMapper commentMapper;
21 |
22 | @Autowired
23 | private SensitiveFilter sensitiveFilter;
24 |
25 | @Autowired
26 | private DiscussPostService discussPostService;
27 |
28 | public List findCommentsByEntity(int entityType, int entityId, int offset, int limit) {
29 | return commentMapper.selectCommentsByEntity(entityType, entityId, offset, limit);
30 | }
31 |
32 | public int findCommentCount(int entityType, int entityId) {
33 | return commentMapper.selectCountByEntity(entityType, entityId);
34 | }
35 |
36 | @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
37 | public int addComment(Comment comment) {
38 | if (comment == null) {
39 | throw new IllegalArgumentException("参数不能为空!");
40 | }
41 |
42 | // 添加评论
43 | comment.setContent(HtmlUtils.htmlEscape(comment.getContent()));
44 | comment.setContent(sensitiveFilter.filter(comment.getContent()));
45 | int rows = commentMapper.insertComment(comment);
46 |
47 | // 更新帖子评论数量
48 | if (comment.getEntityType() == ENTITY_TYPE_POST) {
49 | int count = commentMapper.selectCountByEntity(comment.getEntityType(), comment.getEntityId());
50 | discussPostService.updateCommentCount(comment.getEntityId(), count);
51 | }
52 |
53 | return rows;
54 | }
55 |
56 | public Comment findCommentById(int id) {
57 | return commentMapper.selectCommentById(id);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/main/resources/mapper/discusspost-mapper.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 | id, user_id, title, content, type, status, create_time, comment_count, score
9 |
10 |
11 |
26 |
27 |
35 |
36 |
37 | user_id, title, content, type, status, create_time, comment_count, score
38 |
39 |
40 |
41 | insert into discuss_post()
42 | values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
43 |
44 |
45 |
51 |
52 |
55 |
56 |
57 | update discuss_post set type = #{type} where id = #{id}
58 |
59 |
60 |
61 | update discuss_post set status = #{status} where id = #{id}
62 |
63 |
64 |
65 | update discuss_post set score = #{score} where id = #{id}
66 |
67 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/service/MessageService.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.service;
2 |
3 | import com.nowcoder.community.dao.MessageMapper;
4 | import com.nowcoder.community.entity.Message;
5 | import com.nowcoder.community.util.SensitiveFilter;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Service;
8 | import org.springframework.web.util.HtmlUtils;
9 |
10 | import java.util.List;
11 |
12 | @Service
13 | public class MessageService {
14 |
15 | @Autowired
16 | private MessageMapper messageMapper;
17 |
18 | @Autowired
19 | private SensitiveFilter sensitiveFilter;
20 |
21 | public List findConversations(int userId, int offset, int limit) {
22 | return messageMapper.selectConversations(userId, offset, limit);
23 | }
24 |
25 | public int findConversationCount(int userId) {
26 | return messageMapper.selectConversationCount(userId);
27 | }
28 |
29 | public List findLetters(String conversationId, int offset, int limit) {
30 | return messageMapper.selectLetters(conversationId, offset, limit);
31 | }
32 |
33 | public int findLetterCount(String conversationId) {
34 | return messageMapper.selectLetterCount(conversationId);
35 | }
36 |
37 | public int findLetterUnreadCount(int userId, String conversationId) {
38 | return messageMapper.selectLetterUnreadCount(userId, conversationId);
39 | }
40 |
41 | public int addMessage(Message message) {
42 | message.setContent(HtmlUtils.htmlEscape(message.getContent()));
43 | message.setContent(sensitiveFilter.filter(message.getContent()));
44 | return messageMapper.insertMessage(message);
45 | }
46 |
47 | public int readMessage(List ids) {
48 | return messageMapper.updateStatus(ids, 1);
49 | }
50 |
51 | public Message findLatestNotice(int userId, String topic) {
52 | return messageMapper.selectLatestNotice(userId, topic);
53 | }
54 |
55 | public int findNoticeCount(int userId, String topic) {
56 | return messageMapper.selectNoticeCount(userId, topic);
57 | }
58 |
59 | public int findNoticeUnreadCount(int userId, String topic) {
60 | return messageMapper.selectNoticeUnreadCount(userId, topic);
61 | }
62 |
63 | public List findNotices(int userId, String topic, int offset, int limit) {
64 | return messageMapper.selectNotices(userId, topic, offset, limit);
65 | }
66 |
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/resources/static/css/global.css:
--------------------------------------------------------------------------------
1 | html {
2 | height: 100%;
3 | }
4 |
5 | body {
6 | background: #eee;
7 | font-family: arial, STHeiti, 'Microsoft YaHei', \5b8b\4f53;
8 | font-size: 14px;
9 | height: 100%;
10 | }
11 |
12 | .nk-container {
13 | position: relative;
14 | height: auto;
15 | min-height: 100%;
16 | }
17 |
18 | .container {
19 | width: 960px;
20 | padding: 0;
21 | }
22 |
23 | header .navbar-brand {
24 | background: url('http://static.nowcoder.com/images/res/logo/logo-v3.png') no-repeat;
25 | background-size: 147px 42px;
26 | width: 147px;
27 | height: 42px;
28 | margin: 5px 15px 5px 0;
29 | }
30 |
31 | header .navbar {
32 | padding: 5px 0;
33 | font-size: 16px;
34 | }
35 |
36 | header .badge {
37 | position: absolute;
38 | top: -3px;
39 | left: 33px;
40 | }
41 |
42 | footer {
43 | padding: 20px 0;
44 | font-size: 12px;
45 | position: absolute;
46 | bottom: 0;
47 | width: 100%;
48 | }
49 |
50 | footer .qrcode {
51 | text-align: center;
52 | }
53 |
54 | footer .detail-info{
55 | border-left: 1px solid #888;
56 | }
57 |
58 | footer .company-info li {
59 | padding-left: 16px;
60 | margin: 4px 0;
61 | }
62 |
63 | .main {
64 | padding: 20px 0;
65 | padding-bottom: 200px;
66 | }
67 |
68 | .main .container {
69 | background: #fff;
70 | padding: 20px;
71 | }
72 |
73 | i {
74 | font-style: normal;
75 | }
76 |
77 | u {
78 | text-decoration: none;
79 | }
80 |
81 | b {
82 | font-weight: normal;
83 | }
84 |
85 | a {
86 | color: #000;
87 | }
88 |
89 | a:hover {
90 | text-decoration: none;
91 | }
92 |
93 | .font-size-12 {
94 | font-size: 12px;
95 | }
96 | .font-size-14 {
97 | font-size: 14px;
98 | }
99 | .font-size-16 {
100 | font-size: 16px;
101 | }
102 | .font-size-18 {
103 | font-size: 18px;
104 | }
105 | .font-size-20 {
106 | font-size: 20px;
107 | }
108 | .font-size-22 {
109 | font-size: 20px;
110 | }
111 | .font-size-24 {
112 | font-size: 20px;
113 | }
114 |
115 | .hidden {
116 | display: none;
117 | }
118 |
119 | .rt-0 {
120 | right: 0;
121 | top: 0;
122 | }
123 |
124 | .square {
125 | display: inline-block;
126 | width: 7px;
127 | height: 7px;
128 | background: #ff6547;
129 | margin-bottom: 2px;
130 | margin-right: 3px;
131 | }
132 |
133 | .bg-gray {
134 | background: #eff0f2;
135 | }
136 |
137 | .user-header {
138 | width: 50px;
139 | height: 50px;
140 | }
141 |
142 | em {
143 | font-style: normal;
144 | color: red;
145 | }
146 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/SpringBootTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import com.nowcoder.community.entity.DiscussPost;
4 | import com.nowcoder.community.service.DiscussPostService;
5 | import org.junit.*;
6 | import org.junit.runner.RunWith;
7 | import org.springframework.beans.factory.annotation.Autowired;
8 | import org.springframework.boot.test.context.SpringBootTest;
9 | import org.springframework.test.context.ContextConfiguration;
10 | import org.springframework.test.context.junit4.SpringRunner;
11 |
12 | import java.util.Date;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest
16 | @ContextConfiguration(classes = CommunityApplication.class)
17 | public class SpringBootTests {
18 |
19 | @Autowired
20 | private DiscussPostService discussPostService;
21 |
22 | private DiscussPost data;
23 |
24 | @BeforeClass
25 | public static void beforeClass() {
26 | System.out.println("beforeClass");
27 | }
28 |
29 | @AfterClass
30 | public static void afterClass() {
31 | System.out.println("afterClass");
32 | }
33 |
34 | @Before
35 | public void before() {
36 | System.out.println("before");
37 |
38 | // 初始化测试数据
39 | data = new DiscussPost();
40 | data.setUserId(111);
41 | data.setTitle("Test Title");
42 | data.setContent("Test Content");
43 | data.setCreateTime(new Date());
44 | discussPostService.addDiscussPost(data);
45 | }
46 |
47 | @After
48 | public void after() {
49 | System.out.println("after");
50 |
51 | // 删除测试数据
52 | discussPostService.updateStatus(data.getId(), 2);
53 | }
54 |
55 | @Test
56 | public void test1() {
57 | System.out.println("test1");
58 | }
59 |
60 | @Test
61 | public void test2() {
62 | System.out.println("test2");
63 | }
64 |
65 | @Test
66 | public void testFindById() {
67 | DiscussPost post = discussPostService.findDiscussPostById(data.getId());
68 | Assert.assertNotNull(post);
69 | Assert.assertEquals(data.getTitle(), post.getTitle());
70 | Assert.assertEquals(data.getContent(), post.getContent());
71 | }
72 |
73 | @Test
74 | public void testUpdateScore() {
75 | int rows = discussPostService.updateScore(data.getId(), 2000.00);
76 | Assert.assertEquals(1, rows);
77 |
78 | DiscussPost post = discussPostService.findDiscussPostById(data.getId());
79 | Assert.assertEquals(2000.00, post.getScore(), 2);
80 | }
81 |
82 |
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Comment.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | /**
6 | * @author coolsen
7 | * @version 1.0.0
8 | * @ClassName Comment.java
9 | * @Description 评论实体类
10 | * @createTime 5/7/2020 4:27 PM
11 | */
12 | public class Comment {
13 | private int id;
14 | private int userId;
15 | private int entityType;
16 | private int entityId;
17 | private int targetId;
18 | private String content;
19 | private int status;
20 | private Date createTime;
21 |
22 | public int getId() {
23 | return id;
24 | }
25 |
26 | public void setId(int id) {
27 | this.id = id;
28 | }
29 |
30 | public int getUserId() {
31 | return userId;
32 | }
33 |
34 | public void setUserId(int userId) {
35 | this.userId = userId;
36 | }
37 |
38 | public int getEntityType() {
39 | return entityType;
40 | }
41 |
42 | public void setEntityType(int entityType) {
43 | this.entityType = entityType;
44 | }
45 |
46 | public int getEntityId() {
47 | return entityId;
48 | }
49 |
50 | public void setEntityId(int entityId) {
51 | this.entityId = entityId;
52 | }
53 |
54 | public int getTargetId() {
55 | return targetId;
56 | }
57 |
58 | public void setTargetId(int targetId) {
59 | this.targetId = targetId;
60 | }
61 |
62 | public String getContent() {
63 | return content;
64 | }
65 |
66 | public void setContent(String content) {
67 | this.content = content;
68 | }
69 |
70 | public int getStatus() {
71 | return status;
72 | }
73 |
74 | public void setStatus(int status) {
75 | this.status = status;
76 | }
77 |
78 | public Date getCreateTime() {
79 | return createTime;
80 | }
81 |
82 | public void setCreateTime(Date createTime) {
83 | this.createTime = createTime;
84 | }
85 |
86 | @Override
87 | public String toString() {
88 | return "Comment{" +
89 | "id=" + id +
90 | ", userId=" + userId +
91 | ", entityType=" + entityType +
92 | ", entityId=" + entityId +
93 | ", targetId=" + targetId +
94 | ", content='" + content + '\'' +
95 | ", status=" + status +
96 | ", createTime=" + createTime +
97 | '}';
98 | }
99 | }
100 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/SearchController.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller;
2 |
3 | import com.nowcoder.community.entity.DiscussPost;
4 | import com.nowcoder.community.entity.Page;
5 | import com.nowcoder.community.service.ElasticsearchService;
6 | import com.nowcoder.community.service.LikeService;
7 | import com.nowcoder.community.service.UserService;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 |
10 | import org.springframework.stereotype.Controller;
11 | import org.springframework.ui.Model;
12 | import org.springframework.web.bind.annotation.RequestMapping;
13 | import org.springframework.web.bind.annotation.RequestMethod;
14 |
15 | import javax.management.modelmbean.ModelMBeanOperationInfo;
16 | import java.util.ArrayList;
17 | import java.util.HashMap;
18 | import java.util.List;
19 | import java.util.Map;
20 |
21 | /**
22 | * @author coolsen
23 | * @version 1.0.0
24 | * @ClassName SearchController.java
25 | * @Description Search Controller
26 | * @createTime 2020/5/19 16:41
27 | */
28 |
29 | @Controller
30 | public class SearchController {
31 |
32 | @Autowired
33 | private ElasticsearchService elasticsearchService;
34 |
35 | @Autowired
36 | private UserService userService;
37 |
38 | @Autowired
39 | private LikeService likeService;
40 |
41 | // search?keyword=xxx
42 | @RequestMapping(path = "/search", method = RequestMethod.GET)
43 | public String search(String keyword, Page page, Model model) {
44 | // 搜索帖子
45 | org.springframework.data.domain.Page searchResults = elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());
46 |
47 | // 聚合数据
48 | List