├── src
├── main
│ ├── resources
│ │ ├── sensitive-words.txt
│ │ ├── static
│ │ │ ├── css
│ │ │ │ ├── login.css
│ │ │ │ ├── letter.css
│ │ │ │ ├── discuss-detail.css
│ │ │ │ └── global.css
│ │ │ ├── img
│ │ │ │ ├── 404.png
│ │ │ │ ├── icon.png
│ │ │ │ └── error.png
│ │ │ ├── html
│ │ │ │ ├── student.html
│ │ │ │ └── ajax-demo.html
│ │ │ └── js
│ │ │ │ ├── letter.js
│ │ │ │ ├── forget.js
│ │ │ │ ├── setting.js
│ │ │ │ ├── global.js
│ │ │ │ ├── index.js
│ │ │ │ ├── profile.js
│ │ │ │ └── discuss.js
│ │ ├── application-dev.properties
│ │ ├── application.properties
│ │ ├── templates
│ │ │ ├── demo
│ │ │ │ └── view.html
│ │ │ ├── mail
│ │ │ │ ├── demo.html
│ │ │ │ ├── forget.html
│ │ │ │ └── activation.html
│ │ │ ├── error
│ │ │ │ ├── 404.html
│ │ │ │ └── 500.html
│ │ │ └── site
│ │ │ │ ├── operate.html
│ │ │ │ ├── operate-result.html
│ │ │ │ ├── search.html
│ │ │ │ ├── admin
│ │ │ │ └── data.html
│ │ │ │ ├── my-comment.html
│ │ │ │ ├── my-post.html
│ │ │ │ ├── followee.html
│ │ │ │ ├── follower.html
│ │ │ │ ├── profile.html
│ │ │ │ ├── forget.html
│ │ │ │ ├── notice-detail.html
│ │ │ │ ├── register.html
│ │ │ │ ├── login.html
│ │ │ │ └── letter-detail.html
│ │ ├── mapper
│ │ │ ├── user-mapper.xml
│ │ │ ├── comment-mapper.xml
│ │ │ ├── discusspost-mapper.xml
│ │ │ └── message-mapper.xml
│ │ ├── application-pro.properties
│ │ ├── logback-spring-pro.xml
│ │ └── logback-spring-dev.xml
│ └── java
│ │ └── com
│ │ └── nowcoder
│ │ └── community
│ │ ├── dao
│ │ ├── AlphaDao.java
│ │ ├── AlphaDaoHibernateImpl.java
│ │ ├── AlphaDaoMyBatisImpl.java
│ │ ├── elasticsearch
│ │ │ └── DiscussPostRepository.java
│ │ ├── UserMapper.java
│ │ ├── CommentMapper.java
│ │ ├── LoginTicketMapper.java
│ │ ├── DiscussPostMapper.java
│ │ └── MessageMapper.java
│ │ ├── annotation
│ │ └── LoginRequired.java
│ │ ├── config
│ │ ├── ThreadPoolConfig.java
│ │ ├── AlphaConfig.java
│ │ ├── WkConfig.java
│ │ ├── RedisConfig.java
│ │ ├── KaptchaConfig.java
│ │ ├── WebMvcConfig.java
│ │ └── QuartzConfig.java
│ │ ├── quartz
│ │ ├── AlphaJob.java
│ │ └── PostScoreRefreshJob.java
│ │ ├── util
│ │ ├── HostHolder.java
│ │ ├── CookieUtil.java
│ │ ├── MailClient.java
│ │ ├── CommunityUtil.java
│ │ ├── CommunityConstant.java
│ │ └── RedisKeyUtil.java
│ │ ├── event
│ │ └── EventProducer.java
│ │ ├── CommunityApplication.java
│ │ ├── controller
│ │ ├── interceptor
│ │ │ ├── DataInterceptor.java
│ │ │ ├── AlphaInterceptor.java
│ │ │ ├── MessageInterceptor.java
│ │ │ ├── LoginRequiredInterceptor.java
│ │ │ └── LoginTicketInterceptor.java
│ │ ├── advice
│ │ │ └── ExceptionAdvice.java
│ │ ├── DataController.java
│ │ ├── SearchController.java
│ │ ├── LikeController.java
│ │ ├── HomeController.java
│ │ ├── CommentController.java
│ │ ├── ShareController.java
│ │ └── FollowController.java
│ │ ├── aspect
│ │ ├── AlphaAspect.java
│ │ └── ServiceLogAspect.java
│ │ ├── actuator
│ │ └── DatabaseEndpoint.java
│ │ ├── entity
│ │ ├── LoginTicket.java
│ │ ├── Event.java
│ │ ├── Message.java
│ │ ├── Page.java
│ │ ├── Comment.java
│ │ ├── User.java
│ │ └── DiscussPost.java
│ │ └── service
│ │ ├── MessageService.java
│ │ ├── LikeService.java
│ │ ├── CommentService.java
│ │ └── DataService.java
└── test
│ └── java
│ └── com
│ └── nowcoder
│ └── community
│ ├── WkTests.java
│ ├── LoggerTests.java
│ ├── TransactionTests.java
│ ├── QuartzTests.java
│ ├── SensitiveTests.java
│ ├── SortTests.java
│ ├── MailTests.java
│ ├── KafkaTests.java
│ ├── BlockingQueueTests.java
│ ├── CommunityApplicationTests.java
│ ├── MapperTests.java
│ └── ThreadPoolTests.java
├── .mvn
└── wrapper
│ ├── maven-wrapper.jar
│ └── maven-wrapper.properties
├── .gitignore
├── LICENSE
├── README.md
└── pom.xml
/src/main/resources/sensitive-words.txt:
--------------------------------------------------------------------------------
1 | 赌博
2 | 嫖娼
3 | 吸毒
4 | 开票
--------------------------------------------------------------------------------
/src/main/resources/static/css/login.css:
--------------------------------------------------------------------------------
1 | .main .container {
2 | width: 720px;
3 | }
4 |
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Junyanzhang/community/HEAD/.mvn/wrapper/maven-wrapper.jar
--------------------------------------------------------------------------------
/src/main/resources/static/img/404.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Junyanzhang/community/HEAD/src/main/resources/static/img/404.png
--------------------------------------------------------------------------------
/src/main/resources/static/img/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Junyanzhang/community/HEAD/src/main/resources/static/img/icon.png
--------------------------------------------------------------------------------
/src/main/resources/static/img/error.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Junyanzhang/community/HEAD/src/main/resources/static/img/error.png
--------------------------------------------------------------------------------
/src/main/resources/application-dev.properties:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/Junyanzhang/community/HEAD/src/main/resources/application-dev.properties
--------------------------------------------------------------------------------
/.mvn/wrapper/maven-wrapper.properties:
--------------------------------------------------------------------------------
1 | distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.6.0/apache-maven-3.6.0-bin.zip
2 |
--------------------------------------------------------------------------------
/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=dev
3 |
4 | # logback
5 | logging.config=classpath:logback-spring-${spring.profiles.active}.xml
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ### IDEA ###
2 | .idea/
3 | *.iml
4 | */target/
5 | target/
6 | */*.iml
7 | */.gradle/
8 | */out/
9 | out/
10 | ### Java ###
11 | .settings/
12 | .classpath
13 | .project
14 | bin/
15 | build/
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/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 | @Target(ElementType.METHOD)
9 | @Retention(RetentionPolicy.RUNTIME)
10 | public @interface LoginRequired {
11 |
12 |
13 |
14 | }
15 |
--------------------------------------------------------------------------------
/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 | //这些注解表示启用了Spring的线程池,并且开启定时任务
8 | @Configuration
9 | @EnableScheduling
10 | @EnableAsync
11 | public class ThreadPoolConfig {
12 | }
13 |
--------------------------------------------------------------------------------
/src/main/resources/templates/mail/forget.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 忘记密码
6 |
7 |
8 |
9 |
10 | xxx@xxx.com, 您好!
11 |
12 |
13 | 您正在找回Community的密码, 本次操作的验证码为 u5s6dt ,
14 | 有效时间10分钟, 请您及时进行操作!
15 |
16 |
17 |
18 |
--------------------------------------------------------------------------------
/src/main/resources/templates/mail/activation.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | 激活账号
6 |
7 |
8 |
9 |
10 | xxx@xxx.com, 您好!
11 |
12 |
13 | 您正在注册Community, 这是一封激活邮件, 请点击
14 | 此链接,
15 | 激活您的Community账号!
16 |
17 |
18 |
19 |
--------------------------------------------------------------------------------
/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 | @Repository
8 | public interface DiscussPostRepository extends ElasticsearchRepository {
9 |
10 | }
11 |
--------------------------------------------------------------------------------
/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 | public class AlphaJob implements Job {
8 | @Override
9 | public void execute(JobExecutionContext context) throws JobExecutionException {
10 | System.out.println(Thread.currentThread().getName() + ": execute a quartz job.");
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/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/test/java/com/nowcoder/community/WkTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import java.io.IOException;
4 |
5 | public class WkTests {
6 |
7 | public static void main(String[] args) {
8 | String cmd = "d:/work/wkhtmltopdf/bin/wkhtmltoimage --quality 75 https://www.nowcoder.com d:/work/data/wk-images/3.png";
9 | try {
10 | Runtime.getRuntime().exec(cmd);
11 | System.out.println("ok.");
12 | } catch (IOException e) {
13 | e.printStackTrace();
14 | }
15 | }
16 |
17 | }
18 |
--------------------------------------------------------------------------------
/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/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 | //@Component表示放在容器中,由spring进行管理
11 | public class HostHolder {
12 |
13 | private ThreadLocal users = new ThreadLocal<>();
14 |
15 | public void setUser(User user) {
16 | users.set(user);
17 | }
18 |
19 | public User getUser() {
20 | return users.get();
21 | }
22 |
23 | public void clear() {
24 | users.remove();
25 | }
26 |
27 | }
28 |
--------------------------------------------------------------------------------
/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 | @Component
10 | public class EventProducer {
11 |
12 | @Autowired
13 | private KafkaTemplate kafkaTemplate;
14 |
15 | // 处理事件
16 | public void fireEvent(Event event) {
17 | // 将事件发布到指定的主题,消息的内容是JSON字符串
18 | kafkaTemplate.send(event.getTopic(), JSONObject.toJSONString(event));
19 | }
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/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.apache.ibatis.annotations.Param;
6 |
7 | @Mapper
8 | public interface UserMapper {
9 | //一般都加上@Param比较保险
10 |
11 | User selectById(@Param("id")int id);
12 |
13 | User selectByName(@Param("username")String username);
14 |
15 | User selectByEmail(@Param("email")String email);
16 |
17 | int insertUser(User user);
18 |
19 | int updateStatus(@Param("id")int id, @Param("status")int status);
20 |
21 | int updateHeader(@Param("id")int id, @Param("headerUrl")String headerUrl);
22 |
23 | int updatePassword(@Param("id")int id,@Param("password") String password);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/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 | //这个工具是从request对象取cookies数组,然后取出ticket
7 | public class CookieUtil {
8 |
9 | public static String getValue(HttpServletRequest request, String name) {
10 | if (request == null || name == null) {
11 | throw new IllegalArgumentException("参数为空!");
12 | }
13 |
14 | Cookie[] cookies = request.getCookies();
15 | if (cookies != null) {
16 | for (Cookie cookie : cookies) {
17 | if (cookie.getName().equals(name)) {
18 | return cookie.getValue();
19 | }
20 | }
21 | }
22 |
23 | return null;
24 | }
25 |
26 | }
27 |
--------------------------------------------------------------------------------
/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 提供异步的、基于事件驱动的网络应用程序框架,用以快速开发高性能、高可靠性的网络 IO 程序,
14 | 是目前最流行的 NIO 框架,Netty 在互联网领域、大数据分布式计算领域、游戏行业、通信行业等获得了广泛的应用,
15 | 知名的 Dubbo 、RocketMQ 框架内部都采用了 Netty。*/
16 | // 解决netty启动冲突问题
17 | // see Netty4Utils.setAvailableProcessors()
18 | System.setProperty("es.set.netty.runtime.available.processors", "false");
19 | }
20 |
21 | public static void main(String[] args) {
22 | SpringApplication.run(CommunityApplication.class, args);
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/WkConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 |
4 | import org.slf4j.Logger;
5 | import org.slf4j.LoggerFactory;
6 | import org.springframework.beans.factory.annotation.Value;
7 | import org.springframework.context.annotation.Configuration;
8 |
9 | import javax.annotation.PostConstruct;
10 | import java.io.File;
11 |
12 | @Configuration
13 | public class WkConfig {
14 |
15 | private static final Logger logger = LoggerFactory.getLogger(WkConfig.class);
16 |
17 | @Value("${wk.image.storage}")
18 | private String wkImageStorage;
19 |
20 | @PostConstruct //这个注解表示启动服务的时候,init()方法会调用一次
21 | public void init() {
22 | // 创建WK图片目录
23 | File file = new File(wkImageStorage);
24 | if (!file.exists()) {
25 | file.mkdir();
26 | logger.info("创建WK图片目录: " + wkImageStorage);
27 | }
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/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 | @RunWith(SpringRunner.class)
12 | @SpringBootTest
13 | @ContextConfiguration(classes = CommunityApplication.class)
14 | public class LoggerTests {
15 |
16 | private static final Logger logger = LoggerFactory.getLogger(LoggerTests.class);
17 |
18 | @Test
19 | public void testLogger() {
20 | System.out.println(logger.getName());
21 |
22 | logger.debug("debug log");
23 | logger.info("info log");
24 | logger.warn("warn log");
25 | logger.error("error log");
26 | }
27 |
28 | }
29 |
--------------------------------------------------------------------------------
/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 | import org.apache.ibatis.annotations.Param;
6 |
7 | import java.util.List;
8 |
9 | @Mapper
10 | public interface CommentMapper {
11 |
12 | List selectCommentsByEntity(@Param("entityType")int entityType, @Param("entityId")int entityId, @Param("offset")int offset, @Param("limit")int limit);
13 |
14 | int selectCountByEntity(@Param("entityType")int entityType, @Param("entityId")int entityId);
15 |
16 | int insertComment(Comment comment);
17 |
18 | Comment selectCommentById(@Param("id")int id);
19 |
20 | int selectCommentCountById(@Param("id")int id);
21 |
22 | List selectCommentsByUserId(@Param("id")int id,@Param("offset")int offset,@Param("limit")int limit);
23 |
24 | int updateStatus(@Param("entityId")int entityId, @Param("status")int status);
25 | }
26 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/TransactionTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import com.nowcoder.community.service.AlphaService;
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 |
11 | @RunWith(SpringRunner.class)
12 | @SpringBootTest
13 | @ContextConfiguration(classes = CommunityApplication.class)
14 | public class TransactionTests {
15 |
16 | @Autowired
17 | private AlphaService alphaService;
18 |
19 | @Test
20 | public void testSave1() {
21 | Object obj = alphaService.save1();
22 | System.out.println(obj);
23 | }
24 |
25 | @Test
26 | public void testSave2() {
27 | Object obj = alphaService.save2();
28 | System.out.println(obj);
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/resources/static/html/ajax-demo.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | AJAX
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
31 |
32 |
--------------------------------------------------------------------------------
/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 | @Test
22 | public void testDeleteJob() {
23 | try {
24 | boolean result = scheduler.deleteJob(new JobKey("alphaJob", "alphaJobGroup"));
25 | System.out.println(result);
26 | } catch (SchedulerException e) {
27 | e.printStackTrace();
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/SensitiveTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import com.nowcoder.community.util.SensitiveFilter;
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 |
11 | @RunWith(SpringRunner.class)
12 | @SpringBootTest
13 | @ContextConfiguration(classes = CommunityApplication.class)
14 | public class SensitiveTests {
15 |
16 | @Autowired
17 | private SensitiveFilter sensitiveFilter;
18 |
19 | @Test
20 | public void testSensitiveFilter() {
21 | String text = "这里可以赌博,可以嫖娼,可以吸毒,可以开票,哈哈哈!";
22 | text = sensitiveFilter.filter(text);
23 | System.out.println(text);
24 |
25 | text = "这里可以☆赌☆博☆,可以☆嫖☆娼☆,可以☆吸☆毒☆,可以☆开☆票☆,哈哈哈!";
26 | text = sensitiveFilter.filter(text);
27 | System.out.println(text);
28 | }
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/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().replace(/\r\n/g, '
').replace(/\n/g, '
').replace(/\s/g, ' ');
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/resources/static/js/forget.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $(".feach-btn").click(feach);
3 | });
4 |
5 | function feach() {
6 | var btn = this;
7 | var count = 60;
8 | const countDown = setInterval(() => {
9 | if(count===60){
10 | $.post(
11 | CONTEXT_PATH + "/user/sendCode?p="+Math.random(),
12 | {"email":$("#email").val()},
13 | function(data) {
14 | data = $.parseJSON(data);
15 | if(data.code != 0) {
16 | alert(data.msg);
17 | }
18 | }
19 | );
20 | }
21 | if (count === 0) {
22 | $(btn).text('重新发送').removeAttr('disabled');
23 | $(btn).css({
24 | background: '#ff9400',
25 | color: '#fff',
26 | });
27 | clearInterval(countDown);
28 | } else {
29 | $(btn).attr('disabled', true);
30 | $(btn).css({
31 | background: '#d8d8d8',
32 | color: '#707070',
33 | });
34 | $(btn).text(count + '秒后可重新获取');
35 | }
36 | count--;
37 | }, 1000);
38 | }
39 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 David
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/setting.js:
--------------------------------------------------------------------------------
1 | $(function(){
2 | $("#uploadForm").submit(upload);
3 | });
4 |
5 | //$.ajax是一种比$.post更为强大的异步请求方法,因为文件所需的参数更多了
6 | function upload() {
7 | $.ajax({
8 | url: "http://upload-z1.qiniup.com",
9 | method: "post",
10 | processData: false,
11 | contentType: false,
12 | data: new FormData($("#uploadForm")[0]),
13 | success: function(data) {
14 | if(data && data.code == 0) {
15 | // 更新头像访问路径
16 | $.post(
17 | CONTEXT_PATH + "/user/header/url",
18 | {"fileName":$("input[name='key']").val()},
19 | function(data) {
20 | data = $.parseJSON(data);
21 | if(data.code == 0) {
22 | window.location.reload();
23 | } else {
24 | alert(data.msg);
25 | }
26 | }
27 | );
28 | } else {
29 | alert("上传失败!");
30 | }
31 | }
32 | });
33 | return false;
34 | }
--------------------------------------------------------------------------------
/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 | @Mapper
7 | //弃用,因为重构代码将用户登录信息存到redis中,为了提高效率
8 | public interface LoginTicketMapper {
9 |
10 | @Insert({
11 | "insert into login_ticket(user_id,ticket,status,expired) ",
12 | "values(#{userId},#{ticket},#{status},#{expired})"
13 | })
14 | @Options(useGeneratedKeys = true, keyProperty = "id")
15 | int insertLoginTicket(LoginTicket loginTicket);
16 |
17 | @Select({
18 | "select id,user_id,ticket,status,expired ",
19 | "from login_ticket where ticket=#{ticket}"
20 | })
21 | LoginTicket selectByTicket(@Param("ticket")String ticket);
22 |
23 | @Update({
24 | ""
30 | })
31 | int updateStatus(@Param("ticket")String ticket, @Param("status")int status);
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/resources/static/js/global.js:
--------------------------------------------------------------------------------
1 | var CONTEXT_PATH = "/community";
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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # community
2 | # 社区类型后端项目,可用于本科毕业设计,校招或实习面试项目
3 |
4 |
5 |
6 | 涉及到Spring、SpringMVC、Mybatis的整合,以及SpringBoot去简化Spring的配置开发
7 |
8 | 主要的技术点:
9 |
10 | 登录注册功能:使用kaptcha去生成验证码,使用邮件完成注册,Redis优化验证码的保存,解决分布式session问题
11 |
12 | 使用拦截器拦截用户请求,将用户信息绑定在ThreadLocal上
13 |
14 | 构建Trie数据结构,实现对发表帖子评论的敏感词过滤
15 |
16 | 支持对帖子评论,也支持对评论进行回复
17 |
18 | 利用AOP对service的业务代码实现日志记录
19 |
20 | 利用Redis的zset并结合Redis实现点赞关注的功能
21 |
22 | 点赞关注后的系统通知,实时性不需要特别高,使用kafka实现异步的发送系统通知
23 |
24 | 使用ElasticSearch实现对帖子的搜索功能,以及结果的高亮显示
25 |
26 | SpringQuartz实现定时任务,完成热门帖子的分数计算模块
27 |
28 | 使用本地缓存Quartz缓存热门帖子优化热门帖子页面,提高了QPS(10 - 200)
29 | 
30 | 
31 | 
32 | 
33 | 
34 |
--------------------------------------------------------------------------------
/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 | //orderMode排序模式,默认为0。1是按帖子热度排序
13 | List selectDiscussPosts(
14 | @Param("userId") int userId, @Param("offset")int offset, @Param("limit")int limit, @Param("orderMode")int orderMode);
15 |
16 | // @Param注解用于给参数取别名,只有一个变量可以不加,要是有两个一定要加,所以一般都加上比较保险
17 | // sql在里使用,则所有参数必须加别名.
18 | int selectDiscussPostRows(@Param("userId") int userId);
19 |
20 | int insertDiscussPost(DiscussPost discussPost);
21 |
22 | DiscussPost selectDiscussPostById(@Param("id")int id);
23 |
24 | int updateCommentCount(@Param("id")int id, @Param("commentCount")int commentCount);
25 |
26 | int updateType(@Param("id")int id,@Param("type") int type);
27 |
28 | int updateStatus(@Param("id")int id, @Param("status")int status);
29 |
30 | int updateScore(@Param("id")int id, @Param("score")double score);
31 | }
32 |
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/SortTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import java.util.Arrays;
4 |
5 | public class SortTests {
6 | public static void main(String[] args) {
7 | int arr[]={50,90,-8,100,-78,5};
8 | quickSort(arr,0,arr.length-1);
9 | System.out.println(Arrays.toString(arr));
10 | }
11 |
12 | public static void quickSort(int[] arr, int left, int right) {
13 | int l=left;
14 | int r=right;
15 | int pivot=arr[(left+right)/2];
16 | int temp=0;
17 | while (lpivot)
21 | r--;
22 | if(l>=r)
23 | break;
24 | temp=arr[l];
25 | arr[l]=arr[r];
26 | arr[r]=temp;
27 | if(arr[l]==pivot)
28 | r--;
29 | if(arr[r]==pivot)
30 | l++;
31 | }
32 | if(l==r){
33 | r--;
34 | l++;
35 | }
36 | if(leftl){
40 | quickSort(arr,l,right);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/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 |
9 | @Configuration
10 | public class RedisConfig {
11 |
12 | @Bean
13 | //@Bean的作用是:在服务启动时,将RedisTemplate交给spring管理
14 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) {
15 | RedisTemplate template = new RedisTemplate<>();
16 | template.setConnectionFactory(factory);
17 |
18 | // 设置key的序列化方式
19 | template.setKeySerializer(RedisSerializer.string());
20 | // 设置value的序列化方式
21 | template.setValueSerializer(RedisSerializer.json());
22 | // 设置hash的key的序列化方式
23 | template.setHashKeySerializer(RedisSerializer.string());
24 | // 设置hash的value的序列化方式
25 | template.setHashValueSerializer(RedisSerializer.json());
26 |
27 | template.afterPropertiesSet();
28 | return template;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/DataInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import com.nowcoder.community.entity.User;
4 | import com.nowcoder.community.service.DataService;
5 | import com.nowcoder.community.util.HostHolder;
6 | import org.springframework.beans.factory.annotation.Autowired;
7 | import org.springframework.stereotype.Component;
8 | import org.springframework.web.servlet.HandlerInterceptor;
9 |
10 | import javax.servlet.http.HttpServletRequest;
11 | import javax.servlet.http.HttpServletResponse;
12 |
13 | @Component
14 | public class DataInterceptor implements HandlerInterceptor {
15 |
16 | @Autowired
17 | private DataService dataService;
18 |
19 | @Autowired
20 | private HostHolder hostHolder;
21 |
22 | //在Controller之前执行
23 | @Override
24 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
25 | // 统计UV
26 | String ip = request.getRemoteHost();
27 | dataService.recordUV(ip);
28 |
29 | // 统计DAU
30 | User user = hostHolder.getUser();
31 | if (user != null) {
32 | dataService.recordDAU(user.getId());
33 | }
34 |
35 | return true;
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/aspect/AlphaAspect.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.aspect;
2 |
3 | import org.aspectj.lang.ProceedingJoinPoint;
4 | import org.aspectj.lang.annotation.*;
5 | import org.springframework.stereotype.Component;
6 |
7 | //@Component
8 | //@Aspect
9 | public class AlphaAspect {
10 |
11 | //对所有的业务组件统一处理
12 | @Pointcut("execution(* com.community.service.*.*(..))")
13 | //切点是service包下所有的方法
14 | public void pointcut() {
15 |
16 | }
17 |
18 | @Before("pointcut()")
19 | public void before() {
20 | System.out.println("before");
21 | }
22 |
23 | @After("pointcut()")
24 | public void after() {
25 | System.out.println("after");
26 | }
27 |
28 | @AfterReturning("pointcut()")
29 | public void afterRetuning() {
30 | System.out.println("afterRetuning");
31 | }
32 |
33 | @AfterThrowing("pointcut()")
34 | public void afterThrowing() {
35 | System.out.println("afterThrowing");
36 | }
37 |
38 | @Around("pointcut()")
39 | public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
40 | System.out.println("around before");
41 | Object obj = joinPoint.proceed();
42 | System.out.println("around after");
43 | return obj;
44 | }
45 |
46 | }
47 |
--------------------------------------------------------------------------------
/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 | @Autowired
18 | private MailClient mailClient;
19 |
20 | @Autowired
21 | private TemplateEngine templateEngine;
22 |
23 | @Test
24 | public void testEmailSend(){
25 | mailClient.sendMail("david_zhongdawei@163.com","test","welcome");
26 | }
27 |
28 | @Test
29 | public void testHtmlEmail(){
30 | Context context = new Context();
31 | context.setVariable("username", "sunday");
32 |
33 | String content = templateEngine.process("/mail/demo", context);
34 | System.out.println(content);
35 |
36 | mailClient.sendMail("david_zhongdawei@163.com", "HTML", content);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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 | @Configuration
12 | public class KaptchaConfig {
13 |
14 | @Bean
15 | public Producer kaptchaProducer() {
16 | Properties properties = new Properties();
17 | properties.setProperty("kaptcha.image.width", "100");
18 | properties.setProperty("kaptcha.image.height", "40");
19 | properties.setProperty("kaptcha.textproducer.font.size", "32");
20 | properties.setProperty("kaptcha.textproducer.font.color", "0,0,0");
21 | properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ");
22 | properties.setProperty("kaptcha.textproducer.char.length", "4");
23 | properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise");
24 |
25 | DefaultKaptcha kaptcha = new DefaultKaptcha();
26 | Config config = new Config(properties);
27 | kaptcha.setConfig(config);
28 | return kaptcha;
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/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().replace(/\r\n/g, '
').replace(/\n/g, '
').replace(/\s/g, ' ');
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/profile.js:
--------------------------------------------------------------------------------
1 | //在profile.html中,通过button的class属性获取对象
2 | $(function(){
3 | $(".follow-btn").click(follow);
4 | });
5 |
6 |
7 | function follow() {
8 | var btn = this;
9 | if($(btn).hasClass("btn-info")) {
10 | // 关注TA $(btn).prev()获取前一个结点的值,就是用户id
11 | $.post(
12 | CONTEXT_PATH + "/follow",
13 | {"entityType":3,"entityId":$(btn).prev().val()},
14 | function(data) {
15 | data = $.parseJSON(data);
16 | if(data.code == 0) {
17 | window.location.reload();
18 | } else {
19 | alert(data.msg);
20 | }
21 | }
22 | );
23 | // $(btn).text("已关注").removeClass("btn-info").addClass("btn-secondary");
24 | } else {
25 | // 取消关注
26 | $.post(
27 | CONTEXT_PATH + "/unfollow",
28 | {"entityType":3,"entityId":$(btn).prev().val()},
29 | function(data) {
30 | data = $.parseJSON(data);
31 | if(data.code == 0) {
32 | window.location.reload();
33 | } else {
34 | alert(data.msg);
35 | }
36 | }
37 | );
38 | //$(btn).text("关注TA").removeClass("btn-secondary").addClass("btn-info");
39 | }
40 | }
--------------------------------------------------------------------------------
/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 | //@Component使用容器管理,用@Autowired获取对象
16 | @Component
17 | @Endpoint(id = "database")
18 | public class DatabaseEndpoint {
19 |
20 | private static final Logger logger = LoggerFactory.getLogger(DatabaseEndpoint.class);
21 |
22 | //连接池由Spring进行管理,直接注入进来即可
23 | @Autowired
24 | private DataSource dataSource;
25 |
26 | @ReadOperation
27 | public String checkConnection() {
28 | //加在小括号里的资源,结束的时候会自动关闭
29 | try (
30 | Connection conn = dataSource.getConnection();
31 | ) {
32 | return CommunityUtil.getJSONString(0, "获取连接成功!");
33 | } catch (SQLException e) {
34 | logger.error("获取连接失败:" + e.getMessage());
35 | return CommunityUtil.getJSONString(1, "获取连接失败!");
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/resources/templates/error/404.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 404
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
![]()
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/src/main/resources/templates/error/500.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 | 500
10 |
11 |
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 |
![]()
22 |
23 |
24 |
25 |
26 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
--------------------------------------------------------------------------------
/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.internet.InternetAddress;
12 | import javax.mail.internet.MimeMessage;
13 |
14 | @Component
15 | public class MailClient {
16 |
17 | private static final Logger logger= LoggerFactory.getLogger(MailClient.class);
18 |
19 | @Autowired
20 | private JavaMailSender mailSender;
21 |
22 | @Value("${spring.mail.username}")
23 | private String from;
24 |
25 | public void sendMail(String to,String subject,String content){
26 | try {
27 | MimeMessage message = mailSender.createMimeMessage();
28 | MimeMessageHelper helper = new MimeMessageHelper(message);
29 | helper.setFrom(new InternetAddress(from,"community","UTF-8"));
30 | helper.setTo(to);
31 | helper.setSubject(subject);
32 | helper.setText(content, true);
33 | mailSender.send(helper.getMimeMessage());
34 | } catch (Exception e) {
35 | logger.error("发送邮件失败:" + e.getMessage());
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/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/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 | //生成随机字符串,32位
13 | public static String generateUUID(){
14 | return UUID.randomUUID().toString().replaceAll("-","");
15 | }
16 |
17 | //MD5加密,采用spring自带的md5加密方法。一般还需要加盐,确保安全
18 | public static String md5(String key){
19 | //如果密码为null或空格,返回null
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 | }
47 |
--------------------------------------------------------------------------------
/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.stereotype.Component;
8 | import org.springframework.web.servlet.HandlerInterceptor;
9 | import org.springframework.web.servlet.ModelAndView;
10 |
11 | import javax.servlet.http.HttpServletRequest;
12 | import javax.servlet.http.HttpServletResponse;
13 |
14 | @Component
15 | public class MessageInterceptor implements HandlerInterceptor {
16 |
17 | @Autowired
18 | private HostHolder hostHolder;
19 |
20 | @Autowired
21 | private MessageService messageService;
22 |
23 | //在Controller之后执行,写完拦截器后要在WebMvcConfig中配置
24 | @Override
25 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
26 | User user = hostHolder.getUser();
27 | if (user != null && modelAndView != null) {
28 | int letterUnreadCount = messageService.findLetterUnreadCount(user.getId(), null);
29 | int noticeUnreadCount = messageService.findNoticeUnreadCount(user.getId(), null);
30 | modelAndView.addObject("allUnreadCount", letterUnreadCount + noticeUnreadCount);
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/LoginTicket.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | public class LoginTicket {
6 | private int id;
7 | private int userId;
8 | private String ticket;
9 | private int status;
10 | private Date expired;
11 |
12 | public int getId() {
13 | return id;
14 | }
15 |
16 | public void setId(int id) {
17 | this.id = id;
18 | }
19 |
20 | public int getUserId() {
21 | return userId;
22 | }
23 |
24 | public void setUserId(int userId) {
25 | this.userId = userId;
26 | }
27 |
28 | public String getTicket() {
29 | return ticket;
30 | }
31 |
32 | public void setTicket(String ticket) {
33 | this.ticket = ticket;
34 | }
35 |
36 | public int getStatus() {
37 | return status;
38 | }
39 |
40 | public void setStatus(int status) {
41 | this.status = status;
42 | }
43 |
44 | public Date getExpired() {
45 | return expired;
46 | }
47 |
48 | public void setExpired(Date expired) {
49 | this.expired = expired;
50 | }
51 |
52 | @Override
53 | public String toString() {
54 | return "LoginTicket{" +
55 | "id=" + id +
56 | ", userId=" + userId +
57 | ", ticket='" + ticket + '\'' +
58 | ", status=" + status +
59 | ", expired=" + expired +
60 | '}';
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/src/main/resources/templates/site/operate.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 操作结果
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
您的账号已经激活成功,可以正常使用了!
24 |
25 |
26 |
27 |
28 |
29 |
30 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/controller/interceptor/LoginRequiredInterceptor.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.controller.interceptor;
2 |
3 | import com.nowcoder.community.util.HostHolder;
4 | import com.nowcoder.community.annotation.LoginRequired;
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 | //对需要登录才能访问的资源进行拦截,已弃用,改为Spring Security管理
15 | @Component
16 | public class LoginRequiredInterceptor implements HandlerInterceptor {
17 |
18 | @Autowired
19 | private HostHolder hostHolder;
20 |
21 | @Override
22 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
23 | //instanceof代表拦截器只对方法进行拦截,对静态资源不进行拦截
24 | if (handler instanceof HandlerMethod) {
25 | HandlerMethod handlerMethod = (HandlerMethod) handler;
26 | Method method = handlerMethod.getMethod();
27 | LoginRequired loginRequired = method.getAnnotation(LoginRequired.class);
28 | if (loginRequired != null && hostHolder.getUser() == null) {
29 | response.sendRedirect(request.getContextPath() + "/login");
30 | return false;
31 | }
32 | }
33 | return true;
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/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 | import org.apache.ibatis.annotations.Param;
6 |
7 | import java.util.List;
8 |
9 | @Mapper
10 | public interface MessageMapper {
11 |
12 | // 查询当前用户的会话列表,针对每个会话只返回一条最新的私信.
13 | List selectConversations(@Param("userId")int userId, @Param("offset")int offset, @Param("limit")int limit);
14 |
15 | // 查询当前用户的会话数量.
16 | int selectConversationCount(@Param("userId")int userId);
17 |
18 | // 查询某个会话所包含的私信列表.
19 | List selectLetters(@Param("conversationId")String conversationId, @Param("offset")int offset,@Param("limit") int limit);
20 |
21 | // 查询某个会话所包含的私信数量.
22 | int selectLetterCount(@Param("conversationId")String conversationId);
23 |
24 | // 查询未读私信的数量
25 | int selectLetterUnreadCount(@Param("userId")int userId, @Param("conversationId")String conversationId);
26 |
27 | // 新增消息
28 | int insertMessage(Message message);
29 |
30 | // 修改消息的状态
31 | int updateStatus(@Param("ids")List ids,@Param("status") int status);
32 |
33 | // 查询某个主题下最新的通知
34 | Message selectLatestNotice(@Param("userId")int userId, @Param("topic")String topic);
35 |
36 | // 查询某个主题所包含的通知数量
37 | int selectNoticeCount(@Param("userId")int userId, @Param("topic")String topic);
38 |
39 | // 查询未读的通知的数量
40 | int selectNoticeUnreadCount(@Param("userId")int userId,@Param("topic") String topic);
41 |
42 | // 查询某个主题所包含的通知列表
43 | List selectNotices(@Param("userId")int userId, @Param("topic")String topic,@Param("offset") int offset, @Param("limit")int limit);
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/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 | @ControllerAdvice(annotations = Controller.class)
16 | //这个组件去扫描所有带有controller注解的那些bean。这是统一处理异常的类
17 | public class ExceptionAdvice {
18 |
19 | private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class);
20 |
21 | @ExceptionHandler({Exception.class})
22 | public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException {
23 | logger.error("服务器发生异常: " + e.getMessage());
24 | for (StackTraceElement element : e.getStackTrace()) {
25 | logger.error(element.toString());
26 | }
27 |
28 | String xRequestedWith = request.getHeader("x-requested-with");
29 | //"XMLHttpRequest".equals(xRequestedWith)说明是一个异步请求。代码有误就会报服务器异常
30 | if ("XMLHttpRequest".equals(xRequestedWith)) {
31 | response.setContentType("application/plain;charset=utf-8");
32 | PrintWriter writer = response.getWriter();
33 | writer.write(CommunityUtil.getJSONString(1, "服务器异常!"));
34 | } else {
35 | response.sendRedirect(request.getContextPath() + "/error");
36 | }
37 | }
38 |
39 | }
40 |
--------------------------------------------------------------------------------
/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 | @Component
18 | @Aspect
19 | //这是统一日志处理的类,利用AOP切面技术,在访问service方法时会执行pointcut。定义了一个切面类
20 | public class ServiceLogAspect {
21 |
22 | private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class);
23 |
24 | @Pointcut("execution(* com.community.service.*.*(..))")
25 | public void pointcut() {
26 |
27 | }
28 |
29 | @Before("pointcut()")
30 | public void before(JoinPoint joinPoint) {
31 | // 用户[1.2.3.4],在[xxx],访问了[com.community.service.xxx()].
32 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
33 | if (attributes == null) {
34 | return;
35 | }
36 | HttpServletRequest request = attributes.getRequest();
37 | String ip = request.getRemoteHost();
38 | String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date());
39 | String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName();
40 | logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target));
41 | }
42 |
43 | }
44 |
--------------------------------------------------------------------------------
/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.context.SpringBootTest;
8 | import org.springframework.kafka.annotation.KafkaListener;
9 | import org.springframework.kafka.core.KafkaTemplate;
10 | import org.springframework.stereotype.Component;
11 | import org.springframework.test.context.ContextConfiguration;
12 | import org.springframework.test.context.junit4.SpringRunner;
13 |
14 | @RunWith(SpringRunner.class)
15 | @SpringBootTest
16 | @ContextConfiguration(classes = CommunityApplication.class)
17 | public class KafkaTests {
18 |
19 | @Autowired
20 | private KafkaProducer kafkaProducer;
21 |
22 | @Test
23 | public void testKafka() {
24 | kafkaProducer.sendMessage("test", "Hello!");
25 | kafkaProducer.sendMessage("test", "Are you there?");
26 |
27 | //生产者发消息是我们主动去调的,消费者消费消息是被动去调的。因为阻塞了10s(try catch),所以程序10s后才结束
28 | try {
29 | //1000ms*10 10s
30 | Thread.sleep(1000 * 10);
31 | } catch (InterruptedException e) {
32 | e.printStackTrace();
33 | }
34 | }
35 |
36 | }
37 |
38 | @Component
39 | class KafkaProducer {
40 |
41 | @Autowired
42 | private KafkaTemplate kafkaTemplate;
43 |
44 | public void sendMessage(String topic, String content) {
45 | kafkaTemplate.send(topic, content);
46 | }
47 |
48 | }
49 |
50 | @Component
51 | class KafkaConsumer {
52 |
53 | @KafkaListener(topics = {"test"})
54 | public void handleMessage(ConsumerRecord record) {
55 | System.out.println(record.value());
56 | }
57 |
58 |
59 | }
--------------------------------------------------------------------------------
/src/test/java/com/nowcoder/community/BlockingQueueTests.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community;
2 |
3 | import java.util.Random;
4 | import java.util.concurrent.ArrayBlockingQueue;
5 | import java.util.concurrent.BlockingQueue;
6 |
7 | public class BlockingQueueTests {
8 |
9 | public static void main(String[] args) {
10 | BlockingQueue queue = new ArrayBlockingQueue(10);
11 | new Thread(new Producer(queue)).start();
12 | new Thread(new Consumer(queue)).start();
13 | new Thread(new Consumer(queue)).start();
14 | new Thread(new Consumer(queue)).start();
15 | }
16 |
17 | }
18 |
19 | class Producer implements Runnable {
20 |
21 | private BlockingQueue queue;
22 |
23 | public Producer(BlockingQueue queue) {
24 | this.queue = queue;
25 | }
26 |
27 | @Override
28 | public void run() {
29 | try {
30 | for (int i = 0; i < 100; i++) {
31 | Thread.sleep(20);
32 | queue.put(i);
33 | System.out.println(Thread.currentThread().getName() + "生产:" + queue.size());
34 | }
35 | } catch (Exception e) {
36 | e.printStackTrace();
37 | }
38 | }
39 |
40 | }
41 |
42 | class Consumer implements Runnable {
43 |
44 | private BlockingQueue queue;
45 |
46 | public Consumer(BlockingQueue queue) {
47 | this.queue = queue;
48 | }
49 |
50 | @Override
51 | public void run() {
52 | try {
53 | while (true) {
54 | Thread.sleep(new Random().nextInt(1000));
55 | queue.take();
56 | System.out.println(Thread.currentThread().getName() + "消费:" + queue.size());
57 | }
58 | } catch (Exception e) {
59 | e.printStackTrace();
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/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 | public class Event {
7 |
8 | //事件主题,是评论、点赞还是关注
9 | private String topic;
10 | //事件是由谁触发的 userId
11 | private int userId;
12 | //事件实体的类型,是帖子、评论还是用户 entityType
13 | private int entityType;
14 | //事件实体的id entityId
15 | private int entityId;
16 | //系统消息发送的对象,entityUserId
17 | private int entityUserId;
18 | private Map data = new HashMap<>();
19 |
20 | public String getTopic() {
21 | return topic;
22 | }
23 |
24 | public Event setTopic(String topic) {
25 | this.topic = topic;
26 | return this;
27 | }
28 |
29 | public int getUserId() {
30 | return userId;
31 | }
32 |
33 | public Event setUserId(int userId) {
34 | this.userId = userId;
35 | return this;
36 | }
37 |
38 | public int getEntityType() {
39 | return entityType;
40 | }
41 |
42 | public Event setEntityType(int entityType) {
43 | this.entityType = entityType;
44 | return this;
45 | }
46 |
47 | public int getEntityId() {
48 | return entityId;
49 | }
50 |
51 | public Event setEntityId(int entityId) {
52 | this.entityId = entityId;
53 | return this;
54 | }
55 |
56 | public int getEntityUserId() {
57 | return entityUserId;
58 | }
59 |
60 | public Event setEntityUserId(int entityUserId) {
61 | this.entityUserId = entityUserId;
62 | return this;
63 | }
64 |
65 | public Map getData() {
66 | return data;
67 | }
68 |
69 | public Event setData(String key, Object value) {
70 | this.data.put(key, value);
71 | return this;
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/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/util/CommunityConstant.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.util;
2 |
3 | public interface CommunityConstant {
4 | //constant 常量 activation 激活
5 |
6 | /**
7 | * 激活成功
8 | */
9 | int ACTIVATION_SUCCESS = 0;
10 |
11 | /**
12 | * 重复激活
13 | */
14 | int ACTIVATION_REPEAT = 1;
15 |
16 | /**
17 | * 激活失败
18 | */
19 | int ACTIVATION_FAILURE = 2;
20 |
21 | /**
22 | * 默认状态的登录凭证的超时时间,12小时
23 | */
24 | int DEFAULT_EXPIRED_SECONDS = 3600 * 12;
25 |
26 | /**
27 | * 记住状态的登录凭证超时时间,100天
28 | */
29 | int REMEMBER_EXPIRED_SECONDS = 3600 * 24 * 100;
30 |
31 | /**
32 | * 实体类型: 帖子
33 | */
34 | int ENTITY_TYPE_POST = 1;
35 |
36 | /**
37 | * 实体类型: 评论
38 | */
39 | int ENTITY_TYPE_COMMENT = 2;
40 |
41 | /**
42 | * 实体类型: 用户
43 | */
44 | int ENTITY_TYPE_USER = 3;
45 |
46 | /**
47 | * 主题: 评论
48 | */
49 | String TOPIC_COMMENT = "comment";
50 |
51 | /**
52 | * 主题: 点赞
53 | */
54 | String TOPIC_LIKE = "like";
55 |
56 | /**
57 | * 主题: 关注
58 | */
59 | String TOPIC_FOLLOW = "follow";
60 |
61 | /**
62 | * 主题: 发帖
63 | */
64 | String TOPIC_PUBLISH = "publish";
65 |
66 | /**
67 | * 主题: 删帖
68 | */
69 | String TOPIC_DELETE = "delete";
70 |
71 | /**
72 | * 主题: 分享
73 | */
74 | String TOPIC_SHARE = "share";
75 |
76 | /**
77 | * 系统用户ID 这个是数据库中一定要存在的用户,用于发送系统通知。账号system,密码system,一般不登录这个账户,使用这个账户发私信会有bug
78 | */
79 | int SYSTEM_USER_ID = 1;
80 |
81 | /**
82 | * 权限: 普通用户,数据库对应type 0
83 | */
84 | String AUTHORITY_USER = "user";
85 |
86 | /**
87 | * 权限: 管理员,数据库对应type 1
88 | */
89 | String AUTHORITY_ADMIN = "admin";
90 |
91 | /**
92 | * 权限: 版主,数据库对应type 2
93 | */
94 | String AUTHORITY_MODERATOR = "moderator";
95 | }
96 |
--------------------------------------------------------------------------------
/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.springframework.beans.factory.annotation.Autowired;
5 | import org.springframework.format.annotation.DateTimeFormat;
6 | import org.springframework.stereotype.Controller;
7 | import org.springframework.ui.Model;
8 | import org.springframework.web.bind.annotation.RequestMapping;
9 | import org.springframework.web.bind.annotation.RequestMethod;
10 |
11 | import java.util.Date;
12 |
13 | @Controller
14 | public class DataController {
15 |
16 | @Autowired
17 | private DataService dataService;
18 |
19 | // 统计页面,既可以处理Get请求,也可以处理Post请求
20 | @RequestMapping(path = "/data", method = {RequestMethod.GET, RequestMethod.POST})
21 | public String getDataPage() {
22 | return "/site/admin/data";
23 | }
24 |
25 | // 统计网站UV @DateTimeFormat将字符串格式的日期转为了日期格式
26 | @RequestMapping(path = "/data/uv", method = RequestMethod.POST)
27 | public String getUV(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,
28 | @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {
29 | long uv = dataService.calculateUV(start, end);
30 | model.addAttribute("uvResult", uv);
31 | model.addAttribute("uvStartDate", start);
32 | model.addAttribute("uvEndDate", end);
33 | //forward相当于一个转发的请求
34 | return "forward:/data";
35 | }
36 |
37 | // 统计活跃用户。日活跃用户数DAU
38 | @RequestMapping(path = "/data/dau", method = RequestMethod.POST)
39 | public String getDAU(@DateTimeFormat(pattern = "yyyy-MM-dd") Date start,
40 | @DateTimeFormat(pattern = "yyyy-MM-dd") Date end, Model model) {
41 | long dau = dataService.calculateDAU(start, end);
42 | model.addAttribute("dauResult", dau);
43 | model.addAttribute("dauStartDate", start);
44 | model.addAttribute("dauEndDate", end);
45 | return "forward:/data";
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Message.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | public class Message {
6 |
7 | private int id;
8 | private int fromId;
9 | private int toId;
10 | private String conversationId;
11 | private String content;
12 | private int status;
13 | private Date createTime;
14 |
15 | public int getId() {
16 | return id;
17 | }
18 |
19 | public void setId(int id) {
20 | this.id = id;
21 | }
22 |
23 | public int getFromId() {
24 | return fromId;
25 | }
26 |
27 | public void setFromId(int fromId) {
28 | this.fromId = fromId;
29 | }
30 |
31 | public int getToId() {
32 | return toId;
33 | }
34 |
35 | public void setToId(int toId) {
36 | this.toId = toId;
37 | }
38 |
39 | public String getConversationId() {
40 | return conversationId;
41 | }
42 |
43 | public void setConversationId(String conversationId) {
44 | this.conversationId = conversationId;
45 | }
46 |
47 | public String getContent() {
48 | return content;
49 | }
50 |
51 | public void setContent(String content) {
52 | this.content = content;
53 | }
54 |
55 | public int getStatus() {
56 | return status;
57 | }
58 |
59 | public void setStatus(int status) {
60 | this.status = status;
61 | }
62 |
63 | public Date getCreateTime() {
64 | return createTime;
65 | }
66 |
67 | public void setCreateTime(Date createTime) {
68 | this.createTime = createTime;
69 | }
70 |
71 | @Override
72 | public String toString() {
73 | return "Message{" +
74 | "id=" + id +
75 | ", fromId=" + fromId +
76 | ", toId=" + toId +
77 | ", conversationId='" + conversationId + '\'' +
78 | ", content='" + content + '\'' +
79 | ", status=" + status +
80 | ", createTime=" + createTime +
81 | '}';
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/src/main/resources/templates/site/operate-result.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 | 操作结果
11 |
12 |
13 |
14 |
15 |
18 |
19 |
20 |
21 |
22 |
23 |
您的账号已经激活成功,可以正常使用了!
24 |
25 |
26 | 系统会在 8 秒后自动跳转,
27 | 您也可以点此 链接, 跳转到首页!
28 |
29 |
30 |
31 |
32 |
33 |
34 |
37 |
38 |
39 |
40 |
41 |
42 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/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/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 {
24 | padding: 5px 0;
25 | font-size: 16px;
26 | }
27 |
28 | header .badge {
29 | position: absolute;
30 | top: -3px;
31 | left: 33px;
32 | }
33 |
34 | footer {
35 | padding: 20px 0;
36 | font-size: 12px;
37 | position: absolute;
38 | bottom: 0;
39 | width: 100%;
40 | }
41 |
42 | footer .qrcode {
43 | text-align: center;
44 | }
45 |
46 | footer .detail-info{
47 | border-left: 1px solid #888;
48 | }
49 |
50 | footer .company-info li {
51 | padding-left: 16px;
52 | margin: 4px 0;
53 | }
54 |
55 | .main {
56 | padding: 20px 0;
57 | padding-bottom: 200px;
58 | }
59 |
60 | .main .container {
61 | background: #fff;
62 | padding: 20px;
63 | }
64 |
65 | i {
66 | font-style: normal;
67 | }
68 |
69 | u {
70 | text-decoration: none;
71 | }
72 |
73 | b {
74 | font-weight: normal;
75 | }
76 |
77 | a {
78 | color: #000;
79 | }
80 |
81 | a:hover {
82 | text-decoration: none;
83 | }
84 |
85 | .font-size-12 {
86 | font-size: 12px;
87 | }
88 | .font-size-14 {
89 | font-size: 14px;
90 | }
91 | .font-size-16 {
92 | font-size: 16px;
93 | }
94 | .font-size-18 {
95 | font-size: 18px;
96 | }
97 | .font-size-20 {
98 | font-size: 20px;
99 | }
100 | .font-size-22 {
101 | font-size: 20px;
102 | }
103 | .font-size-24 {
104 | font-size: 20px;
105 | }
106 |
107 | .hidden {
108 | display: none;
109 | }
110 |
111 | .rt-0 {
112 | right: 0;
113 | top: 0;
114 | }
115 |
116 | .square {
117 | display: inline-block;
118 | width: 7px;
119 | height: 7px;
120 | background: #ff6547;
121 | margin-bottom: 2px;
122 | margin-right: 3px;
123 | }
124 |
125 | .bg-gray {
126 | background: #eff0f2;
127 | }
128 |
129 | .user-header {
130 | width: 50px;
131 | height: 50px;
132 | }
133 |
134 | em {
135 | font-style: normal;
136 | color: red;
137 | }
138 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/config/WebMvcConfig.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.config;
2 |
3 | import com.nowcoder.community.controller.interceptor.AlphaInterceptor;
4 | import com.nowcoder.community.controller.interceptor.DataInterceptor;
5 | import com.nowcoder.community.controller.interceptor.LoginTicketInterceptor;
6 | import com.nowcoder.community.controller.interceptor.MessageInterceptor;
7 | import com.community.controller.interceptor.*;
8 | import org.springframework.beans.factory.annotation.Autowired;
9 | import org.springframework.context.annotation.Configuration;
10 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
11 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
12 |
13 | @Configuration
14 | public class WebMvcConfig implements WebMvcConfigurer {
15 |
16 | @Autowired
17 | private AlphaInterceptor alphaInterceptor;
18 |
19 | @Autowired
20 | private LoginTicketInterceptor loginTicketInterceptor;
21 |
22 | // @Autowired
23 | // private LoginRequiredInterceptor loginRequiredInterceptor;
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 | //所有请求都要拦截,除了static下的静态资源不拦截
38 | registry.addInterceptor(loginTicketInterceptor)
39 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
40 |
41 | // registry.addInterceptor(loginRequiredInterceptor)
42 | // .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
43 |
44 | registry.addInterceptor(messageInterceptor)
45 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
46 |
47 | registry.addInterceptor(dataInterceptor)
48 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg");
49 | }
50 |
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/com/nowcoder/community/entity/Comment.java:
--------------------------------------------------------------------------------
1 | package com.nowcoder.community.entity;
2 |
3 | import java.util.Date;
4 |
5 | public class Comment {
6 |
7 | private int id;
8 | private int userId;
9 | private int entityType;
10 | private int entityId;
11 | private int targetId;
12 | private String content;
13 | private int status;
14 | private Date createTime;
15 |
16 | public int getId() {
17 | return id;
18 | }
19 |
20 | public void setId(int id) {
21 | this.id = id;
22 | }
23 |
24 | public int getUserId() {
25 | return userId;
26 | }
27 |
28 | public void setUserId(int userId) {
29 | this.userId = userId;
30 | }
31 |
32 | public int getEntityType() {
33 | return entityType;
34 | }
35 |
36 | public void setEntityType(int entityType) {
37 | this.entityType = entityType;
38 | }
39 |
40 | public int getEntityId() {
41 | return entityId;
42 | }
43 |
44 | public void setEntityId(int entityId) {
45 | this.entityId = entityId;
46 | }
47 |
48 | public int getTargetId() {
49 | return targetId;
50 | }
51 |
52 | public void setTargetId(int targetId) {
53 | this.targetId = targetId;
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 int getStatus() {
65 | return status;
66 | }
67 |
68 | public void setStatus(int status) {
69 | this.status = status;
70 | }
71 |
72 | public Date getCreateTime() {
73 | return createTime;
74 | }
75 |
76 | public void setCreateTime(Date createTime) {
77 | this.createTime = createTime;
78 | }
79 |
80 | @Override
81 | public String toString() {
82 | return "Comment{" +
83 | "id=" + id +
84 | ", userId=" + userId +
85 | ", entityType=" + entityType +
86 | ", entityId=" + entityId +
87 | ", targetId=" + targetId +
88 | ", content='" + content + '\'' +
89 | ", status=" + status +
90 | ", createTime=" + createTime +
91 | '}';
92 | }
93 | }
94 |
--------------------------------------------------------------------------------
/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 |
16 |
25 |
26 |
33 |
34 |
38 |
39 |
44 |
45 |
52 |
53 |
62 |
63 |
64 | update comment set status = #{status} where entity_id = #{entityId} and entity_type=1
65 |
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 |
9 | import java.util.List;
10 |
11 | @Service
12 | public class MessageService {
13 |
14 | @Autowired
15 | private MessageMapper messageMapper;
16 | @Autowired
17 | private SensitiveFilter sensitiveFilter;
18 |
19 | public List findConversations(int userId, int offset, int limit) {
20 | return messageMapper.selectConversations(userId, offset, limit);
21 | }
22 |
23 | public int findConversationCount(int userId) {
24 | return messageMapper.selectConversationCount(userId);
25 | }
26 |
27 | public List findLetters(String conversationId, int offset, int limit) {
28 | return messageMapper.selectLetters(conversationId, offset, limit);
29 | }
30 |
31 | public int findLetterCount(String conversationId) {
32 | return messageMapper.selectLetterCount(conversationId);
33 | }
34 |
35 | public int findLetterUnreadCount(int userId, String conversationId) {
36 | return messageMapper.selectLetterUnreadCount(userId, conversationId);
37 | }
38 |
39 | public int addMessage(Message message) {
40 | // message.setContent(HtmlUtils.htmlEscape(message.getContent()));
41 | message.setContent(sensitiveFilter.filter(message.getContent()));
42 | return messageMapper.insertMessage(message);
43 | }
44 |
45 | public int readMessage(List ids) {
46 | return messageMapper.updateStatus(ids, 1);
47 | }
48 |
49 | public Message findLatestNotice(int userId, String topic) {
50 | return messageMapper.selectLatestNotice(userId, topic);
51 | }
52 |
53 | public int findNoticeCount(int userId, String topic) {
54 | return messageMapper.selectNoticeCount(userId, topic);
55 | }
56 |
57 | public int findNoticeUnreadCount(int userId, String topic) {
58 | return messageMapper.selectNoticeUnreadCount(userId, topic);
59 | }
60 |
61 | public List findNotices(int userId, String topic, int offset, int limit) {
62 | return messageMapper.selectNotices(userId, topic, offset, limit);
63 | }
64 |
65 | }
66 |
--------------------------------------------------------------------------------
/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 |
12 | user_id, title, content, type, status, create_time, comment_count, score
13 |
14 |
15 |
30 |
31 |
39 |
40 |
41 | insert into discuss_post()
42 | values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score})
43 |
44 |
45 |
50 |
51 |
54 |
55 |
56 | update discuss_post set type = #{type} where id = #{id}
57 |
58 |
59 |
60 | update discuss_post set status = #{status} where id = #{id}
61 |
62 |
63 |
64 | update discuss_post set score = #{score} where id = #{id}
65 |
66 |
67 |
--------------------------------------------------------------------------------
/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 com.nowcoder.community.util.CommunityConstant;
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.RequestMapping;
13 | import org.springframework.web.bind.annotation.RequestMethod;
14 |
15 | import java.util.ArrayList;
16 | import java.util.HashMap;
17 | import java.util.List;
18 | import java.util.Map;
19 |
20 | @Controller
21 | public class SearchController implements CommunityConstant {
22 |
23 | @Autowired
24 | private ElasticsearchService elasticsearchService;
25 |
26 | @Autowired
27 | private UserService userService;
28 |
29 | @Autowired
30 | private LikeService likeService;
31 |
32 | // search?keyword=xxx
33 | @RequestMapping(path = "/search", method = RequestMethod.GET)
34 | public String search(String keyword, Page page, Model model) {
35 | // 搜索帖子
36 | org.springframework.data.domain.Page searchResult =
37 | elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());
38 | // 聚合数据
39 | List