├── .gitignore ├── .mvn └── wrapper │ ├── MavenWrapperDownloader.java │ ├── maven-wrapper.jar │ └── maven-wrapper.properties ├── HELP.md ├── LICENSE ├── README.md ├── init_schema.sql ├── mvnw ├── mvnw.cmd ├── pom.xml └── src ├── main ├── java │ └── fun │ │ └── linyuhong │ │ └── myCommunity │ │ ├── MyCommunityApplication.java │ │ ├── MyCommunityServletInitializer.java │ │ ├── aspect │ │ └── ServiceLogAspect.java │ │ ├── async │ │ ├── EventConsumer.java │ │ ├── EventHandler.java │ │ ├── EventModel.java │ │ ├── EventProducer.java │ │ ├── EventType.java │ │ └── handler │ │ │ ├── CommentMessageHandler.java │ │ │ ├── DeleteHandler.java │ │ │ ├── PublishHandler.java │ │ │ └── RegisterHandler.java │ │ ├── common │ │ ├── Const.java │ │ └── Page.java │ │ ├── config │ │ ├── KaptchaConfig.java │ │ ├── QuartzConfig.java │ │ ├── RedisConfig.java │ │ ├── SecurityConfig.java │ │ ├── ThreadPoolConfig.java │ │ └── WebMvcConfig.java │ │ ├── controller │ │ ├── advice │ │ │ └── ExceptionAdvice.java │ │ └── portal │ │ │ ├── CommentController.java │ │ │ ├── DiscussPostController.java │ │ │ ├── FollowController.java │ │ │ ├── HomeController.java │ │ │ ├── LikeController.java │ │ │ ├── LoginController.java │ │ │ ├── MessageController.java │ │ │ ├── RegisterController.java │ │ │ ├── SearchController.java │ │ │ └── UserController.java │ │ ├── dao │ │ ├── CommentMapper.java │ │ ├── DiscussPostMapper.java │ │ ├── MessageMapper.java │ │ ├── UserMapper.java │ │ └── elasticsearch │ │ │ └── DiscussPostRepository.java │ │ ├── entity │ │ ├── Comment.java │ │ ├── DiscussPost.java │ │ ├── JwtUser.java │ │ ├── LoginTicket.java │ │ ├── Message.java │ │ └── User.java │ │ ├── interceptor │ │ ├── LoginOnlyOneInterceptor.java │ │ ├── LoginTicketInterceptor.java │ │ └── MessageInterceptor.java │ │ ├── quartz │ │ ├── AlphaJob.java │ │ └── PostScoreRefreshJob.java │ │ ├── service │ │ ├── AlphaService.java │ │ ├── DataService.java │ │ ├── ICommentService.java │ │ ├── IDiscussPostService.java │ │ ├── IElasticsearchService.java │ │ ├── IFollowService.java │ │ ├── ILikeService.java │ │ ├── IMessageService.java │ │ ├── IUserService.java │ │ └── Impl │ │ │ ├── CommentServiceImpl.java │ │ │ ├── DiscussPostServiceImpl.java │ │ │ ├── ElasticsearchServiceImpl.java │ │ │ ├── FollowServiceImpl.java │ │ │ ├── LikeServiceImpl.java │ │ │ ├── MessageServiceImpl.java │ │ │ └── UserServiceImpl.java │ │ ├── util │ │ ├── CookieUtil.java │ │ ├── EmailUtil.java │ │ ├── GetGenerateUUID.java │ │ ├── HostHolder.java │ │ ├── HttpUtil.java │ │ ├── JSONUtil.java │ │ ├── MD5Encoder.java │ │ ├── MailClient.java │ │ ├── RedisKeyUtil.java │ │ ├── SensitiveFilter.java │ │ └── XORUtil.java │ │ └── vo │ │ └── UserVo.java └── resources │ ├── application-develop.yml │ ├── application-produce.yml │ ├── application.yml │ ├── datasource.properties │ ├── generatorConfig.xml │ ├── logback-spring-develop.xml │ ├── logback-spring-produce.xml │ ├── logback-spring.xml │ ├── mapper │ ├── CommentMapper.xml │ ├── DiscussPostMapper.xml │ ├── MessageMapper.xml │ └── UserMapper.xml │ ├── sensitive-words.txt │ ├── static │ ├── css │ │ ├── bootstrap.min.css │ │ ├── discuss-detail.css │ │ ├── global.css │ │ ├── letter.css │ │ ├── login.css │ │ ├── myPage.css │ │ ├── style_404.css │ │ └── style_500.css │ ├── img │ │ ├── 404.jpg │ │ ├── 404_2.jpg │ │ ├── captcha.png │ │ ├── female.jpg │ │ ├── follow.jpg │ │ ├── head.jpg │ │ ├── headImg2.jpg │ │ ├── icon-discuss-2.png │ │ ├── icon-discuss.jpg │ │ ├── icon.png │ │ ├── like.jpg │ │ ├── like.png │ │ ├── logo.jpg │ │ ├── male.png │ │ ├── profession.jpg │ │ ├── reply.jpg │ │ └── system.jpg │ └── js │ │ ├── discuss.js │ │ ├── global.js │ │ ├── index.js │ │ ├── jquery-3.1.1.min.js │ │ ├── letter.js │ │ ├── myPage.js │ │ ├── profile.js │ │ ├── register.js │ │ └── setting.js │ └── templates │ ├── error │ ├── 404.html │ ├── 404_2.html │ └── 500.html │ ├── home.html │ ├── index.html │ ├── mail │ ├── activation.html │ └── forget.html │ └── site │ ├── admin │ └── data.html │ ├── discuss-detail.html │ ├── error │ ├── 404.html │ └── 500.html │ ├── followee.html │ ├── follower.html │ ├── footer.html │ ├── forget.html │ ├── header.html │ ├── letter-detail.html │ ├── letter.html │ ├── login.html │ ├── my-page.html │ ├── my-post.html │ ├── my-reply.html │ ├── notice-detail.html │ ├── notice.html │ ├── operate-result.html │ ├── pagination.html │ ├── profile.html │ ├── register.html │ ├── search.html │ └── setting.html └── test └── java └── fun └── linyuhong └── myCommunity ├── ElasticsearchTests.java ├── EmailUtilTests.java ├── HttpUtilTests.java ├── MyCommunityApplicationTests.java ├── RedisTest.java ├── SensitiveFilterTests.java ├── ThreadPoolTest.java └── XORUtilTests.java /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # packpage file 4 | *.war 5 | *.ear 6 | 7 | # kdiff3 ignore 8 | *.orig 9 | 10 | # maven ignore 11 | target/ 12 | 13 | # eclipse ignore 14 | .settings/ 15 | .project/ 16 | .classpath 17 | 18 | # idea 19 | .idea/ 20 | /idea/ 21 | *.ipr 22 | *.iml 23 | *.iws 24 | 25 | # temp file 26 | *.log 27 | *.cache 28 | *.diff 29 | *.patch 30 | *.tmp 31 | 32 | # system ignore 33 | .DS_Store 34 | Thumbs.db 35 | -------------------------------------------------------------------------------- /.mvn/wrapper/maven-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/.mvn/wrapper/maven-wrapper.jar -------------------------------------------------------------------------------- /.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 | -------------------------------------------------------------------------------- /HELP.md: -------------------------------------------------------------------------------- 1 | # Getting Started 2 | 3 | ### Reference Documentation 4 | For further reference, please consider the following sections: 5 | 6 | * [Official Apache Maven documentation](https://maven.apache.org/guides/index.html) 7 | * [Spring Boot Maven Plugin Reference Guide](https://docs.spring.io/spring-boot/docs/2.1.7.RELEASE/maven-plugin/) 8 | * [Spring Web Starter](https://docs.spring.io/spring-boot/docs/{bootVersion}/reference/htmlsingle/#boot-features-developing-web-applications) 9 | * [Spring Security](https://docs.spring.io/spring-boot/docs/{bootVersion}/reference/htmlsingle/#boot-features-security) 10 | * [Spring Boot DevTools](https://docs.spring.io/spring-boot/docs/{bootVersion}/reference/htmlsingle/#using-boot-devtools) 11 | * [Thymeleaf](https://docs.spring.io/spring-boot/docs/{bootVersion}/reference/htmlsingle/#boot-features-spring-mvc-template-engines) 12 | 13 | ### Guides 14 | The following guides illustrate how to use some features concretely: 15 | 16 | * [Building a RESTful Web Service](https://spring.io/guides/gs/rest-service/) 17 | * [Serving Web Content with Spring MVC](https://spring.io/guides/gs/serving-web-content/) 18 | * [Building REST services with Spring](https://spring.io/guides/tutorials/bookmarks/) 19 | * [Securing a Web Application](https://spring.io/guides/gs/securing-web/) 20 | * [Spring Boot and OAuth2](https://spring.io/guides/tutorials/spring-boot-oauth2/) 21 | * [Authenticating a User with LDAP](https://spring.io/guides/gs/authenticating-ldap/) 22 | * [Handling Form Submission](https://spring.io/guides/gs/handling-form-submission/) 23 | 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MyCommunity 2 | 3 | # 项目介绍 4 | MyCommunity 是一个模仿知乎的简单问答网站,基于SpringBoot+MyBatis实现。 5 | 用户可以发表帖子、评论、点赞、私信、相互关注、查看用户信息等。 6 | 同时,网站拥有敏感词过滤、热门帖子排行、搜索帖子、系统消息通知等功能。 7 | (新项目:[番剧下载器](https://github.com/UHungLin/AnimeDownloader),这个比较好玩 :smile:)) 8 | 9 | # 项目演示 10 | ### 项目演示地址 11 | ~http://106.15.248.31/ (emmmmm...之前被我不小心把线上代码删了QAQ,网站展示暂时失败)~ 12 | 用户名: 时崎狂三 13 | 密码: 123456 14 | (PS: 或者其它9个精灵名字) 15 | (PS: 项目现在已经不能注册,请使用现有账号密码登录 by.12.21) 16 | 17 | ### 项目截图 18 | ![](http://mycommunity.oss-cn-shanghai.aliyuncs.com/clipboard.png) 19 | 20 | ![](http://mycommunity.oss-cn-shanghai.aliyuncs.com/1569414012%281%29.png) 21 | 22 | ![](http://mycommunity.oss-cn-shanghai.aliyuncs.com/clipboard%20%281%29.png) 23 | 24 | ![](http://mycommunity.oss-cn-shanghai.aliyuncs.com/clipboard%20%282%29.png) 25 | 26 | ### 用到的技术 27 | 技术 | 名称 28 | ----|---- 29 | Spring Boot | 容器+MVC框架 30 | Spring Security | 认证和授权框架 31 | MyBatis | ORM框架 32 | Elasticsearch | 搜索引擎 33 | Redis | 分布式缓存 34 | Thymeleaf | 模板引擎 35 | Quartz | 定时任务支持 36 | 37 | ### 许可证 38 | [Apache License 2.0](https://github.com/UHungLin/MyCommunity/blob/master/LICENSE) 39 | 40 | Copyright (c) 2018-2019 UHungLin 41 | -------------------------------------------------------------------------------- /init_schema.sql: -------------------------------------------------------------------------------- 1 | SET NAMES utf8 ; 2 | 3 | 4 | DROP TABLE IF EXISTS `comment`; 5 | /*!40101 SET @saved_cs_client = @@character_set_client */; 6 | SET character_set_client = utf8mb4 ; 7 | CREATE TABLE `comment` ( 8 | `id` int(11) NOT NULL AUTO_INCREMENT, 9 | `user_id` int(11) DEFAULT NULL, 10 | `entity_type` int(11) DEFAULT NULL, 11 | `entity_id` int(11) DEFAULT NULL, 12 | `target_id` int(11) DEFAULT NULL, 13 | `content` text, 14 | `status` int(11) DEFAULT NULL, 15 | `create_time` timestamp NULL DEFAULT NULL, 16 | PRIMARY KEY (`id`), 17 | KEY `index_user_id` (`user_id`) /*!80000 INVISIBLE */, 18 | KEY `index_entity_id` (`entity_id`) 19 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 20 | 21 | 22 | DROP TABLE IF EXISTS `discuss_post`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | SET character_set_client = utf8mb4 ; 25 | CREATE TABLE `discuss_post` ( 26 | `id` int(11) NOT NULL AUTO_INCREMENT, 27 | `user_id` varchar(45) DEFAULT NULL, 28 | `title` varchar(100) DEFAULT NULL, 29 | `content` text, 30 | `type` int(11) DEFAULT NULL COMMENT '0-普通; 1-置顶;', 31 | `status` int(11) DEFAULT NULL COMMENT '0-正常; 1-精华; 2-拉黑;', 32 | `create_time` timestamp NULL DEFAULT NULL, 33 | `comment_count` int(11) DEFAULT NULL, 34 | `score` double DEFAULT NULL, 35 | PRIMARY KEY (`id`), 36 | KEY `index_user_id` (`user_id`) 37 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 38 | 39 | 40 | DROP TABLE IF EXISTS `message`; 41 | /*!40101 SET @saved_cs_client = @@character_set_client */; 42 | SET character_set_client = utf8mb4 ; 43 | CREATE TABLE `message` ( 44 | `id` int(11) NOT NULL AUTO_INCREMENT, 45 | `from_id` int(11) DEFAULT NULL, 46 | `to_id` int(11) DEFAULT NULL, 47 | `conversation_id` varchar(45) NOT NULL, 48 | `content` text, 49 | `status` int(11) DEFAULT NULL COMMENT '0-未读;1-已读;2-删除;', 50 | `create_time` timestamp NULL DEFAULT NULL, 51 | PRIMARY KEY (`id`), 52 | KEY `index_from_id` (`from_id`), 53 | KEY `index_to_id` (`to_id`), 54 | KEY `index_conversation_id` (`conversation_id`) 55 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 56 | 57 | 58 | DROP TABLE IF EXISTS `user`; 59 | /*!40101 SET @saved_cs_client = @@character_set_client */; 60 | SET character_set_client = utf8mb4 ; 61 | CREATE TABLE `user` ( 62 | `id` int(11) NOT NULL AUTO_INCREMENT, 63 | `username` varchar(50) DEFAULT NULL, 64 | `password` varchar(50) DEFAULT NULL, 65 | `salt` varchar(50) DEFAULT NULL, 66 | `email` varchar(100) DEFAULT NULL, 67 | `type` int(11) DEFAULT NULL COMMENT '0-普通用户; 1-超级管理员; 2-版主;', 68 | `status` int(11) DEFAULT NULL COMMENT '0-未激活; 1-已激活;', 69 | `activation_code` varchar(100) DEFAULT NULL, 70 | `header_url` varchar(200) DEFAULT NULL, 71 | `create_time` timestamp NULL DEFAULT NULL, 72 | PRIMARY KEY (`id`), 73 | KEY `index_username` (`username`(20)), 74 | KEY `index_email` (`email`(20)) 75 | ) ENGINE=InnoDB AUTO_INCREMENT=101 DEFAULT CHARSET=utf8; 76 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/MyCommunityApplication.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 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 MyCommunityApplication { 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 | 18 | public static void main(String[] args) { 19 | SpringApplication.run(MyCommunityApplication.class, args); 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/MyCommunityServletInitializer.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import org.springframework.boot.builder.SpringApplicationBuilder; 4 | import org.springframework.boot.web.servlet.support.SpringBootServletInitializer; 5 | 6 | 7 | /** 8 | * 线上部署相关 9 | * 关闭启动类(因为tomcat里面也有main方法,而我们无法调用原有的main方法),配置入口类,让tomcat通过它启动启动类 10 | * MyCommunityServletInitializer 11 | */ 12 | public class MyCommunityServletInitializer extends SpringBootServletInitializer { 13 | 14 | @Override 15 | protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) { 16 | return builder.sources(MyCommunityApplication.class); 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/aspect/ServiceLogAspect.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.aspect; 2 | 3 | import org.aspectj.lang.JoinPoint; 4 | import org.aspectj.lang.annotation.Before; 5 | import org.aspectj.lang.annotation.Pointcut; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.web.context.request.RequestContextHolder; 9 | import org.springframework.web.context.request.ServletRequestAttributes; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import java.text.SimpleDateFormat; 13 | import java.util.Date; 14 | 15 | //统一记录日志 16 | //@Component 17 | //@Aspect 18 | public class ServiceLogAspect { 19 | 20 | private static final Logger logger = LoggerFactory.getLogger(ServiceLogAspect.class); 21 | 22 | @Pointcut("execution(* fun.linyuhong.myCommunity.service.*.*(..))") 23 | public void pointcut() { 24 | 25 | } 26 | 27 | @Before("pointcut()") 28 | public void before(JoinPoint joinPoint) { 29 | // 用户[1.2.3.4],在[xxx],访问了[com.nowcoder.community.service.xxx()]. 30 | ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); 31 | if (attributes == null) { 32 | return; 33 | } 34 | HttpServletRequest request = attributes.getRequest(); 35 | String ip = request.getRemoteHost(); 36 | String now = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); 37 | String target = joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName(); 38 | logger.info(String.format("用户[%s],在[%s],访问了[%s].", ip, now, target)); 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/EventHandler.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async; 2 | 3 | import java.util.List; 4 | 5 | /** 6 | * @author linyuhong 7 | * @date 2019/9/6 8 | */ 9 | public interface EventHandler { 10 | 11 | void doHandler(EventModel eventModel); 12 | 13 | List getSupportEventTypes(); 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/EventModel.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/6 9 | */ 10 | public class EventModel { 11 | 12 | EventType eventType; 13 | private int actorId; 14 | private int entityType; 15 | private int entityId; 16 | private int entityUserId; 17 | // 存放多余信息 18 | private Map data = new HashMap<>(); 19 | 20 | 21 | public EventModel() { 22 | 23 | } 24 | 25 | public EventModel(EventType type) { 26 | this.eventType = type; 27 | } 28 | 29 | public EventType getEventType() { 30 | return eventType; 31 | } 32 | 33 | public EventModel setEventType(EventType eventType) { 34 | this.eventType = eventType; 35 | return this; 36 | } 37 | 38 | public int getActorId() { 39 | return actorId; 40 | } 41 | 42 | public EventModel setActorId(int actorId) { 43 | this.actorId = actorId; 44 | return this; 45 | } 46 | 47 | public int getEntityType() { 48 | return entityType; 49 | } 50 | 51 | public EventModel setEntityType(int entityType) { 52 | this.entityType = entityType; 53 | return this; 54 | } 55 | 56 | public int getEntityId() { 57 | return entityId; 58 | } 59 | 60 | public EventModel setEntityId(int entityId) { 61 | this.entityId = entityId; 62 | return this; 63 | } 64 | 65 | public int getEntityUserId() { 66 | return entityUserId; 67 | } 68 | 69 | public EventModel setEntityUserId(int entityUserId) { 70 | this.entityUserId = entityUserId; 71 | return this; 72 | } 73 | 74 | public Map getData() { 75 | return data; 76 | } 77 | 78 | public EventModel setData(String key, Object value) { 79 | this.data.put(key, value); 80 | return this; 81 | } 82 | } 83 | 84 | 85 | 86 | 87 | 88 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/EventProducer.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import fun.linyuhong.myCommunity.util.JSONUtil; 5 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.stereotype.Service; 9 | 10 | import javax.xml.ws.Action; 11 | 12 | /** 13 | * @author linyuhong 14 | * @date 2019/9/6 15 | */ 16 | @Service 17 | public class EventProducer { 18 | 19 | @Autowired 20 | private RedisTemplate redisTemplate; 21 | 22 | public boolean fireEvent(EventModel eventModel) { 23 | try { 24 | String eventQueueKey = RedisKeyUtil.getEventqueueKey(); 25 | redisTemplate.opsForList().leftPush(eventQueueKey, JSONObject.toJSONString(eventModel)); 26 | return true; 27 | } catch (Exception e) { 28 | return false; 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/EventType.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async; 2 | 3 | /** 4 | * @author linyuhong 5 | * @date 2019/9/6 6 | */ 7 | public enum EventType { 8 | 9 | LIKE("like"), 10 | FOLLOW("follow"), 11 | COMMENT("comment"), 12 | /** 13 | * 发帖 14 | */ 15 | PUBLISH("publish"), 16 | /** 17 | * 注册 18 | */ 19 | REGISTER("register"), 20 | /** 21 | * 删帖 22 | */ 23 | DELETE("delete"), 24 | 25 | /** 26 | * 表示系统通知,包括注册、登录 等等等 27 | */ 28 | SYSTEM("system"); 29 | 30 | // LIKE(0, "like"), 31 | // FOLLOW(1, "follow"), 32 | // COMMENT(2, "comment"), 33 | // /** 34 | // * 发帖 35 | // */ 36 | // PUBLISH(3, "publish"); 37 | 38 | private String value; 39 | // private String type; 40 | 41 | EventType(String value) { 42 | this.value = value; 43 | } 44 | 45 | // EventType(int value, String type) { 46 | // this.value = value; 47 | // this.type = type; 48 | // } 49 | 50 | public String getValue(){ 51 | return this.value; 52 | } 53 | 54 | // public String getType() { 55 | // return this.type; 56 | // } 57 | } 58 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/handler/CommentMessageHandler.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async.handler; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import fun.linyuhong.myCommunity.async.EventHandler; 5 | import fun.linyuhong.myCommunity.async.EventModel; 6 | import fun.linyuhong.myCommunity.async.EventType; 7 | import fun.linyuhong.myCommunity.common.Const; 8 | import fun.linyuhong.myCommunity.entity.Message; 9 | import fun.linyuhong.myCommunity.service.IMessageService; 10 | import fun.linyuhong.myCommunity.service.IUserService; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.*; 15 | 16 | /** 17 | * @author linyuhong 18 | * @date 2019/9/6 19 | */ 20 | @Component 21 | public class CommentMessageHandler implements EventHandler { 22 | 23 | @Autowired 24 | private IMessageService iMessageService; 25 | 26 | @Autowired 27 | private IUserService iUserService; 28 | 29 | @Override 30 | public void doHandler(EventModel eventModel) { 31 | Message message = new Message(); 32 | message.setFromId(Const.systemuser.SYSTEM_USER_ID); 33 | message.setToId(eventModel.getEntityUserId()); 34 | message.setStatus(Const.status.UNREAD); 35 | message.setConversationId(eventModel.getEventType().getValue()); 36 | message.setCreateTime(new Date()); 37 | 38 | Map content = new HashMap<>(); // Message 数据表里 content 字段的内容,用来拼成显示的 消息模板 39 | content.put("userId", eventModel.getActorId()); // 触发者 40 | content.put("entityType", eventModel.getEntityType()); 41 | content.put("entityId", eventModel.getEntityId()); 42 | 43 | // 把其他的额外消息也一起放进content里 44 | if (!eventModel.getData().isEmpty()) { 45 | for (Map.Entry entry : eventModel.getData().entrySet()) { 46 | content.put(entry.getKey(), entry.getValue()); 47 | } 48 | } 49 | 50 | message.setContent(JSONObject.toJSONString(content)); 51 | iMessageService.addMessage(message); 52 | } 53 | 54 | @Override 55 | public List getSupportEventTypes() { 56 | return Arrays.asList(EventType.LIKE, EventType.COMMENT, EventType.FOLLOW); 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/handler/DeleteHandler.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async.handler; 2 | 3 | import fun.linyuhong.myCommunity.async.EventHandler; 4 | import fun.linyuhong.myCommunity.async.EventModel; 5 | import fun.linyuhong.myCommunity.async.EventType; 6 | import fun.linyuhong.myCommunity.entity.DiscussPost; 7 | import fun.linyuhong.myCommunity.service.IDiscussPostService; 8 | import fun.linyuhong.myCommunity.service.IElasticsearchService; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | 11 | import java.util.Arrays; 12 | import java.util.List; 13 | 14 | /** 15 | * @author linyuhong 16 | * @date 2019/9/8 17 | */ 18 | public class DeleteHandler implements EventHandler { 19 | 20 | @Autowired 21 | private IDiscussPostService iDiscussPostService; 22 | 23 | @Autowired 24 | private IElasticsearchService iElasticsearchService; 25 | 26 | 27 | @Override 28 | public void doHandler(EventModel eventModel) { 29 | iElasticsearchService.deleteDiscussPost(eventModel.getEntityId()); 30 | } 31 | 32 | @Override 33 | public List getSupportEventTypes() { 34 | return Arrays.asList(EventType.DELETE); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/handler/PublishHandler.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async.handler; 2 | 3 | import fun.linyuhong.myCommunity.async.EventHandler; 4 | import fun.linyuhong.myCommunity.async.EventModel; 5 | import fun.linyuhong.myCommunity.async.EventProducer; 6 | import fun.linyuhong.myCommunity.async.EventType; 7 | import fun.linyuhong.myCommunity.entity.DiscussPost; 8 | import fun.linyuhong.myCommunity.service.IDiscussPostService; 9 | import fun.linyuhong.myCommunity.service.IElasticsearchService; 10 | import fun.linyuhong.myCommunity.service.Impl.ElasticsearchServiceImpl; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Component; 13 | 14 | import java.util.Arrays; 15 | import java.util.List; 16 | 17 | /** 18 | * @author linyuhong 19 | * @date 2019/9/8 20 | */ 21 | 22 | /** 23 | * 消费发帖事件,把新增加或修改的帖子添加到 ES 服务器中 24 | */ 25 | @Component 26 | public class PublishHandler implements EventHandler { 27 | 28 | @Autowired 29 | private IDiscussPostService iDiscussPostService; 30 | 31 | @Autowired 32 | private IElasticsearchService iElasticsearchService; 33 | 34 | @Override 35 | public void doHandler(EventModel eventModel) { 36 | // 事件 event 只是存放关键信息,触发消费活动,最终查询具体内容还是调用 mysql,查找帖子放到 ES 中 37 | DiscussPost post = iDiscussPostService.findDiscussPostById(eventModel.getEntityId()); 38 | iElasticsearchService.saveDiscussPost(post); 39 | } 40 | 41 | @Override 42 | public List getSupportEventTypes() { 43 | return Arrays.asList(EventType.PUBLISH); 44 | } 45 | } 46 | 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/async/handler/RegisterHandler.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.async.handler; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | import fun.linyuhong.myCommunity.async.EventHandler; 5 | import fun.linyuhong.myCommunity.async.EventModel; 6 | import fun.linyuhong.myCommunity.async.EventType; 7 | import fun.linyuhong.myCommunity.common.Const; 8 | import fun.linyuhong.myCommunity.dao.UserMapper; 9 | import fun.linyuhong.myCommunity.entity.Message; 10 | import fun.linyuhong.myCommunity.entity.User; 11 | import fun.linyuhong.myCommunity.service.IMessageService; 12 | import fun.linyuhong.myCommunity.util.MailClient; 13 | import fun.linyuhong.myCommunity.util.XORUtil; 14 | import org.springframework.beans.factory.annotation.Autowired; 15 | import org.springframework.beans.factory.annotation.Value; 16 | import org.springframework.data.redis.core.RedisTemplate; 17 | import org.springframework.stereotype.Component; 18 | import org.thymeleaf.TemplateEngine; 19 | import org.thymeleaf.context.Context; 20 | 21 | import java.util.*; 22 | 23 | /** 24 | * @author linyuhong 25 | * @date 2019/9/7 26 | */ 27 | @Component 28 | public class RegisterHandler implements EventHandler { 29 | 30 | @Autowired 31 | private UserMapper userMapper; 32 | 33 | @Autowired 34 | private TemplateEngine templateEngine; 35 | 36 | @Autowired 37 | private MailClient mailClient; 38 | 39 | @Autowired 40 | private IMessageService iMessageService; 41 | 42 | // 域名 43 | @Value("${domain}") 44 | private String domain; 45 | 46 | // 项目名 47 | @Value("${server.servlet.context-path}") 48 | private String contextPath; 49 | 50 | 51 | @Override 52 | public void doHandler(EventModel eventModel) { 53 | 54 | User user = userMapper.selectByPrimaryKey(eventModel.getEntityUserId()); 55 | 56 | // 发送激活邮件 57 | Context context = new Context(); 58 | context.setVariable("email", user.getEmail()); 59 | // http://localhost:8080/activation/10110110/code 60 | String url = domain + contextPath + "/activation/" + XORUtil.encryptId(user.getId(), Const.getIdEncodeKeys.userIdKeys) 61 | + "/" + user.getActivationCode(); 62 | context.setVariable("url", url); 63 | String content = templateEngine.process("/mail/activation", context); 64 | mailClient.sendMail(user.getEmail(), "激活账号", content); 65 | 66 | 67 | Message message = new Message(); 68 | message.setFromId(Const.systemuser.SYSTEM_USER_ID); 69 | message.setToId(eventModel.getEntityUserId()); 70 | message.setStatus(Const.status.UNREAD); 71 | // message.setConversationId(eventModel.getEventType().getValue()); 72 | /** 73 | * 为了统一方便,这里 ConversationId 不用 register,而是用 system 代表所有跟注册、登录相关的系统通知事件 74 | * 这样在展示详细信息时方便,因为像 注册这种,一般就一次,不用单独用页面展示这条信息 75 | */ 76 | message.setConversationId(EventType.SYSTEM.getValue()); 77 | message.setCreateTime(new Date()); 78 | 79 | Map messageContent = new HashMap<>(); 80 | messageContent.put("userId", eventModel.getActorId()); // 触发者 81 | messageContent.put("entityType", eventModel.getEntityType()); 82 | messageContent.put("entityId", eventModel.getEntityId()); 83 | 84 | // 把其他的额外消息也一起放进content里 85 | if (!eventModel.getData().isEmpty()) { 86 | for (Map.Entry entry : eventModel.getData().entrySet()) { 87 | messageContent.put(entry.getKey(), entry.getValue()); 88 | } 89 | } 90 | 91 | message.setContent(JSONObject.toJSONString(messageContent)); 92 | 93 | iMessageService.addMessage(message); 94 | } 95 | 96 | @Override 97 | public List getSupportEventTypes() { 98 | return Arrays.asList(EventType.REGISTER); 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/common/Const.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.common; 2 | 3 | public class Const { 4 | 5 | /** 6 | * 系统用户 7 | */ 8 | public interface systemuser { 9 | int SYSTEM_USER_ID = 1; 10 | } 11 | 12 | /** 13 | * 关注实体类型 14 | */ 15 | public interface follow { 16 | /** 17 | * 实体类型: 用户 18 | */ 19 | int ENTITY_TYPE_USER = 3; 20 | } 21 | 22 | /** 23 | * 点赞实体类型 24 | */ 25 | public interface like { 26 | /** 27 | * 帖子 28 | */ 29 | int ENTITY_TYPE_POST = 1; 30 | /** 31 | * 评论 32 | */ 33 | int ENTITY_TYPE_COMMENT = 2; 34 | /** 35 | * 实体类型: 用户 36 | */ 37 | int ENTITY_TYPE_USER = 3; 38 | 39 | } 40 | 41 | /** 42 | * Message 类型 43 | */ 44 | public interface status { 45 | /** 46 | * 信息未读 47 | */ 48 | int UNREAD = 0; 49 | 50 | /** 51 | * 信息已读 52 | */ 53 | int READ = 1; 54 | 55 | /** 56 | * 信息删除 57 | */ 58 | int DELETE = 2; 59 | } 60 | 61 | /** 62 | * Comment 类型 63 | */ 64 | public interface entityType { 65 | /** 66 | * 实体类型: 帖子 67 | */ 68 | int ENTITY_TYPE_POST = 1; 69 | 70 | /** 71 | * 实体类型: 评论 72 | */ 73 | int ENTITY_TYPE_COMMENT = 2; 74 | 75 | /** 76 | * 实体类型: 用户 77 | */ 78 | int ENTITY_TYPE_USER = 3; 79 | 80 | 81 | } 82 | 83 | /** 84 | * 用户是否存在 85 | */ 86 | public interface isExist { 87 | int NOEXIST = 0; 88 | int EXIST = 1; 89 | } 90 | 91 | 92 | /** 93 | * id 加密 94 | */ 95 | public interface getIdEncodeKeys { 96 | byte[] userIdKeys = new byte[]{1, 2}; 97 | byte[] postIdKeys = new byte[]{2, 1}; 98 | } 99 | 100 | 101 | /** 102 | * 用户权限 103 | */ 104 | public enum Role{ 105 | 106 | ROLE_USER(0, "ROLE_USER"), 107 | ROLE_ADMIN(1,"ROLE_ADMIN"); 108 | 109 | private Integer type; 110 | private String role; 111 | 112 | Role(Integer type, String role) { 113 | this.type = type; 114 | this.role = role; 115 | } 116 | 117 | public Integer getType() { 118 | return type; 119 | } 120 | 121 | public String getRole() { 122 | return role; 123 | } 124 | 125 | public static String getRole(int type) { 126 | for (Role s : Role.values()){ 127 | if (s.type == type){ 128 | return s.role; 129 | } 130 | } 131 | return ROLE_USER.role; 132 | } 133 | } 134 | 135 | /** 136 | * 登录凭证 loginTicket 137 | * VALID 有效 138 | * INVALID 无效 139 | * DEFAULT_EXPIRED_SECONDS * 1000 = 12小时 140 | */ 141 | public interface loginStatus{ 142 | int VALID = 0; 143 | int INVALID = 1; 144 | int DEFAULT_EXPIRED_SECONDS = 3600 * 12; 145 | } 146 | 147 | /** 148 | * 用户登录凭证 ticket 149 | */ 150 | public interface ticket { 151 | String TICKET = "ticket"; 152 | } 153 | 154 | /** 155 | * 用户账号是否激活 156 | */ 157 | public interface active { 158 | 159 | /** 160 | * 账号未激活 161 | */ 162 | int INACTIVE = 0; 163 | 164 | /** 165 | * 账号已激活 166 | */ 167 | int ACTIVE = 1; 168 | 169 | /** 170 | * 激活成功 返回给controller判断用的 171 | */ 172 | int ACTIVATION_SUCCESS = 3; 173 | 174 | /** 175 | * 重复激活 176 | */ 177 | int ACTIVATION_REPEAT = 4; 178 | 179 | /** 180 | * 激活失败 181 | */ 182 | int ACTIVATION_FAILURE = 5; 183 | } 184 | 185 | /** 186 | * 用户随机头像生成地址 187 | */ 188 | public interface avatarUrl { 189 | String AVATARURL = "https://api.uomg.com/api/rand.avatar"; 190 | } 191 | 192 | } 193 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/common/Page.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.common; 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 | // 整除 73 | if (rows % limit == 0) { 74 | return rows / limit; 75 | } else { 76 | return rows / limit + 1; 77 | } 78 | } 79 | 80 | /** 81 | * 获取起始页码 82 | * 83 | * @return 84 | */ 85 | public int getFrom() { 86 | int from = current - 2; 87 | return from < 1 ? 1 : from; 88 | } 89 | 90 | /** 91 | * 获取结束页码 92 | * 93 | * @return 94 | */ 95 | public int getTo() { 96 | int to = current + 2; 97 | int total = getTotal(); 98 | return to > total ? total : to; 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/config/KaptchaConfig.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.config; 2 | 3 | 4 | import com.google.code.kaptcha.Producer; 5 | import com.google.code.kaptcha.impl.DefaultKaptcha; 6 | import com.google.code.kaptcha.util.Config; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | 10 | import java.util.Properties; 11 | 12 | 13 | @Configuration 14 | public class KaptchaConfig { 15 | 16 | @Bean 17 | public Producer kaptchaProducer() { 18 | Properties properties = new Properties(); 19 | properties.setProperty("kaptcha.image.width", "100"); 20 | properties.setProperty("kaptcha.image.height", "40"); 21 | properties.setProperty("kaptcha.textproducer.font.size", "32"); // 字号 22 | properties.setProperty("kaptcha.textproducer.font.color", "0,0,0"); // 字体颜色 23 | properties.setProperty("kaptcha.textproducer.char.string", "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYAZ"); 24 | properties.setProperty("kaptcha.textproducer.char.length", "4"); // 验证码数量 25 | properties.setProperty("kaptcha.noise.impl", "com.google.code.kaptcha.impl.NoNoise"); // 设置干扰 26 | 27 | DefaultKaptcha kaptcha = new DefaultKaptcha(); 28 | Config config = new Config(properties); 29 | kaptcha.setConfig(config); 30 | return kaptcha; 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/config/QuartzConfig.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.config; 2 | 3 | 4 | 5 | import fun.linyuhong.myCommunity.quartz.AlphaJob; 6 | import fun.linyuhong.myCommunity.quartz.PostScoreRefreshJob; 7 | import org.quartz.JobDataMap; 8 | import org.quartz.JobDetail; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.scheduling.quartz.JobDetailFactoryBean; 12 | import org.springframework.scheduling.quartz.SimpleTriggerFactoryBean; 13 | 14 | //@Configuration 15 | public class QuartzConfig { 16 | 17 | // @Bean 18 | public JobDetailFactoryBean alphaJobDetail () { 19 | JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); 20 | factoryBean.setJobClass(AlphaJob.class); // 管理的Job 21 | factoryBean.setName("alphaJob"); 22 | factoryBean.setGroup("alphaGroup"); 23 | factoryBean.setDurability(true); // 任务是否持久保存 24 | factoryBean.setRequestsRecovery(true); // 任务发生错误时是否可恢复 25 | 26 | return factoryBean; 27 | } 28 | 29 | /** 30 | *注意 参数 alphaJobDetail 的名字要上下一致,这样当有多个 31 | * JobDetailFactoryBean 实例时,Spring会优先根据 32 | * 名字匹配进行注入 33 | * 34 | */ 35 | 36 | // 配置Trigger(SimpleTriggerFactoryBean, CronTriggerFactoryBean) 37 | // @Bean 38 | public SimpleTriggerFactoryBean alphaTrigger(JobDetail alphaJobDetail) { 39 | SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); 40 | factoryBean.setJobDetail(alphaJobDetail); 41 | factoryBean.setName("alphaTrigger"); 42 | factoryBean.setGroup("alphaTriggerGroup"); 43 | factoryBean.setRepeatInterval(3000); // 每 3 秒执行一次 44 | factoryBean.setJobDataMap(new JobDataMap()); // Trigger 底层以 JobDataMap() 数据类型保存Job的一些状态 45 | return factoryBean; 46 | } 47 | 48 | 49 | // 刷新帖子分数任务 50 | // @Bean 51 | public JobDetailFactoryBean postScoreRefreshJobDetail() { 52 | JobDetailFactoryBean factoryBean = new JobDetailFactoryBean(); 53 | factoryBean.setJobClass(PostScoreRefreshJob.class); 54 | factoryBean.setName("postScoreRefreshJob"); 55 | factoryBean.setGroup("communityJobGroup"); 56 | factoryBean.setDurability(true); 57 | factoryBean.setRequestsRecovery(true); 58 | return factoryBean; 59 | } 60 | 61 | // @Bean 62 | public SimpleTriggerFactoryBean postScoreRefreshTrigger(JobDetail postScoreRefreshJobDetail) { 63 | SimpleTriggerFactoryBean factoryBean = new SimpleTriggerFactoryBean(); 64 | factoryBean.setJobDetail(postScoreRefreshJobDetail); 65 | factoryBean.setName("postScoreRefreshTrigger"); 66 | factoryBean.setGroup("communityTriggerGroup"); 67 | // factoryBean.setRepeatInterval(1000 * 60 * 5); // 5分钟 68 | factoryBean.setRepeatInterval(1000 * 60 * 120); // 2小时 69 | factoryBean.setJobDataMap(new JobDataMap()); 70 | return factoryBean; 71 | } 72 | 73 | } 74 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/config/RedisConfig.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.config; 2 | 3 | import com.fasterxml.jackson.annotation.JsonAutoDetect; 4 | import com.fasterxml.jackson.annotation.PropertyAccessor; 5 | import com.fasterxml.jackson.databind.ObjectMapper; 6 | import org.springframework.context.annotation.Bean; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.data.redis.connection.RedisConnectionFactory; 9 | import org.springframework.data.redis.core.RedisTemplate; 10 | import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer; 11 | import org.springframework.data.redis.serializer.RedisSerializer; 12 | import org.springframework.data.redis.serializer.StringRedisSerializer; 13 | 14 | @Configuration 15 | public class RedisConfig { 16 | 17 | @Bean 18 | // RedisConnectionFactory 连接工厂,Spring会自动注入这个Bean 19 | public RedisTemplate redisTemplate(RedisConnectionFactory factory) { 20 | RedisTemplate template = new RedisTemplate<>(); 21 | // 配置连接工厂 22 | template.setConnectionFactory(factory); 23 | 24 | RedisSerializer redisSerializer = new StringRedisSerializer(); 25 | //使用Jackson2JsonRedisSerializer来序列化和反序列化redis的value值(默认使用JDK的序列化方式) 26 | Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class); 27 | 28 | ObjectMapper om = new ObjectMapper(); 29 | // 指定要序列化的域,field,get和set,以及修饰符范围,ANY是都有包括private和public 30 | om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); 31 | // 指定序列化输入的类型,类必须是非final修饰的,final修饰的类,比如String,Integer等会跑出异常 32 | om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); 33 | 34 | jackson2JsonRedisSerializer.setObjectMapper(om); 35 | 36 | // 设置key的序列化方式 37 | template.setKeySerializer(redisSerializer); // 序列化为String 38 | // 设置value的序列化方式 39 | template.setValueSerializer(jackson2JsonRedisSerializer); 40 | // // 设置hash的key的序列化方式 当value为hash时,hash有key和value,所以需要设置 41 | // template.setHashKeySerializer(RedisSerializer.string()); 42 | // // 设置hash的value的序列化方式 43 | // template.setHashValueSerializer(RedisSerializer.json()); 44 | //value hashmap序列化 45 | template.setHashValueSerializer(jackson2JsonRedisSerializer); 46 | 47 | template.afterPropertiesSet(); // 使上面设置生效 48 | return template; 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/config/ThreadPoolConfig.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.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 | * Spring 线程池运行需要的配置类 9 | */ 10 | @Configuration 11 | @EnableScheduling 12 | @EnableAsync 13 | public class ThreadPoolConfig { 14 | 15 | } 16 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/config/WebMvcConfig.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.config; 2 | 3 | import fun.linyuhong.myCommunity.interceptor.LoginOnlyOneInterceptor; 4 | import fun.linyuhong.myCommunity.interceptor.LoginTicketInterceptor; 5 | import fun.linyuhong.myCommunity.interceptor.MessageInterceptor; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.context.annotation.Configuration; 8 | import org.springframework.web.servlet.config.annotation.InterceptorRegistry; 9 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; 10 | 11 | /** 12 | * @author linyuhong 13 | * @date 2019/9/1 14 | */ 15 | 16 | @Configuration 17 | public class WebMvcConfig implements WebMvcConfigurer { 18 | 19 | @Autowired 20 | private LoginTicketInterceptor loginTicketInterceptor; 21 | 22 | @Autowired 23 | private LoginOnlyOneInterceptor loginOnlyOneInterceptor; 24 | 25 | @Autowired 26 | private MessageInterceptor messageInterceptor; 27 | 28 | @Override 29 | public void addInterceptors(InterceptorRegistry registry) { 30 | registry.addInterceptor(loginTicketInterceptor) 31 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg"); 32 | 33 | registry.addInterceptor(loginOnlyOneInterceptor) 34 | .addPathPatterns("/login"); 35 | 36 | registry.addInterceptor(messageInterceptor) 37 | .excludePathPatterns("/**/*.css", "/**/*.js", "/**/*.png", "/**/*.jpg", "/**/*.jpeg"); 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/controller/advice/ExceptionAdvice.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.controller.advice; 2 | 3 | 4 | import fun.linyuhong.myCommunity.util.JSONUtil; 5 | import org.slf4j.Logger; 6 | import org.slf4j.LoggerFactory; 7 | import org.springframework.stereotype.Controller; 8 | import org.springframework.web.bind.annotation.ControllerAdvice; 9 | import org.springframework.web.bind.annotation.ExceptionHandler; 10 | 11 | import javax.servlet.http.HttpServletRequest; 12 | import javax.servlet.http.HttpServletResponse; 13 | import java.io.IOException; 14 | import java.io.PrintWriter; 15 | 16 | 17 | /** 18 | * 全局统一异常配置类 19 | */ 20 | @ControllerAdvice(annotations = Controller.class) 21 | public class ExceptionAdvice { 22 | 23 | private static final Logger logger = LoggerFactory.getLogger(ExceptionAdvice.class); 24 | 25 | @ExceptionHandler({Exception.class}) 26 | public void handleException(Exception e, HttpServletRequest request, HttpServletResponse response) throws IOException { 27 | logger.error("服务器发生异常: " + e.getMessage()); 28 | for (StackTraceElement element : e.getStackTrace()) { 29 | logger.error(element.toString()); 30 | } 31 | 32 | String xRequestedWith = request.getHeader("x-requested-with"); 33 | if ("XMLHttpRequest".equals(xRequestedWith)) { 34 | response.setContentType("application/plain;charset=utf-8"); 35 | PrintWriter writer = response.getWriter(); 36 | writer.write(JSONUtil.getJSONString(1, "服务器异常!")); 37 | } else { 38 | response.sendRedirect(request.getContextPath() + "/error"); 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/controller/portal/CommentController.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.controller.portal; 2 | 3 | import fun.linyuhong.myCommunity.async.EventModel; 4 | import fun.linyuhong.myCommunity.async.EventProducer; 5 | import fun.linyuhong.myCommunity.async.EventType; 6 | import fun.linyuhong.myCommunity.common.Const; 7 | import fun.linyuhong.myCommunity.entity.Comment; 8 | import fun.linyuhong.myCommunity.entity.DiscussPost; 9 | import fun.linyuhong.myCommunity.service.ICommentService; 10 | import fun.linyuhong.myCommunity.service.IDiscussPostService; 11 | import fun.linyuhong.myCommunity.util.HostHolder; 12 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 13 | import fun.linyuhong.myCommunity.util.XORUtil; 14 | import org.apache.commons.lang3.StringUtils; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.data.redis.core.RedisTemplate; 17 | import org.springframework.stereotype.Controller; 18 | import org.springframework.web.bind.annotation.*; 19 | 20 | 21 | /** 22 | * @author linyuhong 23 | * @date 2019/9/3 24 | */ 25 | @Controller 26 | @RequestMapping("/comment") 27 | public class CommentController { 28 | 29 | @Autowired 30 | private HostHolder hostHolder; 31 | 32 | @Autowired 33 | private ICommentService iCommentService; 34 | 35 | @Autowired 36 | private EventProducer eventProducer; 37 | 38 | @Autowired 39 | private IDiscussPostService iDiscussPostService; 40 | 41 | @Autowired 42 | private RedisTemplate redisTemplate; 43 | 44 | 45 | @RequestMapping(path = "/add/{discussPostId}", method = RequestMethod.POST) 46 | public String addComment(@PathVariable("discussPostId") Integer discussPostId, Comment comment) { 47 | 48 | if (StringUtils.isBlank(comment.getContent())) { 49 | throw new IllegalArgumentException("评论内容不能为空!"); 50 | } 51 | // 解密 id 52 | comment.setUserId(XORUtil.encryptId(hostHolder.getUser().getId(), Const.getIdEncodeKeys.userIdKeys)); 53 | // 根据传过来的 entityType 判断评论的类型,进行相应的 id 解密 54 | Integer entityType = comment.getEntityType(); 55 | if (entityType == Const.entityType.ENTITY_TYPE_POST) { 56 | comment.setEntityId(XORUtil.encryptId(comment.getEntityId(), Const.getIdEncodeKeys.postIdKeys)); 57 | } else { 58 | // comment.setEntityId(XORUtil.encryptId(comment.getEntityId(), Const.getIdEncodeKeys.userIdKeys)); 59 | comment.setEntityId(comment.getEntityId()); 60 | // 判断是否是 回复 某人,是的话解密其 id 61 | comment.setTargetId(comment.getTargetId() == 0 ? 0 : XORUtil.encryptId(comment.getTargetId(), Const.getIdEncodeKeys.userIdKeys)); 62 | } 63 | 64 | iCommentService.addComment(comment); 65 | 66 | /** 67 | * 触发评论事件 68 | * 69 | */ 70 | EventModel eventModel = new EventModel(EventType.COMMENT) 71 | .setActorId(XORUtil.encryptId(hostHolder.getUser().getId(), Const.getIdEncodeKeys.userIdKeys)) 72 | .setEntityType(entityType) 73 | .setEntityId(comment.getEntityId()) 74 | .setData("postId", XORUtil.encryptId(discussPostId, Const.getIdEncodeKeys.postIdKeys)); // postId 为帖子Id,点击查看详情时用到 对于点赞对象为 comment 时有用 75 | if (comment.getEntityType() == Const.entityType.ENTITY_TYPE_POST) { 76 | DiscussPost target = iDiscussPostService.findDiscussPostById(comment.getEntityId()); 77 | eventModel.setEntityUserId(target.getUserId()); 78 | } else if (comment.getEntityType() == Const.entityType.ENTITY_TYPE_COMMENT) { 79 | Comment target = iCommentService.findCommentById(comment.getEntityId()); 80 | eventModel.setEntityUserId(target.getUserId()); 81 | } 82 | eventProducer.fireEvent(eventModel); 83 | 84 | 85 | 86 | /** 87 | * 触发 添加帖子评论 事件,将修改后的帖子加到 ES 服务器 88 | */ 89 | if (comment.getEntityType() == Const.entityType.ENTITY_TYPE_POST) { 90 | eventModel = new EventModel(EventType.PUBLISH) 91 | .setActorId(hostHolder.getUser().getId()) 92 | .setEntityType(Const.entityType.ENTITY_TYPE_POST) 93 | .setEntityId(XORUtil.encryptId(discussPostId, Const.getIdEncodeKeys.postIdKeys)); 94 | eventProducer.fireEvent(eventModel); 95 | 96 | // 计算帖子分数 97 | String redisKey = RedisKeyUtil.getPostScoreKey(); 98 | redisTemplate.opsForSet().add(redisKey, XORUtil.encryptId(discussPostId, Const.getIdEncodeKeys.postIdKeys)); 99 | } 100 | 101 | 102 | return "redirect:/discuss/detail/" + discussPostId; 103 | } 104 | 105 | } 106 | 107 | 108 | 109 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/controller/portal/LikeController.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.controller.portal; 2 | 3 | import fun.linyuhong.myCommunity.async.EventModel; 4 | import fun.linyuhong.myCommunity.async.EventProducer; 5 | import fun.linyuhong.myCommunity.async.EventType; 6 | import fun.linyuhong.myCommunity.common.Const; 7 | import fun.linyuhong.myCommunity.service.ILikeService; 8 | import fun.linyuhong.myCommunity.util.HostHolder; 9 | import fun.linyuhong.myCommunity.util.JSONUtil; 10 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 11 | import fun.linyuhong.myCommunity.util.XORUtil; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.data.redis.core.RedisTemplate; 14 | import org.springframework.stereotype.Controller; 15 | import org.springframework.web.bind.annotation.RequestMapping; 16 | import org.springframework.web.bind.annotation.RequestMethod; 17 | import org.springframework.web.bind.annotation.ResponseBody; 18 | 19 | import java.util.HashMap; 20 | import java.util.Map; 21 | 22 | /** 23 | * @author linyuhong 24 | * @date 2019/9/5 25 | */ 26 | @Controller 27 | public class LikeController { 28 | 29 | @Autowired 30 | private HostHolder hostHolder; 31 | 32 | @Autowired 33 | private ILikeService iLikeService; 34 | 35 | @Autowired 36 | private EventProducer eventProducer; 37 | 38 | @Autowired 39 | private RedisTemplate redisTemplate; 40 | 41 | 42 | /** 43 | * 44 | * @param entityType 实体类型 45 | * @param entityId 实体Id 46 | * @param entityUserId 被点赞的用户Id 47 | * @return 48 | */ 49 | @RequestMapping(path = "/like", method = RequestMethod.POST) 50 | @ResponseBody 51 | public String like(int entityType, int entityId, int entityUserId, int postId) { 52 | // 解密Id 53 | Integer userId = XORUtil.encryptId(hostHolder.getUser().getId(), Const.getIdEncodeKeys.userIdKeys); 54 | entityUserId = XORUtil.encryptId(entityUserId, Const.getIdEncodeKeys.userIdKeys); 55 | // 判断是 帖子id 或者 用户id,进行解密 评论id则不理会,因为我没有加密 56 | if (entityType == Const.like.ENTITY_TYPE_POST) { 57 | entityId = XORUtil.encryptId(entityId, Const.getIdEncodeKeys.postIdKeys); 58 | } else if (entityType == Const.like.ENTITY_TYPE_USER) { 59 | entityId = XORUtil.encryptId(entityId, Const.getIdEncodeKeys.userIdKeys); 60 | } 61 | 62 | iLikeService.like(userId, entityType, entityId, entityUserId); 63 | 64 | 65 | // 数量 66 | long likeCount = iLikeService.findEntityLikeCount(entityType, entityId); 67 | // 状态 68 | int likeStatus = iLikeService.findEntityLikeStatus(userId, entityType, entityId); 69 | // 返回的结果 70 | Map map = new HashMap<>(); 71 | map.put("likeCount", likeCount); 72 | map.put("likeStatus", likeStatus); 73 | 74 | /** 75 | * 触发点赞事件 76 | * 自己给自己点赞不触发 77 | */ 78 | if (likeStatus == 1 && userId != entityUserId) { 79 | EventModel eventModel = new EventModel(EventType.LIKE) 80 | .setActorId(userId) 81 | .setEntityType(entityType) 82 | .setEntityId(entityId) 83 | .setEntityUserId(entityUserId) 84 | .setData("postId", XORUtil.encryptId(postId, Const.getIdEncodeKeys.postIdKeys)); // postId 为帖子Id,点击查看详情时用到 对于点赞对象为 comment 时有用 85 | eventProducer.fireEvent(eventModel); 86 | } 87 | 88 | if(entityType == Const.entityType.ENTITY_TYPE_POST) { // 只计算对帖子点赞的分 89 | // 计算帖子分数 90 | String redisKey = RedisKeyUtil.getPostScoreKey(); 91 | redisTemplate.opsForSet().add(redisKey, XORUtil.encryptId(postId, Const.getIdEncodeKeys.postIdKeys)); 92 | } 93 | 94 | return JSONUtil.getJSONString(0, null, map); 95 | } 96 | 97 | } 98 | 99 | 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/controller/portal/RegisterController.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.controller.portal; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.entity.User; 5 | import fun.linyuhong.myCommunity.service.IUserService; 6 | import org.apache.commons.lang3.StringUtils; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Component; 9 | import org.springframework.stereotype.Controller; 10 | import org.springframework.ui.Model; 11 | import org.springframework.web.bind.annotation.PathVariable; 12 | import org.springframework.web.bind.annotation.RequestMapping; 13 | import org.springframework.web.bind.annotation.RequestMethod; 14 | 15 | import java.util.Map; 16 | 17 | /** 18 | * @author linyuhong 19 | * @date 2019/9/2 20 | */ 21 | @Controller 22 | public class RegisterController { 23 | 24 | @Autowired 25 | private IUserService iUserService; 26 | 27 | @RequestMapping(value = "/register", method = RequestMethod.GET) 28 | public String register() { 29 | return "/site/register"; 30 | } 31 | 32 | @RequestMapping(value = "/register", method = RequestMethod.POST) 33 | public String register(User user, Model model) { 34 | 35 | Map map = iUserService.register(user); 36 | if (map == null || map.isEmpty()) { 37 | model.addAttribute("msg", "注册成功,我们已经向您的邮箱发送了一封激活邮件,请尽快激活!"); 38 | model.addAttribute("target", "/index"); 39 | return "/site/operate-result"; 40 | } else { 41 | model.addAttribute("usernameMsg", map.get("usernameMsg")); 42 | model.addAttribute("passwordMsg", map.get("passwordMsg")); 43 | model.addAttribute("emailMsg", map.get("emailMsg")); 44 | return "/site/register"; 45 | } 46 | } 47 | 48 | @RequestMapping(value = "/activation/{userId}/{code}", method = RequestMethod.GET) 49 | public String activation(@PathVariable("userId") Integer userId, @PathVariable("code") String code, Model model) { 50 | 51 | // 用 Integer 类型可以接收空值null,避免使用 int 报错 52 | if (userId == null || StringUtils.isBlank(code)) { 53 | model.addAttribute("msg", "参数错误"); 54 | model.addAttribute("target", "/index"); 55 | } 56 | int result = iUserService.activation(userId, code); 57 | if (result == Const.isExist.NOEXIST) { 58 | model.addAttribute("msg", "用户不存在"); 59 | model.addAttribute("target", "/index"); 60 | }else if (result == Const.active.ACTIVATION_SUCCESS) { 61 | model.addAttribute("msg", "激活成功,您的账号已经可以正常使用了!"); 62 | model.addAttribute("target", "/login"); 63 | } else if (result == Const.active.ACTIVATION_REPEAT) { 64 | model.addAttribute("msg", "无效操作,该账号已经激活过了!"); 65 | model.addAttribute("target", "/index"); 66 | } else { 67 | model.addAttribute("msg", "激活失败,您提供的激活码不正确!"); 68 | model.addAttribute("target", "/index"); 69 | } 70 | return "/site/operate-result"; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/controller/portal/SearchController.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.controller.portal; 2 | 3 | 4 | import fun.linyuhong.myCommunity.common.Const; 5 | import fun.linyuhong.myCommunity.common.Page; 6 | import fun.linyuhong.myCommunity.entity.DiscussPost; 7 | import fun.linyuhong.myCommunity.service.IElasticsearchService; 8 | import fun.linyuhong.myCommunity.service.ILikeService; 9 | import fun.linyuhong.myCommunity.service.IUserService; 10 | import fun.linyuhong.myCommunity.util.XORUtil; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.stereotype.Controller; 13 | import org.springframework.ui.Model; 14 | import org.springframework.web.bind.annotation.RequestMapping; 15 | import org.springframework.web.bind.annotation.RequestMethod; 16 | 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Map; 21 | 22 | @Controller 23 | public class SearchController { 24 | 25 | @Autowired 26 | private IElasticsearchService iElasticsearchService; 27 | 28 | @Autowired 29 | private IUserService iUserService; 30 | 31 | @Autowired 32 | private ILikeService iLikeService; 33 | 34 | // search?keyword=xxx 35 | @RequestMapping(path = "/search", method = RequestMethod.GET) 36 | public String search(String keyword, Page page, Model model) { 37 | // 搜索帖子 38 | org.springframework.data.domain.Page searchResult = 39 | iElasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit()); 40 | // 聚合数据 41 | List> discussPosts = new ArrayList<>(); 42 | if (searchResult != null) { 43 | for (DiscussPost post : searchResult) { 44 | Map map = new HashMap<>(); 45 | // 帖子 46 | // id 加密 47 | post.setId(XORUtil.encryptId(post.getId(), Const.getIdEncodeKeys.postIdKeys)); 48 | map.put("post", post); 49 | // 作者 50 | map.put("user", iUserService.findUserById(post.getUserId())); 51 | // 点赞数量 52 | map.put("likeCount", iLikeService.findEntityLikeCount(Const.entityType.ENTITY_TYPE_POST, post.getId())); 53 | 54 | discussPosts.add(map); 55 | } 56 | } 57 | model.addAttribute("discussPosts", discussPosts); 58 | model.addAttribute("keyword", keyword); 59 | 60 | // 分页信息 61 | page.setPath("/search?keyword=" + keyword); 62 | page.setRows(searchResult == null ? 0 : (int) searchResult.getTotalElements()); 63 | 64 | return "/site/search"; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/dao/CommentMapper.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.dao; 2 | 3 | import fun.linyuhong.myCommunity.entity.Comment; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | import java.util.List; 7 | 8 | @Mapper 9 | public interface CommentMapper { 10 | // int deleteByPrimaryKey(Integer id); 11 | // 12 | // int insert(Comment record); 13 | // 14 | // int insertSelective(Comment record); 15 | // 16 | // Comment selectByPrimaryKey(Integer id); 17 | // 18 | // int updateByPrimaryKeySelective(Comment record); 19 | // 20 | // int updateByPrimaryKeyWithBLOBs(Comment record); 21 | // 22 | // int updateByPrimaryKey(Comment record); 23 | 24 | List selectCommentsByEntity(int entityType, int entityId, int offset, int limit); 25 | 26 | int selectCountByEntity(int entityType, int entityId); 27 | 28 | int insertComment(Comment comment); 29 | 30 | Comment selectCommentById(int id); 31 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/dao/DiscussPostMapper.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.dao; 2 | 3 | import fun.linyuhong.myCommunity.entity.DiscussPost; 4 | import org.apache.ibatis.annotations.Mapper; 5 | import org.apache.ibatis.annotations.Param; 6 | 7 | import java.util.List; 8 | 9 | 10 | @Mapper 11 | public interface DiscussPostMapper { 12 | 13 | // orderMode 默认值是0 按原先模式排,为1表示按热度排 14 | List selectDiscussPosts(int userId, int orderMode, int offset, int limit); 15 | 16 | // @Param注解用于给参数取别名, 17 | // 如果只有一个参数,并且在里使用,则必须加别名. 18 | int selectDiscussPostRows(@Param("userId") int userId); 19 | 20 | int insertDiscussPost(DiscussPost discussPost); 21 | 22 | int updateCommentCount(int id, int commentCount); 23 | 24 | DiscussPost selectDiscussPostById(int id); 25 | 26 | int updateType(int id, int type); 27 | 28 | int updateStatus(int id, int status); 29 | 30 | int updateScore(int id, double score); 31 | 32 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/dao/MessageMapper.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.dao; 2 | 3 | import fun.linyuhong.myCommunity.entity.Message; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | import java.util.List; 7 | 8 | @Mapper 9 | public interface MessageMapper { 10 | // int deleteByPrimaryKey(Integer id); 11 | // 12 | // int insert(Message record); 13 | // 14 | // int insertSelective(Message record); 15 | // 16 | // Message selectByPrimaryKey(Integer id); 17 | // 18 | // int updateByPrimaryKeySelective(Message record); 19 | // 20 | // int updateByPrimaryKeyWithBLOBs(Message record); 21 | // 22 | // int updateByPrimaryKey(Message record); 23 | 24 | // 查询当前用户的会话数量. 分页用 25 | int selectConversationCount(int userId); 26 | 27 | // 查询当前用户的会话列表,针对每个会话只返回一条最新的私信. 28 | List selectConversations(int userId, int offset, int limit); 29 | 30 | // 查询某个会话所包含的私信数量. 31 | int selectLetterCount(String conversationId); 32 | 33 | // 查询未读私信的数量 通过拼接 conversationId 决定是查询所有未读数量还是某条会话的未读数量 34 | int selectLetterUnreadCount(int userId, String conversationId); 35 | 36 | // 查询某个会话所包含的私信列表. 某条会话的详细信息 37 | List selectLetters(String conversationId, int offset, int limit); 38 | 39 | int updateStatus(List ids, int status); 40 | 41 | // 新增消息 42 | int insertMessage(Message message); 43 | 44 | // 查询某个主题下的最新通知 45 | Message selectLatestNotice(int userId, String topic); 46 | 47 | // 查询某个主题所包含的通知数量 48 | int selectNoticeCount(int userId, String topic); 49 | 50 | // 查询未读的通知的数量 51 | int selectNoticeUnreadCount(int userId, String topic); 52 | 53 | // 查询某个主题所包含的通知列表 54 | List selectNotices(int userId, String topic, int offset, int limit); 55 | 56 | 57 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/dao/UserMapper.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.dao; 2 | 3 | import fun.linyuhong.myCommunity.entity.User; 4 | import org.apache.ibatis.annotations.Mapper; 5 | 6 | @Mapper 7 | public interface UserMapper { 8 | // int deleteByPrimaryKey(Integer id); 9 | // 10 | // int insert(User record); 11 | // 12 | // int insertSelective(User record); 13 | // 14 | // 15 | // int updateByPrimaryKeySelective(User record); 16 | // 17 | // int updateByPrimaryKey(User record); 18 | 19 | User selectByPrimaryKey(Integer id); 20 | 21 | User selectByUsername(String username); 22 | 23 | User selectByUserEmail(String email); 24 | 25 | int updateStatus(int userId, int status); // 更新用户激活状态 26 | 27 | int insertUser(User user); 28 | 29 | int updateHeader(int userId, String headerUrl); 30 | 31 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/dao/elasticsearch/DiscussPostRepository.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.dao.elasticsearch; 2 | 3 | 4 | import fun.linyuhong.myCommunity.entity.DiscussPost; 5 | import org.springframework.data.elasticsearch.repository.ElasticsearchRepository; 6 | import org.springframework.stereotype.Repository; 7 | 8 | // 操作ES数据库 9 | @Repository 10 | public interface DiscussPostRepository extends ElasticsearchRepository { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/Comment.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.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 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/DiscussPost.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.entity; 2 | 3 | 4 | import org.springframework.data.annotation.Id; 5 | import org.springframework.data.elasticsearch.annotations.Document; 6 | import org.springframework.data.elasticsearch.annotations.Field; 7 | import org.springframework.data.elasticsearch.annotations.FieldType; 8 | 9 | import java.util.Date; 10 | 11 | // 索引名字 文档,起到占位作用 分片数量 副本数量 12 | @Document(indexName = "discusspost", type = "_doc", shards = 6, replicas = 2) 13 | public class DiscussPost { 14 | 15 | @Id 16 | private int id; 17 | 18 | @Field(type = FieldType.Integer) 19 | private int userId; 20 | 21 | // 互联网校招(可以分为 "互联网" "校招"等关键词建立索引,通过该关键词就可以找到这句话) 22 | // 存储时候的分词器(分解为关键词的依据) 搜索时候的分词器(搜索时拆分关键词不用那么细) 23 | @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") 24 | private String title; 25 | 26 | @Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart") 27 | private String content; 28 | 29 | @Field(type = FieldType.Integer) 30 | private int type; 31 | 32 | @Field(type = FieldType.Integer) 33 | private int status; 34 | 35 | @Field(type = FieldType.Date) 36 | private Date createTime; 37 | 38 | @Field(type = FieldType.Integer) 39 | private int commentCount; 40 | 41 | @Field(type = FieldType.Double) 42 | private double score; 43 | 44 | public int getId() { 45 | return id; 46 | } 47 | 48 | public void setId(int id) { 49 | this.id = id; 50 | } 51 | 52 | public int getUserId() { 53 | return userId; 54 | } 55 | 56 | public void setUserId(int userId) { 57 | this.userId = userId; 58 | } 59 | 60 | public String getTitle() { 61 | return title; 62 | } 63 | 64 | public void setTitle(String title) { 65 | this.title = title; 66 | } 67 | 68 | public String getContent() { 69 | return content; 70 | } 71 | 72 | public void setContent(String content) { 73 | this.content = content; 74 | } 75 | 76 | public int getType() { 77 | return type; 78 | } 79 | 80 | public void setType(int type) { 81 | this.type = type; 82 | } 83 | 84 | public int getStatus() { 85 | return status; 86 | } 87 | 88 | public void setStatus(int status) { 89 | this.status = status; 90 | } 91 | 92 | public Date getCreateTime() { 93 | return createTime; 94 | } 95 | 96 | public void setCreateTime(Date createTime) { 97 | this.createTime = createTime; 98 | } 99 | 100 | public int getCommentCount() { 101 | return commentCount; 102 | } 103 | 104 | public void setCommentCount(int commentCount) { 105 | this.commentCount = commentCount; 106 | } 107 | 108 | public double getScore() { 109 | return score; 110 | } 111 | 112 | public void setScore(double score) { 113 | this.score = score; 114 | } 115 | 116 | @Override 117 | public String toString() { 118 | return "DiscussPost{" + 119 | "id=" + id + 120 | ", userId=" + userId + 121 | ", title='" + title + '\'' + 122 | ", content='" + content + '\'' + 123 | ", type=" + type + 124 | ", status=" + status + 125 | ", createTime=" + createTime + 126 | ", commentCount=" + commentCount + 127 | ", score=" + score + 128 | '}'; 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/JwtUser.java: -------------------------------------------------------------------------------- 1 | //package fun.linyuhong.myCommunity.entity; 2 | // 3 | //import fun.linyuhong.myCommunity.common.Const; 4 | //import org.springframework.security.core.GrantedAuthority; 5 | //import org.springframework.security.core.authority.SimpleGrantedAuthority; 6 | //import org.springframework.security.core.userdetails.UserDetails; 7 | // 8 | //import java.util.Arrays; 9 | //import java.util.Collection; 10 | // 11 | ///** 12 | // * @author linyuhong 13 | // * @date 2019/9/1 14 | // */ 15 | //public class JwtUser implements UserDetails { 16 | // 17 | // private User user; 18 | // 19 | // public JwtUser(User user) { 20 | // this.user = user; 21 | // } 22 | // 23 | // 24 | // @Override 25 | // public Collection getAuthorities() { 26 | // //返回当前用户的权限 27 | // return Arrays.asList(new SimpleGrantedAuthority(Const.Role.getRole(user.getType()))); 28 | // } 29 | // 30 | // @Override 31 | // public String getPassword() { 32 | // return user.getPassword(); 33 | // } 34 | // 35 | // @Override 36 | // public String getUsername() { 37 | // return user.getUsername(); 38 | // } 39 | // 40 | // //账户是否未过期 41 | // @Override 42 | // public boolean isAccountNonExpired() { 43 | // return true; 44 | // } 45 | // 46 | // //账户是否未被锁 47 | // @Override 48 | // public boolean isAccountNonLocked() { 49 | // return true; 50 | // } 51 | // 52 | // @Override 53 | // public boolean isCredentialsNonExpired() { 54 | // return true; 55 | // } 56 | // 57 | // //是否启用 58 | // @Override 59 | // public boolean isEnabled() { 60 | // return user.getStatus() == 1; 61 | // } 62 | //} 63 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/LoginTicket.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.entity; 2 | 3 | import java.io.Serializable; 4 | import java.util.Date; 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/1 9 | */ 10 | public class LoginTicket { 11 | 12 | private int id; 13 | private int userId; 14 | private String ticket; 15 | private int status; // 该用户是否登录状态 16 | private Date expired; 17 | 18 | public int getId() { 19 | return id; 20 | } 21 | 22 | public void setId(int id) { 23 | this.id = id; 24 | } 25 | 26 | public int getUserId() { 27 | return userId; 28 | } 29 | 30 | public void setUserId(int userId) { 31 | this.userId = userId; 32 | } 33 | 34 | public String getTicket() { 35 | return ticket; 36 | } 37 | 38 | public void setTicket(String ticket) { 39 | this.ticket = ticket; 40 | } 41 | 42 | public int getStatus() { 43 | return status; 44 | } 45 | 46 | public void setStatus(int status) { 47 | this.status = status; 48 | } 49 | 50 | public Date getExpired() { 51 | return expired; 52 | } 53 | 54 | public void setExpired(Date expired) { 55 | this.expired = expired; 56 | } 57 | 58 | @Override 59 | public String toString() { 60 | return "LoginTicket{" + 61 | "id=" + id + 62 | ", userId=" + userId + 63 | ", ticket='" + ticket + '\'' + 64 | ", status=" + status + 65 | ", expired=" + expired + 66 | '}'; 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/Message.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.entity; 2 | 3 | import java.util.Date; 4 | 5 | public class Message { 6 | private Integer id; 7 | 8 | private Integer fromId; 9 | 10 | private Integer toId; 11 | 12 | private String conversationId; 13 | 14 | private Integer status; 15 | 16 | private Date createTime; 17 | 18 | private String content; 19 | 20 | public Message(Integer id, Integer fromId, Integer toId, String conversationId, Integer status, Date createTime, String content) { 21 | this.id = id; 22 | this.fromId = fromId; 23 | this.toId = toId; 24 | this.conversationId = conversationId; 25 | this.status = status; 26 | this.createTime = createTime; 27 | this.content = content; 28 | } 29 | 30 | public Message() { 31 | super(); 32 | } 33 | 34 | public Integer getId() { 35 | return id; 36 | } 37 | 38 | public void setId(Integer id) { 39 | this.id = id; 40 | } 41 | 42 | public Integer getFromId() { 43 | return fromId; 44 | } 45 | 46 | public void setFromId(Integer fromId) { 47 | this.fromId = fromId; 48 | } 49 | 50 | public Integer getToId() { 51 | return toId; 52 | } 53 | 54 | public void setToId(Integer toId) { 55 | this.toId = toId; 56 | } 57 | 58 | public String getConversationId() { 59 | return conversationId; 60 | } 61 | 62 | public void setConversationId(String conversationId) { 63 | this.conversationId = conversationId == null ? null : conversationId.trim(); 64 | } 65 | 66 | public Integer getStatus() { 67 | return status; 68 | } 69 | 70 | public void setStatus(Integer status) { 71 | this.status = status; 72 | } 73 | 74 | public Date getCreateTime() { 75 | return createTime; 76 | } 77 | 78 | public void setCreateTime(Date createTime) { 79 | this.createTime = createTime; 80 | } 81 | 82 | public String getContent() { 83 | return content; 84 | } 85 | 86 | public void setContent(String content) { 87 | this.content = content == null ? null : content.trim(); 88 | } 89 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/entity/User.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.entity; 2 | 3 | import java.util.Date; 4 | 5 | public class User { 6 | private Integer id; 7 | 8 | private String username; 9 | 10 | private String password; 11 | 12 | private String salt; 13 | 14 | private String email; 15 | 16 | private Integer type; 17 | 18 | private Integer status; 19 | 20 | private String activationCode; 21 | 22 | private String headerUrl; 23 | 24 | private Date createTime; 25 | 26 | public User(Integer id, String username, String password, String salt, String email, Integer type, Integer status, String activationCode, String headerUrl, Date createTime) { 27 | this.id = id; 28 | this.username = username; 29 | this.password = password; 30 | this.salt = salt; 31 | this.email = email; 32 | this.type = type; 33 | this.status = status; 34 | this.activationCode = activationCode; 35 | this.headerUrl = headerUrl; 36 | this.createTime = createTime; 37 | } 38 | 39 | public User() { 40 | super(); 41 | } 42 | 43 | public Integer getId() { 44 | return id; 45 | } 46 | 47 | public void setId(Integer id) { 48 | this.id = id; 49 | } 50 | 51 | public String getUsername() { 52 | return username; 53 | } 54 | 55 | public void setUsername(String username) { 56 | this.username = username == null ? null : username.trim(); 57 | } 58 | 59 | public String getPassword() { 60 | return password; 61 | } 62 | 63 | public void setPassword(String password) { 64 | this.password = password == null ? null : password.trim(); 65 | } 66 | 67 | public String getSalt() { 68 | return salt; 69 | } 70 | 71 | public void setSalt(String salt) { 72 | this.salt = salt == null ? null : salt.trim(); 73 | } 74 | 75 | public String getEmail() { 76 | return email; 77 | } 78 | 79 | public void setEmail(String email) { 80 | this.email = email == null ? null : email.trim(); 81 | } 82 | 83 | public Integer getType() { 84 | return type; 85 | } 86 | 87 | public void setType(Integer type) { 88 | this.type = type; 89 | } 90 | 91 | public Integer getStatus() { 92 | return status; 93 | } 94 | 95 | public void setStatus(Integer status) { 96 | this.status = status; 97 | } 98 | 99 | public String getActivationCode() { 100 | return activationCode; 101 | } 102 | 103 | public void setActivationCode(String activationCode) { 104 | this.activationCode = activationCode == null ? null : activationCode.trim(); 105 | } 106 | 107 | public String getHeaderUrl() { 108 | return headerUrl; 109 | } 110 | 111 | public void setHeaderUrl(String headerUrl) { 112 | this.headerUrl = headerUrl == null ? null : headerUrl.trim(); 113 | } 114 | 115 | public Date getCreateTime() { 116 | return createTime; 117 | } 118 | 119 | public void setCreateTime(Date createTime) { 120 | this.createTime = createTime; 121 | } 122 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/interceptor/LoginOnlyOneInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.interceptor; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.entity.LoginTicket; 5 | import fun.linyuhong.myCommunity.service.IUserService; 6 | import fun.linyuhong.myCommunity.util.CookieUtil; 7 | import fun.linyuhong.myCommunity.util.XORUtil; 8 | import fun.linyuhong.myCommunity.vo.UserVo; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.servlet.HandlerInterceptor; 12 | 13 | import javax.servlet.http.HttpServletRequest; 14 | import javax.servlet.http.HttpServletResponse; 15 | import java.util.Date; 16 | 17 | /** 18 | * 防止重复登录 19 | * @author linyuhong 20 | * @date 2019/9/1 21 | */ 22 | @Component 23 | public class LoginOnlyOneInterceptor implements HandlerInterceptor { 24 | 25 | @Autowired 26 | private IUserService userService; 27 | 28 | @Override 29 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 30 | // loginTicket.getTicket() 31 | String ticket = CookieUtil.getValue(request, Const.ticket.TICKET); 32 | if (ticket != null) { 33 | // ticket:UUID 34 | LoginTicket loginTicket = userService.findLoginTicket(ticket); 35 | if (loginTicket != null && loginTicket.getStatus() == Const.loginStatus.VALID && loginTicket.getExpired().after(new Date())) { 36 | response.sendRedirect("/index"); 37 | return false; 38 | } 39 | } 40 | return true; 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/interceptor/LoginTicketInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.interceptor; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.dao.UserMapper; 5 | import fun.linyuhong.myCommunity.entity.LoginTicket; 6 | import fun.linyuhong.myCommunity.entity.User; 7 | import fun.linyuhong.myCommunity.service.IUserService; 8 | import fun.linyuhong.myCommunity.util.CookieUtil; 9 | import fun.linyuhong.myCommunity.util.HostHolder; 10 | import fun.linyuhong.myCommunity.util.XORUtil; 11 | import fun.linyuhong.myCommunity.vo.UserVo; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 14 | import org.springframework.security.core.Authentication; 15 | import org.springframework.security.core.context.SecurityContextHolder; 16 | import org.springframework.security.core.context.SecurityContextImpl; 17 | import org.springframework.security.web.http.SecurityHeaders; 18 | import org.springframework.stereotype.Component; 19 | import org.springframework.web.servlet.HandlerInterceptor; 20 | import org.springframework.web.servlet.ModelAndView; 21 | 22 | import javax.servlet.http.HttpServletRequest; 23 | import javax.servlet.http.HttpServletResponse; 24 | import java.util.Date; 25 | 26 | /** 27 | * 获取每次请求中的用户信息和验证是否有效 28 | * @author linyuhong 29 | * @date 2019/9/1 30 | */ 31 | @Component 32 | public class LoginTicketInterceptor implements HandlerInterceptor { 33 | 34 | @Autowired 35 | private HostHolder hostHolder; 36 | 37 | @Autowired 38 | private IUserService userService; 39 | 40 | @Autowired 41 | private UserMapper userMapper; 42 | 43 | 44 | @Override 45 | public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { 46 | // loginTicket.getTicket() 47 | String ticket = CookieUtil.getValue(request, Const.ticket.TICKET); 48 | if (ticket != null) { 49 | // ticket:UUID 50 | LoginTicket loginTicket = userService.findLoginTicket(ticket); 51 | if (loginTicket != null && loginTicket.getStatus() == Const.loginStatus.VALID && loginTicket.getExpired().after(new Date())) { 52 | // 对 userId 加密,只分装必要的信息,密码不泄漏 53 | UserVo userVo = userService.findUserById(XORUtil.encryptId(loginTicket.getUserId(), Const.getIdEncodeKeys.userIdKeys)); 54 | // 在本次请求中持有的用户 55 | hostHolder.setUser(userVo); 56 | 57 | // 构建用户认证的结果,并存入SecurityContext,以便于Security进行授权. 58 | // authentication 认证结果 59 | User user = userMapper.selectByPrimaryKey(XORUtil.encryptId(userVo.getId(), Const.getIdEncodeKeys.userIdKeys)); 60 | Authentication authentication = new UsernamePasswordAuthenticationToken( 61 | userVo.getId(), user.getPassword(), userService.getAuthorities(user.getId()) 62 | ); 63 | SecurityContextHolder.setContext(new SecurityContextImpl(authentication)); 64 | } 65 | } 66 | return true; 67 | } 68 | 69 | // 在Controller的方法调用之后执行,但是它会在DispatcherServlet进行视图的渲染之前执行, 70 | // 也就是说在这个方法中你可以对ModelAndView进行操作。 71 | @Override 72 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 73 | UserVo user = hostHolder.getUser(); 74 | if (user != null && modelAndView != null) { 75 | modelAndView.addObject("loginUser", user); 76 | } 77 | } 78 | 79 | // 该方法将在整个请求完成之后,也就是DispatcherServlet渲染了视图 80 | @Override 81 | public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { 82 | hostHolder.clear(); 83 | SecurityContextHolder.clearContext(); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/interceptor/MessageInterceptor.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.interceptor; 2 | 3 | 4 | import fun.linyuhong.myCommunity.common.Const; 5 | import fun.linyuhong.myCommunity.service.IMessageService; 6 | import fun.linyuhong.myCommunity.util.HostHolder; 7 | import fun.linyuhong.myCommunity.util.XORUtil; 8 | import fun.linyuhong.myCommunity.vo.UserVo; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.stereotype.Component; 11 | import org.springframework.web.servlet.HandlerInterceptor; 12 | import org.springframework.web.servlet.ModelAndView; 13 | 14 | import javax.servlet.http.HttpServletRequest; 15 | import javax.servlet.http.HttpServletResponse; 16 | 17 | /** 18 | * 展示标题头总的未读信息数量 19 | */ 20 | @Component 21 | public class MessageInterceptor implements HandlerInterceptor { 22 | 23 | @Autowired 24 | private HostHolder hostHolder; 25 | 26 | @Autowired 27 | private IMessageService iMessageService; 28 | 29 | @Override 30 | public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { 31 | UserVo user = hostHolder.getUser(); 32 | if (user != null && modelAndView != null) { 33 | int userId = XORUtil.encryptId(user.getId(), Const.getIdEncodeKeys.userIdKeys); 34 | int letterUnreadCount = iMessageService.findLetterUnreadCount(userId, null); 35 | int noticeUnreadCount = iMessageService.findNoticeUnreadCount(userId, null); 36 | modelAndView.addObject("allUnreadCount", letterUnreadCount + noticeUnreadCount); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/quartz/AlphaJob.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.quartz; 2 | 3 | import org.quartz.Job; 4 | import org.quartz.JobExecutionContext; 5 | import org.quartz.JobExecutionException; 6 | 7 | 8 | public class AlphaJob implements Job { 9 | 10 | @Override 11 | public void execute(JobExecutionContext context) throws JobExecutionException { 12 | System.out.println(Thread.currentThread().getName() + ": execute a quartz job"); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/quartz/PostScoreRefreshJob.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.quartz; 2 | 3 | 4 | import fun.linyuhong.myCommunity.common.Const; 5 | import fun.linyuhong.myCommunity.entity.DiscussPost; 6 | import fun.linyuhong.myCommunity.service.IDiscussPostService; 7 | import fun.linyuhong.myCommunity.service.IElasticsearchService; 8 | import fun.linyuhong.myCommunity.service.ILikeService; 9 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 10 | import org.quartz.Job; 11 | import org.quartz.JobExecutionContext; 12 | import org.quartz.JobExecutionException; 13 | import org.slf4j.Logger; 14 | import org.slf4j.LoggerFactory; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.data.redis.core.BoundSetOperations; 17 | import org.springframework.data.redis.core.RedisTemplate; 18 | 19 | import java.text.ParseException; 20 | import java.text.SimpleDateFormat; 21 | import java.util.Date; 22 | 23 | public class PostScoreRefreshJob implements Job { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(PostScoreRefreshJob.class); 26 | 27 | @Autowired 28 | private RedisTemplate redisTemplate; 29 | 30 | @Autowired 31 | private IDiscussPostService iDiscussPostService; 32 | 33 | @Autowired 34 | private ILikeService iLikeService; 35 | 36 | @Autowired 37 | private IElasticsearchService iElasticsearchService; 38 | 39 | // 溢出网纪元 40 | private static final Date epoch; 41 | 42 | static { 43 | try { 44 | // 把字符串转化为日期 45 | epoch = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2019-09-01 00:00:00"); 46 | } catch (ParseException e) { 47 | throw new RuntimeException("初始化溢出网纪元失败!", e); 48 | } 49 | } 50 | 51 | @Override 52 | public void execute(JobExecutionContext context) throws JobExecutionException { 53 | String redisKey = RedisKeyUtil.getPostScoreKey(); 54 | BoundSetOperations operations = redisTemplate.boundSetOps(redisKey); 55 | 56 | // 例如半夜时间段没人访问网站帖子,就不用刷新了 57 | if (operations.size() == 0) { 58 | logger.info("[任务取消] 没有需要刷新的帖子!"); 59 | return; 60 | } 61 | 62 | // 统计每次刷新的时间,将来如果出现很卡的现象可以分析 63 | logger.info("[任务开始] 正在刷新帖子分数: " + operations.size()); 64 | while (operations.size() > 0) { 65 | this.refresh((Integer) operations.pop()); 66 | } 67 | logger.info("[任务结束] 帖子分数刷新完毕!"); 68 | } 69 | 70 | private void refresh(int postId) { 71 | DiscussPost post = iDiscussPostService.findDiscussPostById(postId); 72 | // 可能在要计算的时候被删除了 73 | if (post == null) { 74 | logger.error("该帖子不存在: id = " + postId); 75 | return; 76 | } 77 | 78 | // 是否精华 79 | boolean wonderful = post.getStatus() == 1; 80 | // 评论数量 81 | int commentCount = post.getCommentCount(); 82 | // 点赞数量 83 | long likeCount = iLikeService.findEntityLikeCount(Const.entityType.ENTITY_TYPE_POST, postId); 84 | 85 | // 计算权重 86 | double w = (wonderful ? 75 : 0) + commentCount * 10 + likeCount * 2; 87 | // 分数 = 帖子权重 + 距离天数 88 | // Math.log10(x) x不能小于1,否则就是负数了 89 | double score = Math.log10(Math.max(w, 1)) 90 | + (post.getCreateTime().getTime() - epoch.getTime()) / (1000 * 3600 * 24); // 得到毫秒值,换算成天 91 | // 更新帖子分数 92 | iDiscussPostService.updateScore(postId, score); 93 | // 同步搜索数据 94 | post.setScore(score); 95 | iElasticsearchService.saveDiscussPost(post); 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/AlphaService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.scheduling.annotation.Async; 7 | import org.springframework.stereotype.Service; 8 | 9 | 10 | @Service 11 | //@Scope("prototype") 12 | public class AlphaService { 13 | 14 | private static final Logger logger = LoggerFactory.getLogger(AlphaService.class); 15 | 16 | 17 | // 让该方法在多线程环境下,被异步的调用. 18 | @Async 19 | public void execute1() { 20 | logger.debug("execute1"); 21 | } 22 | 23 | // fixedRate 固定频率 24 | /*@Scheduled(initialDelay = 10000, fixedRate = 1000)*/ 25 | public void execute2() { 26 | logger.debug("execute2"); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/DataService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | 4 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.dao.DataAccessException; 7 | import org.springframework.data.redis.connection.RedisConnection; 8 | import org.springframework.data.redis.connection.RedisStringCommands; 9 | import org.springframework.data.redis.core.RedisCallback; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.stereotype.Service; 12 | 13 | import java.text.SimpleDateFormat; 14 | import java.util.ArrayList; 15 | import java.util.Calendar; 16 | import java.util.Date; 17 | import java.util.List; 18 | 19 | /** 20 | * 网站数据统计 21 | */ 22 | @Service 23 | public class DataService { 24 | 25 | @Autowired 26 | private RedisTemplate redisTemplate; 27 | 28 | private SimpleDateFormat df = new SimpleDateFormat("yyyyMMdd"); 29 | 30 | // 将指定的IP计入UV 31 | public void recordUV(String ip) { 32 | String redisKey = RedisKeyUtil.getUVKey(df.format(new Date())); 33 | redisTemplate.opsForHyperLogLog().add(redisKey, ip); 34 | } 35 | 36 | // 统计指定日期范围内的UV 37 | public long calculateUV(Date start, Date end) { 38 | if (start == null || end == null) { 39 | throw new IllegalArgumentException("参数不能为空!"); 40 | } 41 | 42 | // 整理该日期范围内的key 43 | List keyList = new ArrayList<>(); 44 | Calendar calendar = Calendar.getInstance(); 45 | calendar.setTime(start); 46 | while (!calendar.getTime().after(end)) { 47 | String key = RedisKeyUtil.getUVKey(df.format(calendar.getTime())); 48 | keyList.add(key); 49 | calendar.add(Calendar.DATE, 1); 50 | } 51 | 52 | // 合并这些数据 53 | String redisKey = RedisKeyUtil.getUVKey(df.format(start), df.format(end)); 54 | redisTemplate.opsForHyperLogLog().union(redisKey, keyList.toArray()); 55 | 56 | // 返回统计的结果 57 | return redisTemplate.opsForHyperLogLog().size(redisKey); 58 | } 59 | 60 | // 将指定用户计入DAU 61 | public void recordDAU(int userId) { 62 | String redisKey = RedisKeyUtil.getDAUKey(df.format(new Date())); 63 | redisTemplate.opsForValue().setBit(redisKey, userId, true); 64 | } 65 | 66 | // 统计指定日期范围内的DAU 67 | public long calculateDAU(Date start, Date end) { 68 | if (start == null || end == null) { 69 | throw new IllegalArgumentException("参数不能为空!"); 70 | } 71 | 72 | // 整理该日期范围内的key 73 | List keyList = new ArrayList<>(); 74 | Calendar calendar = Calendar.getInstance(); 75 | calendar.setTime(start); 76 | while (!calendar.getTime().after(end)) { 77 | String key = RedisKeyUtil.getDAUKey(df.format(calendar.getTime())); 78 | keyList.add(key.getBytes()); 79 | calendar.add(Calendar.DATE, 1); 80 | } 81 | 82 | // 进行OR运算 83 | return (long) redisTemplate.execute(new RedisCallback() { 84 | @Override 85 | public Object doInRedis(RedisConnection connection) throws DataAccessException { 86 | String redisKey = RedisKeyUtil.getDAUKey(df.format(start), df.format(end)); 87 | connection.bitOp(RedisStringCommands.BitOperation.OR, 88 | redisKey.getBytes(), keyList.toArray(new byte[0][0])); 89 | return connection.bitCount(redisKey.getBytes()); 90 | } 91 | }); 92 | } 93 | 94 | } 95 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/ICommentService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import fun.linyuhong.myCommunity.entity.Comment; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author linyuhong 9 | * @date 2019/9/3 10 | */ 11 | public interface ICommentService { 12 | 13 | List selectCommentByEntity(int entityType, int entityId, int offset, int limit); 14 | 15 | int findCommentCount(int entityType, int entityId); 16 | 17 | int addComment(Comment comment); 18 | 19 | Comment findCommentById(int id); 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/IDiscussPostService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import com.github.pagehelper.PageInfo; 4 | import fun.linyuhong.myCommunity.entity.DiscussPost; 5 | 6 | import javax.validation.constraints.Size; 7 | import java.util.List; 8 | import java.util.Map; 9 | 10 | public interface IDiscussPostService { 11 | 12 | List selectDiscussPosts(int userId, int orderMode, int offset, int limit); 13 | 14 | int findDiscussPostRows(int userId); 15 | 16 | int addDiscussPost(DiscussPost post); 17 | 18 | DiscussPost getDiscussPost(Integer discussPostId); 19 | 20 | DiscussPost findDiscussPostById(int id); 21 | 22 | int updateType(int id, int type); 23 | 24 | int updateStatus(int id, int status); 25 | 26 | int updateScore(int id, double score); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/IElasticsearchService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import fun.linyuhong.myCommunity.entity.DiscussPost; 4 | import org.springframework.data.domain.Page; 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/8 9 | */ 10 | public interface IElasticsearchService { 11 | 12 | void saveDiscussPost(DiscussPost post); 13 | 14 | void deleteDiscussPost(int id); 15 | 16 | Page searchDiscussPost(String keyword, int current, int limit); 17 | 18 | } 19 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/IFollowService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import fun.linyuhong.myCommunity.vo.UserVo; 4 | 5 | import java.util.List; 6 | import java.util.Map; 7 | 8 | /** 9 | * @author linyuhong 10 | * @date 2019/9/5 11 | */ 12 | public interface IFollowService { 13 | 14 | void follow(int userId, int entityType, int entityId); 15 | 16 | void unfollow(int userId, int entityType, int entityId); 17 | 18 | Long findFolloweeCount(int userId, int entityType); 19 | 20 | Long findFollowerCount(int entityType, int entityId); 21 | 22 | List> findFollowee(int userId, int entityType, int offset, int limit); 23 | 24 | List> findFollowers(int entityType, int userId, int offset, int limit); 25 | 26 | boolean hasFollowed(int userId, int entityType, int entityId); 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/ILikeService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | /** 4 | * @author linyuhong 5 | * @date 2019/9/5 6 | */ 7 | public interface ILikeService { 8 | 9 | void like(int userId, int entityType, int entityId, int entityUserId); 10 | 11 | Long findEntityLikeCount(int entityType, int entityId); 12 | 13 | int findEntityLikeStatus(int userId, int entityType, int entityId); 14 | 15 | int findUserLikeCount(int userId); 16 | 17 | } 18 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/IMessageService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import fun.linyuhong.myCommunity.entity.Message; 4 | 5 | import java.util.List; 6 | 7 | /** 8 | * @author linyuhong 9 | * @date 2019/9/4 10 | */ 11 | public interface IMessageService { 12 | 13 | int findConversationCount(int id); 14 | 15 | List findConversations(int userId, int offset, int limit); 16 | 17 | int findLetterCount(String conversationId); 18 | 19 | int findLetterUnreadCount(int userId, String conversationId); 20 | 21 | List findLetters(String conversationId, int offset, int limit); 22 | 23 | int readMessage(List ids); 24 | 25 | int addMessage(Message message); 26 | 27 | Message findLatestNotice(int userId, String topic); 28 | 29 | int findNoticeCount(int userId, String topic); 30 | 31 | int findNoticeUnreadCount(int userId, String topic); 32 | 33 | List findNotices(int userId, String topic, int offset, int limit); 34 | 35 | } 36 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/IUserService.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service; 2 | 3 | import fun.linyuhong.myCommunity.entity.LoginTicket; 4 | import fun.linyuhong.myCommunity.entity.Message; 5 | import fun.linyuhong.myCommunity.entity.User; 6 | import fun.linyuhong.myCommunity.vo.UserVo; 7 | import org.springframework.security.core.GrantedAuthority; 8 | 9 | import java.util.Collection; 10 | import java.util.Map; 11 | 12 | /** 13 | * @author linyuhong 14 | * @date 2019/9/1 15 | */ 16 | public interface IUserService { 17 | 18 | Map login(String username, String password); 19 | 20 | LoginTicket findLoginTicket(String key); 21 | 22 | UserVo findUserById(int id); 23 | 24 | void logout(String ticket); 25 | 26 | Map register(User user); 27 | 28 | int activation(int userId, String code); 29 | 30 | UserVo findUserByName(String username); 31 | 32 | int updateHeader(int userId, String url); 33 | 34 | Collection getAuthorities(int userId); 35 | 36 | 37 | } 38 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/Impl/CommentServiceImpl.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service.Impl; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.dao.CommentMapper; 5 | import fun.linyuhong.myCommunity.dao.DiscussPostMapper; 6 | import fun.linyuhong.myCommunity.entity.Comment; 7 | import fun.linyuhong.myCommunity.service.ICommentService; 8 | import fun.linyuhong.myCommunity.util.SensitiveFilter; 9 | import fun.linyuhong.myCommunity.util.XORUtil; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.stereotype.Service; 12 | import org.springframework.transaction.annotation.Isolation; 13 | import org.springframework.transaction.annotation.Propagation; 14 | import org.springframework.transaction.annotation.Transactional; 15 | import org.springframework.web.util.HtmlUtils; 16 | 17 | import java.util.Date; 18 | import java.util.List; 19 | 20 | /** 21 | * @author linyuhong 22 | * @date 2019/9/3 23 | */ 24 | @Service 25 | public class CommentServiceImpl implements ICommentService { 26 | 27 | @Autowired 28 | private CommentMapper commentMapper; 29 | 30 | @Autowired 31 | private SensitiveFilter sensitiveFilter; 32 | 33 | @Autowired 34 | private DiscussPostMapper discussPostMapper; 35 | 36 | @Override 37 | public List selectCommentByEntity(int entityType, int entityId, int offset, int limit) { 38 | // 帖子的评论 39 | List commentList = commentMapper.selectCommentsByEntity(entityType, entityId, offset, limit); 40 | return commentList; 41 | } 42 | 43 | @Override 44 | public int findCommentCount(int entityType, int entityId) { 45 | return commentMapper.selectCountByEntity(entityType, entityId); 46 | } 47 | 48 | @Override 49 | @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED) 50 | public int addComment(Comment comment) { 51 | if (comment == null) { 52 | throw new IllegalArgumentException("参数不能为空!"); 53 | } 54 | 55 | comment.setContent(HtmlUtils.htmlEscape(comment.getContent())); 56 | comment.setContent(sensitiveFilter.filter(comment.getContent())); 57 | comment.setStatus(0); 58 | comment.setCreateTime(new Date()); 59 | int rows = commentMapper.insertComment(comment); 60 | 61 | // 更新帖子的评论数 62 | if (comment.getEntityType() == Const.entityType.ENTITY_TYPE_POST) { 63 | int count = commentMapper.selectCountByEntity(comment.getEntityType(), comment.getEntityId()); 64 | discussPostMapper.updateCommentCount(comment.getEntityId(), count); 65 | } 66 | 67 | return rows; 68 | } 69 | 70 | @Override 71 | public Comment findCommentById(int id) { 72 | return commentMapper.selectCommentById(id); 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/Impl/LikeServiceImpl.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service.Impl; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.service.ILikeService; 5 | import fun.linyuhong.myCommunity.util.RedisKeyUtil; 6 | import org.aspectj.lang.annotation.Aspect; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.dao.DataAccessException; 9 | import org.springframework.data.redis.core.RedisOperations; 10 | import org.springframework.data.redis.core.RedisTemplate; 11 | import org.springframework.data.redis.core.SessionCallback; 12 | import org.springframework.stereotype.Service; 13 | 14 | /** 15 | * @author linyuhong 16 | * @date 2019/9/5 17 | */ 18 | @Service 19 | public class LikeServiceImpl implements ILikeService { 20 | 21 | @Autowired 22 | private RedisTemplate redisTemplate; 23 | 24 | @Override 25 | public void like(int userId, int entityType, int entityId, int entityUserId) { 26 | 27 | redisTemplate.execute(new SessionCallback() { 28 | @Override 29 | public Object execute(RedisOperations redisOperations) throws DataAccessException { 30 | 31 | String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); 32 | String userLikeKey = RedisKeyUtil.getUserLikeKey(entityUserId); 33 | // 当前用户是否点过赞 34 | Boolean isMember = redisTemplate.opsForSet().isMember(entityLikeKey, userId); 35 | 36 | // 开启事务 37 | redisOperations.multi(); 38 | 39 | if (isMember) { 40 | redisTemplate.opsForSet().remove(entityLikeKey, userId); 41 | redisTemplate.opsForValue().increment(userLikeKey, -1); 42 | } else { 43 | redisTemplate.opsForSet().add(entityLikeKey, userId); 44 | redisTemplate.opsForValue().increment(userLikeKey, 1); 45 | } 46 | return redisOperations.exec(); 47 | } 48 | }); 49 | 50 | } 51 | 52 | // 查询某实体点赞的数量 53 | @Override 54 | public Long findEntityLikeCount(int entityType, int entityId) { 55 | String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); 56 | return redisTemplate.opsForSet().size(entityLikeKey); 57 | } 58 | 59 | // 查询某人对某实体的点赞状态 60 | // 返回 int 而不是 boolean 是为了以后有机会做扩展,boolean只能表示两种状态,例如已赞或未赞,如果要添加 踩 的功能则不能 61 | @Override 62 | public int findEntityLikeStatus(int userId, int entityType, int entityId) { 63 | String entityLikeKey = RedisKeyUtil.getEntityLikeKey(entityType, entityId); 64 | return redisTemplate.opsForSet().isMember(entityLikeKey, userId) ? 1 : 0; 65 | } 66 | 67 | // 查询某个用户获得的赞 68 | @Override 69 | public int findUserLikeCount(int userId) { 70 | String userLikeKey = RedisKeyUtil.getUserLikeKey(userId); 71 | Integer count = (Integer) redisTemplate.opsForValue().get(userLikeKey); 72 | return count == null ? 0 : count.intValue(); 73 | } 74 | } 75 | 76 | 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/service/Impl/MessageServiceImpl.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.service.Impl; 2 | 3 | import fun.linyuhong.myCommunity.common.Const; 4 | import fun.linyuhong.myCommunity.dao.MessageMapper; 5 | import fun.linyuhong.myCommunity.entity.Message; 6 | import fun.linyuhong.myCommunity.service.IMessageService; 7 | import org.springframework.beans.factory.annotation.Autowired; 8 | import org.springframework.stereotype.Service; 9 | 10 | import java.util.List; 11 | 12 | /** 13 | * @author linyuhong 14 | * @date 2019/9/4 15 | */ 16 | @Service 17 | public class MessageServiceImpl implements IMessageService { 18 | 19 | @Autowired 20 | private MessageMapper messageMapper; 21 | 22 | @Override 23 | public int findConversationCount(int id) { 24 | return messageMapper.selectConversationCount(id); 25 | } 26 | 27 | @Override 28 | public List findConversations(int userId, int offset, int limit) { 29 | return messageMapper.selectConversations(userId, offset, limit); 30 | } 31 | 32 | @Override 33 | public int findLetterCount(String conversationId) { 34 | return messageMapper.selectLetterCount(conversationId); 35 | } 36 | 37 | @Override 38 | public int findLetterUnreadCount(int userId, String conversationId) { 39 | return messageMapper.selectLetterUnreadCount(userId, conversationId); 40 | } 41 | 42 | @Override 43 | public List findLetters(String conversationId, int offset, int limit) { 44 | return messageMapper.selectLetters(conversationId, offset, limit); 45 | } 46 | 47 | @Override 48 | public int readMessage(List ids) { 49 | return messageMapper.updateStatus(ids, Const.status.READ); 50 | } 51 | 52 | @Override 53 | public int addMessage(Message message) { 54 | return messageMapper.insertMessage(message); 55 | } 56 | 57 | @Override 58 | public Message findLatestNotice(int userId, String topic) { 59 | return messageMapper.selectLatestNotice(userId, topic); 60 | } 61 | 62 | @Override 63 | public int findNoticeCount(int userId, String topic) { 64 | return messageMapper.selectNoticeCount(userId, topic); 65 | } 66 | 67 | @Override 68 | public int findNoticeUnreadCount(int userId, String topic) { 69 | return messageMapper.selectNoticeUnreadCount(userId, topic); 70 | } 71 | 72 | @Override 73 | public List findNotices(int userId, String topic, int offset, int limit) { 74 | return messageMapper.selectNotices(userId, topic, offset, limit); 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/CookieUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | 5 | import javax.servlet.http.Cookie; 6 | import javax.servlet.http.HttpServletRequest; 7 | import javax.servlet.http.HttpServletResponse; 8 | 9 | /** 10 | * @author linyuhong 11 | * @date 2019/9/1 12 | */ 13 | public class CookieUtil { 14 | 15 | public static String getValue(HttpServletRequest request, String name) { 16 | if (request == null || StringUtils.isBlank(name)) { 17 | throw new IllegalArgumentException("参数为空"); 18 | } 19 | 20 | Cookie[] cookies = request.getCookies(); 21 | if (cookies != null) { 22 | for (Cookie cookie : cookies) { 23 | if (cookie.getName().equals(name)) { 24 | return cookie.getValue(); 25 | } 26 | } 27 | } 28 | return null; 29 | } 30 | 31 | } 32 | 33 | 34 | 35 | 36 | 37 | 38 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/EmailUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import java.util.regex.Matcher; 4 | import java.util.regex.Pattern; 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/2 9 | */ 10 | public class EmailUtil { 11 | 12 | public static boolean isEmail(String string) { 13 | if (string == null) 14 | return false; 15 | // String regEx1 = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"; 16 | String regEx1 = "\\w[-\\w.+]*@([A-Za-z0-9][-A-Za-z0-9]+\\.)+[A-Za-z]{2,14}"; 17 | Pattern p; 18 | Matcher m; 19 | p = Pattern.compile(regEx1); 20 | m = p.matcher(string); 21 | if (m.matches()) 22 | return true; 23 | else 24 | return false; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/GetGenerateUUID.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import java.util.UUID; 4 | 5 | /** 6 | * @author linyuhong 7 | * @date 2019/9/1 8 | */ 9 | public class GetGenerateUUID { 10 | 11 | // 生成随机字符串 12 | public static String generateUUID() { 13 | return UUID.randomUUID().toString().replaceAll("-", ""); 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/HostHolder.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import fun.linyuhong.myCommunity.entity.User; 4 | import fun.linyuhong.myCommunity.vo.UserVo; 5 | import org.springframework.stereotype.Component; 6 | 7 | /** 8 | * @author linyuhong 9 | * @date 2019/9/1 10 | */ 11 | @Component 12 | public class HostHolder { 13 | 14 | private ThreadLocal users = new ThreadLocal<>(); 15 | 16 | public void setUser(UserVo user) { 17 | users.set(user); 18 | } 19 | 20 | public UserVo getUser() { 21 | return users.get(); 22 | } 23 | 24 | public void clear() { 25 | users.remove(); 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/HttpUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import java.net.HttpURLConnection; 4 | import java.net.URL; 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/2 9 | */ 10 | public class HttpUtil { 11 | public static String sendGet(String url) { 12 | String resultUrl = ""; 13 | try { 14 | 15 | //发送get请求 16 | URL serverUrl = new URL(url); 17 | HttpURLConnection conn = (HttpURLConnection) serverUrl.openConnection(); 18 | conn.setRequestMethod("GET"); 19 | //必须设置false,否则会自动redirect到重定向后的地址 20 | conn.setInstanceFollowRedirects(false); 21 | conn.addRequestProperty("Accept-Charset", "UTF-8;"); 22 | conn.addRequestProperty("User-Agent", "Mozilla/5.0 (Windows; U; Windows NT 5.1; zh-CN; rv:1.9.2.8) Firefox/3.6.8"); 23 | conn.addRequestProperty("Referer", "http://matols.com/"); 24 | conn.connect(); 25 | 26 | //判定是否会进行302重定向 27 | if (conn.getResponseCode() == 302) { 28 | //如果会重定向,保存302重定向地址,以及Cookies,然后重新发送请求(模拟请求) 29 | resultUrl = conn.getHeaderField("Location"); 30 | 31 | return resultUrl; 32 | } 33 | 34 | } catch (Exception e) { 35 | e.printStackTrace(); 36 | } 37 | // 出现网络状况,统一获取同一张图片 38 | resultUrl = "http://cdn.u2.huluxia.com/g3/M00/27/CD/wKgBOVwJgW-ASQo9AACvmQ_XQ_k733.jpg"; 39 | return resultUrl; 40 | } 41 | 42 | } -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/JSONUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import com.alibaba.fastjson.JSONObject; 4 | 5 | import java.util.Map; 6 | 7 | public class JSONUtil { 8 | 9 | public static String getJSONString(int code, String msg, Map map) { 10 | JSONObject json = new JSONObject(); 11 | json.put("code", code); 12 | json.put("msg", msg); 13 | if (map != null) { 14 | for (String key : map.keySet()) { 15 | json.put(key, map.get(key)); 16 | } 17 | } 18 | return json.toJSONString(); 19 | } 20 | 21 | public static String getJSONString(int code, String msg) { 22 | return getJSONString(code, msg, null); 23 | } 24 | 25 | public static String getJSONString(int code) { 26 | return getJSONString(code, "", null); 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/MD5Encoder.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | import org.apache.commons.lang3.StringUtils; 4 | import org.springframework.util.DigestUtils; 5 | 6 | import java.util.UUID; 7 | 8 | /** 9 | * @author linyuhong 10 | * @date 2019/9/1 11 | */ 12 | public class MD5Encoder { 13 | 14 | // 生成随机字符串 15 | public static String generateUUID() { 16 | return UUID.randomUUID().toString().replaceAll("-", ""); 17 | } 18 | 19 | // MD5加密 20 | public static String md5(String key) { 21 | if (StringUtils.isBlank(key)){ // " " or ---> true 22 | return null; 23 | } 24 | return DigestUtils.md5DigestAsHex(key.getBytes()); 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/MailClient.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | 4 | import org.slf4j.Logger; 5 | import org.slf4j.LoggerFactory; 6 | import org.springframework.beans.factory.annotation.Autowired; 7 | import org.springframework.beans.factory.annotation.Value; 8 | import org.springframework.mail.javamail.JavaMailSender; 9 | import org.springframework.mail.javamail.MimeMessageHelper; 10 | import org.springframework.stereotype.Controller; 11 | 12 | import javax.mail.MessagingException; 13 | import javax.mail.internet.MimeMessage; 14 | 15 | @Controller 16 | public class MailClient { 17 | 18 | private static final Logger logger = LoggerFactory.getLogger(MailClient.class); 19 | 20 | 21 | @Autowired 22 | private JavaMailSender javaMailSender; 23 | 24 | @Value("${spring.mail.username}") 25 | private String from; // 发送者 26 | 27 | /** 28 | * 29 | * @param to 收件者 30 | * @param subject 发送主题 31 | * @param content 发送内容 32 | */ 33 | public void sendMail(String to, String subject, String content){ 34 | try{ 35 | MimeMessage mimeMessage = javaMailSender.createMimeMessage(); 36 | // MimeMessageHelper 是帮忙构建 MimeMessage 对象, 因为JavaMailSender的发送需要 MimeMessage 对象 37 | MimeMessageHelper messageHelper = new MimeMessageHelper(mimeMessage); 38 | messageHelper.setFrom(from); 39 | messageHelper.setTo(to); 40 | messageHelper.setSubject(subject); 41 | messageHelper.setText(content, true); // 不加true是以纯文本形式发送 42 | javaMailSender.send(messageHelper.getMimeMessage()); 43 | }catch (MessagingException e){ 44 | logger.error("邮件发送失败: " + e.getMessage()); 45 | } 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/RedisKeyUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | /** 4 | * @author linyuhong 5 | * @date 2019/9/1 6 | */ 7 | public class RedisKeyUtil { 8 | 9 | private static final String SPLIT = ":"; 10 | private static final String PREFIX_TICKET = "ticket"; 11 | private static final String PREFIX_KAPTCHA = "kaptcha"; 12 | private static final String PREFIX_ENTITY_LIKE = "like:entity"; 13 | private static final String PREFIX_USER_LIKE = "like:user"; 14 | private static final String PREFIX_FOLLOWEE = "followee"; 15 | private static final String PREFIX_FOLLOWER = "follower"; 16 | private static final String PREFIX_USER = "user"; 17 | private static String PREFIX_EVENTQUEUE = "EVENT_QUEUE"; 18 | private static final String PREFIX_UV = "uv"; 19 | private static final String PREFIX_DAU = "dau"; 20 | private static final String PREFIX_POST = "post"; 21 | 22 | 23 | // 帖子分数 24 | public static String getPostScoreKey() { 25 | return PREFIX_POST + SPLIT + "score"; 26 | } 27 | 28 | // 事件主题 29 | public static String getEventqueueKey() { 30 | return PREFIX_EVENTQUEUE; 31 | } 32 | 33 | // 用户 34 | public static String getUserKey(int userId) { 35 | return PREFIX_USER + SPLIT + userId; 36 | } 37 | 38 | // 某个用户关注的实体 39 | // followee:userId:entityType -> zset(entityId,now) zset()以关注时间进行排序 40 | public static String getFolloweeKey(int userId, int entityType) { 41 | return PREFIX_FOLLOWEE + SPLIT + userId + SPLIT + entityType; 42 | } 43 | 44 | // 某个实体拥有的粉丝 45 | // follower:entityType:entityId -> zset(userId,now) 46 | public static String getFollowerKey(int entityType, int entityId) { 47 | return PREFIX_FOLLOWER + SPLIT + entityType + SPLIT + entityId; 48 | } 49 | 50 | /** 51 | * 生成登录凭证 52 | * @param ticket UUID 53 | * @return ticket:UUID 54 | */ 55 | public static String getTicketKey(String ticket) { 56 | return PREFIX_TICKET + SPLIT + ticket; 57 | } 58 | 59 | // 登录验证码 60 | public static String getKaptchaKey(String owner) { 61 | return PREFIX_KAPTCHA + SPLIT + owner; 62 | } 63 | 64 | // 某个实体的赞 65 | // like:entity:entityType:entityId : userId 66 | // entityType 分为文章的赞和评论的赞两类 67 | public static String getEntityLikeKey(int entityType, int entityId) { 68 | return PREFIX_ENTITY_LIKE + SPLIT + entityType + SPLIT + entityId; 69 | } 70 | 71 | // 用户收到的赞 72 | // like:user:userId ---> int 73 | public static String getUserLikeKey(int userId) { 74 | return PREFIX_USER_LIKE + SPLIT + userId; 75 | } 76 | 77 | 78 | 79 | // 单日UV 80 | public static String getUVKey(String date) { 81 | return PREFIX_UV + SPLIT + date; 82 | } 83 | 84 | // 区间UV 85 | public static String getUVKey(String startDate, String endDate) { 86 | return PREFIX_UV + SPLIT + startDate + SPLIT + endDate; 87 | } 88 | 89 | // 单日活跃用户 90 | public static String getDAUKey(String date) { 91 | return PREFIX_DAU + SPLIT + date; 92 | } 93 | 94 | // 区间活跃用户 95 | public static String getDAUKey(String startDate, String endDate) { 96 | return PREFIX_DAU + SPLIT + startDate + SPLIT + endDate; 97 | } 98 | 99 | } 100 | 101 | 102 | 103 | 104 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/util/XORUtil.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.util; 2 | 3 | 4 | import org.springframework.context.annotation.Configuration; 5 | 6 | 7 | public class XORUtil { 8 | 9 | /** 10 | * 异或算法加密/解密 11 | * 12 | * @param id 数据(密文/明文) 13 | * @param key 密钥 14 | * @return 返回解密/加密后的数据 15 | */ 16 | public static int encryptId(int id, byte[] key) { 17 | byte[] ids = leIntToByteArray(id); 18 | if (ids == null || ids.length == 0 || key == null || key.length == 0) { 19 | return id; 20 | } 21 | 22 | byte[] result = new byte[ids.length]; 23 | 24 | // 使用密钥字节数组循环加密或解密 25 | for (int i = 0; i < ids.length; i++) { 26 | // 数据与密钥异或, 再与循环变量的低8位异或(增加复杂度) 27 | result[i] = (byte) (ids[i] ^ key[i % key.length] ^ (i & 0xFF)); 28 | } 29 | 30 | return byteArrayToLeInt(result); 31 | } 32 | 33 | /** 34 | * 将 int 转化为 byte[] 35 | * @param value 36 | * @return 37 | */ 38 | private static byte[] leIntToByteArray(int value) { 39 | byte[] encodedValue = new byte[Integer.SIZE / Byte.SIZE]; 40 | encodedValue[3] = (byte) (value >> Byte.SIZE * 3); 41 | encodedValue[2] = (byte) (value >> Byte.SIZE * 2); 42 | encodedValue[1] = (byte) (value >> Byte.SIZE); 43 | encodedValue[0] = (byte) value; 44 | return encodedValue; 45 | } 46 | 47 | private static int byteArrayToLeInt(byte[] encodedValue) { 48 | int value = (encodedValue[3] << (Byte.SIZE * 3)); 49 | value |= (encodedValue[2] & 0xFF) << (Byte.SIZE * 2); 50 | value |= (encodedValue[1] & 0xFF) << (Byte.SIZE * 1); 51 | value |= (encodedValue[0] & 0xFF); 52 | return value; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /src/main/java/fun/linyuhong/myCommunity/vo/UserVo.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity.vo; 2 | 3 | import java.util.Date; 4 | 5 | public class UserVo { 6 | 7 | private Integer id; 8 | 9 | private String username; 10 | 11 | private String email; 12 | 13 | private String headerUrl; 14 | 15 | private Date createTime; 16 | 17 | private Integer type; // 0-普通用户; 1-管理员; 18 | 19 | public Integer getId() { 20 | return id; 21 | } 22 | 23 | public void setId(Integer id) { 24 | this.id = id; 25 | } 26 | 27 | public String getUsername() { 28 | return username; 29 | } 30 | 31 | public void setUsername(String username) { 32 | this.username = username; 33 | } 34 | 35 | public String getEmail() { 36 | return email; 37 | } 38 | 39 | public void setEmail(String email) { 40 | this.email = email; 41 | } 42 | 43 | public Integer getType() { 44 | return type; 45 | } 46 | 47 | public void setType(Integer type) { 48 | this.type = type; 49 | } 50 | 51 | public String getHeaderUrl() { 52 | return headerUrl; 53 | } 54 | 55 | public void setHeaderUrl(String headerUrl) { 56 | this.headerUrl = headerUrl; 57 | } 58 | 59 | public Date getCreateTime() { 60 | return createTime; 61 | } 62 | 63 | public void setCreateTime(Date createTime) { 64 | this.createTime = createTime; 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /src/main/resources/application-develop.yml: -------------------------------------------------------------------------------- 1 | # ServerProperties 2 | server: 3 | port: 8080 4 | servlet: 5 | # url访问路径名 6 | context-path: 7 | 8 | 9 | spring: 10 | # DataSourceProperties 11 | datasource: 12 | driver-class-name=com: com.mysql.cj.jdbc.Driver 13 | url: jdbc:mysql://localhost:3306/myCommunity(数据库名)?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong 14 | username: 用户名 15 | password: 密码 16 | # 默认数据源 17 | type: com.zaxxer.hikari.HikariDataSource 18 | hikari: 19 | maximum-pool-size: 15 20 | minimum-idle: 5 21 | idle-timeout: 2000 22 | 23 | # ThymeleafProperties 24 | thymeleaf: 25 | cache: false 26 | 27 | # MailProperties 28 | mail: 29 | host: smtp.qq.com 30 | port: 465 31 | username: xxxxxx@qq.com 32 | password: xxxxxx 33 | protocol: smtps 34 | properties.mail.smtp.ssl.enable: true 35 | 36 | # RedisProperties 37 | redis: 38 | database: 2 39 | host: 127.0.0.1 40 | port: 6379 41 | timeout: 50000 42 | # commandTimeout: 0 43 | # jedis: 44 | # pool: 45 | # max-idle: 10 46 | # min-idle: 0 47 | # max-active: 10 48 | # max-wait: -1 49 | lettuce: 50 | pool: 51 | max-wait: -1 # 连接池最大阻塞等待时间 52 | max-idle: 10 53 | min-idle: 0 54 | max-active: 10 55 | 56 | # ElasticsearchProperties 57 | data: 58 | elasticsearch: 59 | cluster-name: 60 | cluster-nodes: 127.0.0.1:9300 61 | 62 | 63 | mybatis: 64 | mapper-locations: 65 | - classpath:mapper/*.xml 66 | type-aliases-package: fun.linyuhong.myCommunity.entity 67 | configuration: 68 | useGeneratedKeys: true 69 | mapUnderscoreToCamelCase: true 70 | 71 | 72 | 73 | # 配置发送邮件时的域名 74 | domain: http://127.0.0.1:8080 75 | # 上传头像的域名 76 | upload: g:/work/data/upload(存放头像的本地地址) 77 | 78 | 79 | # qiniu 80 | qiniu: 81 | key: 82 | access: 83 | # 文件加密秘钥 84 | secret: 85 | 86 | bucket: 87 | header: 88 | # 空间名字 89 | name: mycommunity-header(自定义) 90 | # 空间域名 有效期30天 91 | url: http://pxixdvvsz.bkt.clouddn.com 92 | 93 | 94 | # caffeine 95 | # posts 是自定义的key 96 | caffeine: 97 | posts: 98 | # 缓存15条数据 99 | max-size: 15 100 | # 有效期 180 s = 3 分钟 101 | expire-seconds: 180 102 | 103 | -------------------------------------------------------------------------------- /src/main/resources/application-produce.yml: -------------------------------------------------------------------------------- 1 | # ServerProperties 2 | server: 3 | port: 8080 4 | servlet: 5 | # url访问路径名 6 | context-path: 7 | 8 | 9 | spring: 10 | # DataSourceProperties 11 | datasource: 12 | driver-class-name=com: com.mysql.cj.jdbc.Driver 13 | url: jdbc:mysql://localhost:3306/myCommunity(数据库名)?characterEncoding=utf-8&useSSL=false&serverTimezone=Hongkong 14 | username: 用户名 15 | password: 密码 16 | # 默认数据源 17 | type: com.zaxxer.hikari.HikariDataSource 18 | hikari: 19 | maximum-pool-size: 15 20 | minimum-idle: 5 21 | idle-timeout: 2000 22 | 23 | # ThymeleafProperties 24 | thymeleaf: 25 | cache: true 26 | 27 | # MailProperties 28 | mail: 29 | host: smtp.qq.com 30 | port: 465 31 | username: xxxxxx@qq.com 32 | password: xxxxxx 33 | protocol: smtps 34 | properties.mail.smtp.ssl.enable: true 35 | 36 | # RedisProperties 37 | redis: 38 | database: 1 39 | host: 127.0.0.1 40 | port: 6379 41 | timeout: 50000 42 | 43 | lettuce: 44 | pool: 45 | max-wait: -1 # 连接池最大阻塞等待时间 46 | max-idle: 10 47 | min-idle: 0 48 | max-active: 10 49 | 50 | # ElasticsearchProperties 51 | # data: 52 | # elasticsearch: 53 | # cluster-name: 54 | # cluster-nodes: 127.0.0.1:9300 55 | 56 | 57 | mybatis: 58 | mapper-locations: 59 | - classpath:mapper/*.xml 60 | type-aliases-package: fun.linyuhong.myCommunity.entity 61 | configuration: 62 | useGeneratedKeys: true 63 | mapUnderscoreToCamelCase: true 64 | 65 | 66 | # 配置发送邮件时的域名 67 | domain: 服务器IP地址 68 | # 上传头像的域名 69 | upload: /tmp/uploads 70 | 71 | 72 | # qiniu 73 | qiniu: 74 | key: 75 | access: 76 | # 文件加密秘钥 77 | secret: 78 | 79 | bucket: 80 | header: 81 | # 空间名字 82 | name: mycommunity-header(自定义) 83 | # 空间域名 有效期30天 84 | url: http://pxixdvvsz.bkt.clouddn.com 85 | 86 | 87 | # caffeine 88 | # posts 是自定义的key 89 | caffeine: 90 | posts: 91 | # 缓存15条数据 92 | max-size: 15 93 | # 有效期 180 s = 3 分钟 94 | expire-seconds: 180 95 | 96 | -------------------------------------------------------------------------------- /src/main/resources/application.yml: -------------------------------------------------------------------------------- 1 | # profile 2 | spring: 3 | profiles: 4 | active: develop 5 | 6 | # logback 7 | logging: 8 | config: 9 | classpath: logback-spring-${spring.profiles.active}.xml 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/datasource.properties: -------------------------------------------------------------------------------- 1 | #db.driverLocation=C:/Users/lin/Desktop/mysql-connector-java-5.1.6-bin.jar 2 | #db.driverClassName=com.mysql.jdbc.Driver 3 | #db.url=jdbc:mysql://127.0.0.1:3306/myCommunity?characterEncoding=utf-8 4 | #db.username=root 5 | #db.password=root 6 | 7 | #db.initialSize = 20 8 | #db.maxActive = 50 9 | #db.maxIdle = 20 10 | #db.minIdle = 10 11 | #db.maxWait = 10 12 | #db.defaultAutoCommit = true 13 | #db.minEvictableIdleTimeMillis = 3600000 14 | -------------------------------------------------------------------------------- /src/main/resources/generatorConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 |
73 |
74 |
75 |
76 | 77 | 78 |
79 |
-------------------------------------------------------------------------------- /src/main/resources/logback-spring-develop.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | myCommunity 4 | 5 | 6 | 7 | 8 | 9 | ${LOG_PATH}/${APPDIR}/log_error.log 10 | 11 | ${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log 12 | 13 | 5MB 14 | 15 | 30 16 | 17 | true 18 | 19 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 20 | utf-8 21 | 22 | 23 | error 24 | ACCEPT 25 | DENY 26 | 27 | 28 | 29 | 30 | 31 | ${LOG_PATH}/${APPDIR}/log_warn.log 32 | 33 | ${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log 34 | 35 | 5MB 36 | 37 | 30 38 | 39 | true 40 | 41 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 42 | utf-8 43 | 44 | 45 | warn 46 | ACCEPT 47 | DENY 48 | 49 | 50 | 51 | 52 | 53 | ${LOG_PATH}/${APPDIR}/log_info.log 54 | 55 | ${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log 56 | 57 | 5MB 58 | 59 | 30 60 | 61 | true 62 | 63 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 64 | utf-8 65 | 66 | 67 | info 68 | ACCEPT 69 | DENY 70 | 71 | 72 | 73 | 74 | 75 | 76 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 77 | utf-8 78 | 79 | 80 | debug 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring-produce.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | myCommunity 4 | 5 | 6 | 7 | 8 | 9 | ${LOG_PATH}/${APPDIR}/log_error.log 10 | 11 | ${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log 12 | 13 | 5MB 14 | 15 | 30 16 | 17 | true 18 | 19 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 20 | utf-8 21 | 22 | 23 | error 24 | ACCEPT 25 | DENY 26 | 27 | 28 | 29 | 30 | 31 | ${LOG_PATH}/${APPDIR}/log_warn.log 32 | 33 | ${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log 34 | 35 | 5MB 36 | 37 | 30 38 | 39 | true 40 | 41 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 42 | utf-8 43 | 44 | 45 | warn 46 | ACCEPT 47 | DENY 48 | 49 | 50 | 51 | 52 | 53 | ${LOG_PATH}/${APPDIR}/log_info.log 54 | 55 | ${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log 56 | 57 | 5MB 58 | 59 | 30 60 | 61 | true 62 | 63 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 64 | utf-8 65 | 66 | 67 | info 68 | ACCEPT 69 | DENY 70 | 71 | 72 | 73 | 74 | 75 | 76 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 77 | utf-8 78 | 79 | 80 | debug 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/resources/logback-spring.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | myCommunity 4 | 5 | 6 | 7 | 8 | 9 | ${LOG_PATH}/${APPDIR}/log_error.log 10 | 11 | ${LOG_PATH}/${APPDIR}/error/log-error-%d{yyyy-MM-dd}.%i.log 12 | 13 | 5MB 14 | 15 | 30 16 | 17 | true 18 | 19 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 20 | utf-8 21 | 22 | 23 | error 24 | ACCEPT 25 | DENY 26 | 27 | 28 | 29 | 30 | 31 | ${LOG_PATH}/${APPDIR}/log_warn.log 32 | 33 | ${LOG_PATH}/${APPDIR}/warn/log-warn-%d{yyyy-MM-dd}.%i.log 34 | 35 | 5MB 36 | 37 | 30 38 | 39 | true 40 | 41 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 42 | utf-8 43 | 44 | 45 | warn 46 | ACCEPT 47 | DENY 48 | 49 | 50 | 51 | 52 | 53 | ${LOG_PATH}/${APPDIR}/log_info.log 54 | 55 | ${LOG_PATH}/${APPDIR}/info/log-info-%d{yyyy-MM-dd}.%i.log 56 | 57 | 5MB 58 | 59 | 30 60 | 61 | true 62 | 63 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 64 | utf-8 65 | 66 | 67 | info 68 | ACCEPT 69 | DENY 70 | 71 | 72 | 73 | 74 | 75 | 76 | %d %level [%thread] %logger{10} [%file:%line] %msg%n 77 | utf-8 78 | 79 | 80 | debug 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /src/main/resources/mapper/DiscussPostMapper.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | id, user_id, title, content, type, status, create_time, comment_count, score 6 | 7 | 8 | 9 | user_id, title, content, type, status, create_time, comment_count, score 10 | 11 | 12 | 27 | 28 | 37 | 38 | 39 | insert into discuss_post() 40 | values(#{userId},#{title},#{content},#{type},#{status},#{createTime},#{commentCount},#{score}) 41 | 42 | 43 | 48 | 49 | 50 | update discuss_post set comment_count = #{commentCount} where id = #{id} 51 | 52 | 53 | 54 | update discuss_post set type = #{type} where id = #{id} 55 | 56 | 57 | 58 | update discuss_post set status = #{status} where id = #{id} 59 | 60 | 61 | 62 | update discuss_post set score = #{score} where id = #{id} 63 | 64 | 65 | 66 | 67 | 68 | -------------------------------------------------------------------------------- /src/main/resources/sensitive-words.txt: -------------------------------------------------------------------------------- 1 | 赌博 2 | 嫖娼 3 | 吸毒 4 | 开票 5 | 色情 -------------------------------------------------------------------------------- /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/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: url('/img/logo.jpg') no-repeat; 26 | background-size: 64px 43px; 27 | width: 64px; 28 | height: 43px; 29 | margin: 5px 15px 5px 0; 30 | } 31 | 32 | header .navbar { 33 | padding: 5px 0; 34 | font-size: 16px; 35 | } 36 | 37 | header .badge { 38 | position: absolute; 39 | top: -3px; 40 | left: 33px; 41 | } 42 | 43 | footer { 44 | padding: 20px 0; 45 | font-size: 12px; 46 | position: absolute; 47 | bottom: 0; 48 | width: 100%; 49 | } 50 | 51 | footer .qrcode { 52 | text-align: center; 53 | } 54 | 55 | footer .detail-info{ 56 | border-left: 1px solid #888; 57 | } 58 | 59 | footer .company-info li { 60 | padding-left: 16px; 61 | margin: 4px 0; 62 | } 63 | 64 | .main { 65 | padding: 20px 0; 66 | padding-bottom: 200px; 67 | } 68 | 69 | .main .container { 70 | background: #fff; 71 | padding: 20px; 72 | } 73 | 74 | i { 75 | font-style: normal; 76 | } 77 | 78 | u { 79 | text-decoration: none; 80 | } 81 | 82 | b { 83 | font-weight: normal; 84 | } 85 | 86 | a { 87 | color: #000; 88 | } 89 | 90 | a:hover { 91 | text-decoration: none; 92 | } 93 | 94 | .font-size-12 { 95 | font-size: 12px; 96 | } 97 | .font-size-14 { 98 | font-size: 14px; 99 | } 100 | .font-size-16 { 101 | font-size: 16px; 102 | } 103 | .font-size-18 { 104 | font-size: 18px; 105 | } 106 | .font-size-20 { 107 | font-size: 20px; 108 | } 109 | .font-size-22 { 110 | font-size: 20px; 111 | } 112 | .font-size-24 { 113 | font-size: 20px; 114 | } 115 | 116 | .hidden { 117 | display: none; 118 | } 119 | 120 | .rt-0 { 121 | right: 0; 122 | top: 0; 123 | } 124 | 125 | .square { 126 | display: inline-block; 127 | width: 7px; 128 | height: 7px; 129 | background: #ff6547; 130 | margin-bottom: 2px; 131 | margin-right: 3px; 132 | } 133 | 134 | .bg-gray { 135 | background: #eff0f2; 136 | } 137 | 138 | .user-header { 139 | width: 50px; 140 | height: 50px; 141 | } 142 | 143 | em { 144 | font-style: normal; 145 | color: #ffff00; 146 | } 147 | -------------------------------------------------------------------------------- /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/static/css/login.css: -------------------------------------------------------------------------------- 1 | .main .container { 2 | width: 720px; 3 | } 4 | -------------------------------------------------------------------------------- /src/main/resources/static/css/style_404.css: -------------------------------------------------------------------------------- 1 | body{ 2 | background-color: black; 3 | color: white; 4 | font-family: Sans-Serif; 5 | padding: 2%; 6 | } 7 | 8 | .center-text{ 9 | text-align: center; 10 | } 11 | 12 | #heading{ 13 | font-family: 'Creepster', cursive; 14 | font-size: 5em 15 | } 16 | 17 | 18 | .eyes{ 19 | width: 5em; 20 | height: 5em; 21 | border-radius: 50%; 22 | background-color: white; 23 | margin: 20px; 24 | display: flex; 25 | align-items: center; 26 | justify-content: center; 27 | position: relative; 28 | animation: blinking 3s ease-out 2 5s; 29 | 30 | } 31 | 32 | #eye-container{ 33 | display: flex; 34 | justify-content: center; 35 | margin-bottom: 5%; 36 | } 37 | 38 | .pupils{ 39 | width: 1em; 40 | height: 1em; 41 | border-radius: 50%; 42 | position: absolute; 43 | background-color: black; 44 | animation: moving-pups 2.5s cubic-bezier(.43,-0.45,.45,1.42) infinite; 45 | } 46 | 47 | @keyframes moving-pups{ 48 | 0% {left: 14%;} 49 | 50%{left: 70%; } 50 | 100% {left: 14%;} 51 | } 52 | 53 | @keyframes blinking{ 54 | 0%{transform: rotateX(0deg);} 55 | 20%{transform: rotateX(90deg);} 56 | 60%{transform: rotateX(45deg);} 57 | 70%{transform: rotateX(30deg);} 58 | 80%{transform: rotateX(0deg);} 59 | 100%{transform: rotateX(0deg);} 60 | } -------------------------------------------------------------------------------- /src/main/resources/static/css/style_500.css: -------------------------------------------------------------------------------- 1 | .full-screen { 2 | background-color: #333333; 3 | width: 100vw; 4 | height: 100vh; 5 | color: white; 6 | font-family: 'Arial Black'; 7 | text-align: center; 8 | } 9 | 10 | .container { 11 | padding-top: 4em; 12 | width: 50%; 13 | display: block; 14 | margin: 0 auto; 15 | } 16 | 17 | .error-num { 18 | font-size: 8em; 19 | } 20 | 21 | .eye { 22 | background: #fff; 23 | border-radius: 50%; 24 | display: inline-block; 25 | height: 100px; 26 | position: relative; 27 | width: 100px; 28 | } 29 | .eye::after { 30 | background: #000; 31 | border-radius: 50%; 32 | bottom: 56.1px; 33 | content: ' '; 34 | height: 33px; 35 | position: absolute; 36 | right: 33px; 37 | width: 33px; 38 | } 39 | 40 | .italic { 41 | font-style: italic; 42 | } 43 | 44 | p { 45 | margin-bottom: 4em; 46 | } 47 | 48 | a { 49 | color: white; 50 | text-decoration: none; 51 | text-transform: uppercase; 52 | } 53 | a:hover { 54 | color: lightgray; 55 | } 56 | -------------------------------------------------------------------------------- /src/main/resources/static/img/404.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/404.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/404_2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/404_2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/captcha.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/captcha.png -------------------------------------------------------------------------------- /src/main/resources/static/img/female.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/female.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/follow.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/follow.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/head.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/head.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/headImg2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/headImg2.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/icon-discuss-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/icon-discuss-2.png -------------------------------------------------------------------------------- /src/main/resources/static/img/icon-discuss.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/icon-discuss.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/icon.png -------------------------------------------------------------------------------- /src/main/resources/static/img/like.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/like.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/like.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/like.png -------------------------------------------------------------------------------- /src/main/resources/static/img/logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/logo.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/male.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/male.png -------------------------------------------------------------------------------- /src/main/resources/static/img/profession.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/profession.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/reply.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/reply.jpg -------------------------------------------------------------------------------- /src/main/resources/static/img/system.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/UHungLin/MyCommunity/1a039de97e3e10486d1c3cd66c5556210c271979/src/main/resources/static/img/system.jpg -------------------------------------------------------------------------------- /src/main/resources/static/js/discuss.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | $("#topBtn").click(setTop); 3 | $("#wonderfulBtn").click(setWonderful); 4 | $("#deleteBtn").click(setDelete); 5 | }); 6 | 7 | 8 | function like(btn, entityType, entityId, entityUserId, postId) { 9 | $.post( 10 | CONTEXT_PATH + "/like", 11 | {"entityType":entityType,"entityId":entityId,"entityUserId":entityUserId,"postId":postId}, 12 | function(data) { 13 | data = $.parseJSON(data); 14 | if(data.code == 0) { 15 | $(btn).children("i").text(data.likeCount); 16 | $(btn).children("b").text(data.likeStatus==1?'已赞':"赞"); 17 | } else { 18 | alert(data.msg); 19 | } 20 | } 21 | ); 22 | } 23 | 24 | // 置顶 25 | function setTop() { 26 | $.post( 27 | CONTEXT_PATH + "/discuss/top", 28 | {"id":$("#postId").val()}, 29 | function(data) { 30 | data = $.parseJSON(data); 31 | if(data.code == 0) { 32 | // $("#topBtn").attr("disabled", "disabled"); 33 | $("#topBtn").text(data.type==1 ? '已置顶' : '置顶'); 34 | } else { 35 | alert(data.msg); 36 | } 37 | } 38 | ); 39 | } 40 | 41 | // 加精 42 | function setWonderful() { 43 | $.post( 44 | CONTEXT_PATH + "/discuss/wonderful", 45 | {"id":$("#postId").val()}, 46 | function(data) { 47 | data = $.parseJSON(data); 48 | if(data.code == 0) { 49 | // $("#wonderfulBtn").attr("disabled", "disabled"); 50 | $("#wonderfulBtn").text(data.status==1 ? '已加精' : '加精'); 51 | } else { 52 | alert(data.msg); 53 | } 54 | } 55 | ); 56 | } 57 | 58 | // 删除 59 | function setDelete() { 60 | $.post( 61 | CONTEXT_PATH + "/discuss/delete", 62 | {"id":$("#postId").val()}, 63 | function(data) { 64 | data = $.parseJSON(data); 65 | if(data.code == 0) { 66 | location.href = CONTEXT_PATH + "/index"; 67 | } else { 68 | alert(data.msg); 69 | } 70 | } 71 | ); 72 | } 73 | 74 | -------------------------------------------------------------------------------- /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 | '' 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/resources/static/js/index.js: -------------------------------------------------------------------------------- 1 | 2 | $(function(){ 3 | $("#publishBtn").click(publish); 4 | }); 5 | 6 | function publish() { 7 | $("#publishModal").modal("hide"); 8 | 9 | // 获取标题和内容 10 | var title = $("#recipient-name").val(); 11 | var content = $("#message-text").val(); 12 | // 发送异步请求(POST) 13 | $.post( 14 | CONTEXT_PATH + "/discuss/add", 15 | {"title":title,"content":content}, 16 | function(data) { 17 | data = $.parseJSON(data); 18 | // 在提示框中显示返回消息 19 | $("#hintBody").text(data.msg); 20 | // 显示提示框 21 | $("#hintModal").modal("show"); 22 | // 2秒后,自动隐藏提示框 23 | setTimeout(function(){ 24 | $("#hintModal").modal("hide"); 25 | // 刷新页面 26 | if(data.code == 0) { 27 | window.location.reload(); 28 | } 29 | }, 2000); 30 | } 31 | ); 32 | } 33 | 34 | // 500页面的JS 35 | $(".full-screen").mousemove(function(event) { 36 | var eye = $(".eye"); 37 | var x = (eye.offset().left) + (eye.width() / 2); 38 | var y = (eye.offset().top) + (eye.height() / 2); 39 | var rad = Math.atan2(event.pageX - x, event.pageY - y); 40 | var rot = (rad * (180 / Math.PI) * -1) + 180; 41 | eye.css({ 42 | '-webkit-transform': 'rotate(' + rot + 'deg)', 43 | '-moz-transform': 'rotate(' + rot + 'deg)', 44 | '-ms-transform': 'rotate(' + rot + 'deg)', 45 | 'transform': 'rotate(' + rot + 'deg)' 46 | }); 47 | }); -------------------------------------------------------------------------------- /src/main/resources/static/js/letter.js: -------------------------------------------------------------------------------- 1 | $(function(){ 2 | $("#sendBtn").click(send_letter); 3 | $(".close").click(delete_msg); 4 | $("#back").click(back); 5 | }); 6 | 7 | function send_letter() { 8 | $("#sendModal").modal("hide"); 9 | 10 | var toName = $("#recipient-name").val(); 11 | var content = $("#message-text").val(); 12 | 13 | 14 | $.post( 15 | CONTEXT_PATH + "/letter/send", 16 | {"toName":toName,"content":content}, 17 | function(data) { 18 | data = $.parseJSON(data); 19 | if(data.code == 0) { 20 | $("#hintBody").text("发送成功!"); 21 | } else { 22 | $("#hintBody").text(data.msg); 23 | } 24 | 25 | $("#hintModal").modal("show"); 26 | setTimeout(function(){ 27 | $("#hintModal").modal("hide"); 28 | location.reload(); 29 | }, 2000); 30 | } 31 | ); 32 | } 33 | 34 | function delete_msg() { 35 | // TODO 删除数据 36 | // $(this).parents(".media").remove(); 37 | alert("暂不支持删除操作"); 38 | } 39 | 40 | function back() { 41 | location.href= CONTEXT_PATH + "/letter/list"; 42 | } 43 | 44 | 45 | -------------------------------------------------------------------------------- /src/main/resources/static/js/myPage.js: -------------------------------------------------------------------------------- 1 | function changeDiv(n) { 2 | if( n == 1) { 3 | var play = document.getElementById("active"); 4 | play.className = "active"; 5 | var hideCom = document.getElementById("community"); 6 | hideCom.className = "hidden"; 7 | var hideLea = document.getElementById("leaveMes"); 8 | hideLea.className = "hidden"; 9 | } else if(n == 2) { 10 | var play = document.getElementById("active"); 11 | play.className = "hidden"; 12 | var hideCom = document.getElementById("community"); 13 | hideCom.className = "followee"; 14 | var hideLea = document.getElementById("leaveMes"); 15 | hideLea.className = "hidden"; 16 | } else if(n == 3) { 17 | var play = document.getElementById("active"); 18 | play.className = "hidden"; 19 | var hideCom = document.getElementById("community"); 20 | hideCom.className = "hidden"; 21 | var hideLea = document.getElementById("leaveMes"); 22 | hideLea.className = "follower"; 23 | } 24 | } 25 | 26 | function changeLi(n) { 27 | var lis = document.getElementsByClassName("tagItem"); 28 | for(var i=0;i 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 溢出网-404 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | 26 |
27 | 28 |
29 |
30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/resources/templates/error/404_2.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 404 Not Found 404 - Who turned off the light? 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |

404 NOT FOUND

14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |

没有你要找的页面哦

25 |

26 |
27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /src/main/resources/templates/error/500.html: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 500 Error Page Challenge 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 | 5 15 |
16 |
17 |

Emmmmm... 抱歉 发生了某种不可以预料的情况 (ノ=Д=)ノ┻━┻

18 | 19 |
20 |
21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /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/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/resources/templates/site/followee.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 溢出网-关注 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 | 37 | 38 | 39 |
    40 |
  • 41 | 42 | 用户头像 43 | 44 |
    45 |
    46 | 落基山脉下的闲人 47 | 48 | 关注于 2019-04-28 14:13:25 49 | 50 |
    51 |
    52 | 53 | 55 |
    56 |
    57 |
  • 58 |
59 | 60 | 63 |
64 |
65 | 66 | 67 |
68 | 69 |
70 |
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/follower.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 溢出网-关注 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 | 37 | 38 | 39 |
    40 |
  • 41 | 42 | 用户头像 43 | 44 |
    45 |
    46 | 落基山脉下的闲人 47 | 48 | 关注于 2019-04-28 14:13:25 49 | 50 |
    51 |
    52 | 53 | 55 |
    56 |
    57 |
  • 58 |
59 | 60 | 63 |
64 |
65 | 66 | 67 |
68 | 69 |
70 |
71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/footer.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | 5 |
6 | 8 |
9 | 10 |
11 |
12 |
13 | 36 |
37 |
38 |
39 |
40 | 56 |
57 |
58 |
59 |
60 |
61 |
62 | 63 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/header.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 |
5 |
6 | 7 | 62 |
63 |
64 | 65 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 登录 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 |
22 |

登  录

23 |
24 |
25 | 26 |
27 | 30 |
31 | 该账号不存在! 32 |
33 |
34 |
35 |
36 | 37 |
38 | 41 |
42 | 密码长度不能小于8位! 43 |
44 |
45 |
46 |
47 | 48 |
49 | 51 |
52 | 验证码不正确! 53 |
54 |
55 |
56 | 57 | 刷新验证码 58 |
59 |
60 |
61 |
62 |
63 | 64 | 65 | 66 | 67 |
68 |
69 |
70 |
71 |
72 | 73 |
74 |
75 |
76 |
77 |
78 | 79 | 80 |
81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 94 | 95 | 96 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/my-post.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/notice-detail.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 溢出网-通知详情 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 |
22 |
23 |
24 |
系统通知
25 |
26 |
27 | 28 |
29 |
30 | 31 | 32 |
    33 |
  • 34 | 系统图标 35 | 70 |
  • 71 |
72 | 73 | 76 |
77 |
78 | 79 | 80 |
81 | 82 |
83 |
84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/operate-result.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 操作结果 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
22 |

23 |
24 |

25 | 系统会在 8 秒后自动跳转, 26 | 您也可以点此 链接, 手动跳转! 27 |

28 |
29 |
30 |
31 | 32 | 33 |
34 | 35 |
36 |
37 | 38 | 39 | 40 | 41 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/pagination.html: -------------------------------------------------------------------------------- 1 | 19 | 20 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 注册 11 | 12 | 13 |
14 | 15 |
16 | 17 |
18 | 19 | 20 |
21 |
22 |

注  册

23 |
24 |
25 | 26 |
27 | 31 |
32 | 该账号已存在! 33 |
34 |
35 |
36 |
37 | 38 |
39 | 43 |
44 | 密码长度不能小于8位! 45 |
46 |
47 |
48 |
49 | 50 |
51 | 54 |
55 | 两次输入的密码不一致! 56 |
57 |
58 |
59 |
60 | 61 |
62 | 66 |
67 | 该邮箱已注册! 68 |
69 |
70 |
71 |
72 |
73 |
74 | 75 |
76 |
77 |
78 |
79 |
80 | 81 | 82 |
83 | 84 |
85 |
86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /src/main/resources/templates/site/search.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 溢出网-搜索结果 10 | 11 | 12 |
13 | 14 |
15 | 16 |
17 | 18 | 19 |
20 |
21 |
相关帖子
22 | 23 |
    24 |
  • 25 | 用户头像 26 |
    27 |
    28 | 备战春招,面试刷题跟他复习,一个月全搞定! 29 |
    30 |
    31 | 金三银四的金三已经到了,你还沉浸在过年的喜悦中吗? 如果是,那我要让你清醒一下了:目前大部分公司已经开启了内推,正式网申也将在3月份陆续开始,金三银四,春招的求职黄金时期已经来啦!!! 再不准备,作为19应届生的你可能就找不到工作了。。。作为20届实习生的你可能就找不到实习了。。。 现阶段时间紧,任务重,能做到短时间内快速提升的也就只有算法了, 那么算法要怎么复习?重点在哪里?常见笔试面试算法题型和解题思路以及最优代码是怎样的? 跟左程云老师学算法,不仅能解决以上所有问题,还能在短时间内得到最大程度的提升!!! 32 |
    33 |
    34 | 寒江雪 35 | 发布于 2019-04-15 15:32:18 36 |
      37 |
    • 11
    • 38 |
    • |
    • 39 |
    • 回复 7
    • 40 |
    41 |
    42 |
    43 |
  • 44 |
45 | 46 | 49 |
50 |
51 | 52 | 53 |
54 | 55 |
56 |
57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/EmailUtilTests.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import fun.linyuhong.myCommunity.util.EmailUtil; 4 | import org.junit.Test; 5 | 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | 9 | /** 10 | * @author linyuhong 11 | * @date 2019/9/2 12 | */ 13 | public class EmailUtilTests { 14 | 15 | @Test 16 | public void test() { 17 | System.out.println(EmailUtil.isEmail("test@qq.com")); 18 | } 19 | 20 | public static void main(String[] args) { 21 | Map map = new HashMap(); 22 | System.out.println(map != null); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/HttpUtilTests.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import fun.linyuhong.myCommunity.util.HttpUtil; 4 | 5 | 6 | /** 7 | * @author linyuhong 8 | * @date 2019/9/2 9 | */ 10 | public class HttpUtilTests { 11 | 12 | public static void main(String[] args) { 13 | System.out.println(HttpUtil.sendGet("https://api.uomg.com/api/rand.avatar")); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/MyCommunityApplicationTests.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.boot.test.context.SpringBootTest; 6 | import org.springframework.test.context.junit4.SpringRunner; 7 | 8 | @RunWith(SpringRunner.class) 9 | @SpringBootTest 10 | public class MyCommunityApplicationTests { 11 | 12 | @Test 13 | public void contextLoads() { 14 | String s = "abc"; 15 | System.out.println(s.substring(0)); 16 | System.out.println(s.substring(1)); 17 | System.out.println(s.substring(3)); 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/RedisTest.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import org.junit.Test; 4 | import org.junit.runner.RunWith; 5 | import org.springframework.beans.factory.annotation.Autowired; 6 | import org.springframework.boot.test.context.SpringBootTest; 7 | import org.springframework.data.redis.core.RedisTemplate; 8 | import org.springframework.test.context.junit4.SpringRunner; 9 | 10 | import java.util.concurrent.TimeUnit; 11 | 12 | /** 13 | * @author linyuhong 14 | * @date 2019/9/6 15 | */ 16 | @RunWith(SpringRunner.class) 17 | @SpringBootTest 18 | public class RedisTest { 19 | 20 | @Autowired 21 | private RedisTemplate redisTemplate; 22 | 23 | @Test 24 | public void test() { 25 | redisTemplate.opsForList().leftPush("queueTestKey", 1); 26 | redisTemplate.opsForList().leftPush("queueTestKey", 2); 27 | redisTemplate.opsForList().leftPush("queueTestKey", 3); 28 | redisTemplate.opsForList().leftPush("queueTestKey", 6); 29 | redisTemplate.opsForList().leftPush("queueTestKey", 7); 30 | redisTemplate.opsForList().leftPush("queueTestKey", 8); 31 | 32 | while (true) { 33 | 34 | System.out.println(redisTemplate.opsForList().rightPop("queueTestKey", 0, TimeUnit.MINUTES)); 35 | } 36 | 37 | // System.out.println(redisTemplate.opsForList().rightPop("queueTestKey", 0, TimeUnit.SECONDS)); 38 | // System.out.println(redisTemplate.opsForList().range("queueTestKey", 0, -1)); 39 | // System.out.println(redisTemplate.opsForList().range("queueTestKey", 0, -1)); 40 | // while (true) { 41 | //// System.out.println(redisTemplate.opsForList().rightPop("queueTestKey", 0, TimeUnit.SECONDS)); 42 | //// System.out.println(redisTemplate.opsForList().rightPop("queueTestKey")); 43 | //// System.out.println(message); 44 | //// System.out.println(redisTemplate.opsForList().range("queueTestKey", 0, 2)); 45 | //// redisTemplate.opsForList().trim("queueTestKey", 3, -1); 46 | //// System.out.println(redisTemplate.opsForList().range("queueTestKey", 0, -1)); 47 | // 48 | // Integer message = (Integer) redisTemplate.opsForList().rightPop("queueTestKey", 10L, TimeUnit.HOURS); 49 | // System.out.println(message); 50 | // } 51 | } 52 | 53 | 54 | 55 | } 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/SensitiveFilterTests.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import fun.linyuhong.myCommunity.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 | /** 12 | * @author linyuhong 13 | * @date 2019/9/3 14 | */ 15 | @RunWith(SpringRunner.class) 16 | @SpringBootTest 17 | @ContextConfiguration(classes = MyCommunityApplication.class) 18 | public class SensitiveFilterTests { 19 | 20 | @Autowired 21 | private SensitiveFilter sensitiveFilter; 22 | 23 | @Test 24 | public void testSensitiveFilter() { 25 | System.out.println(sensitiveFilter.filter("你好赌博")); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/ThreadPoolTest.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | import fun.linyuhong.myCommunity.service.AlphaService; 4 | import org.junit.Test; 5 | import org.junit.runner.RunWith; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; 11 | import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; 12 | import org.springframework.test.context.ContextConfiguration; 13 | import org.springframework.test.context.junit4.SpringRunner; 14 | 15 | import java.util.Date; 16 | import java.util.concurrent.ExecutorService; 17 | import java.util.concurrent.Executors; 18 | import java.util.concurrent.ScheduledExecutorService; 19 | import java.util.concurrent.TimeUnit; 20 | 21 | @RunWith(SpringRunner.class) 22 | @SpringBootTest 23 | @ContextConfiguration(classes = MyCommunityApplication.class) 24 | public class ThreadPoolTest { 25 | 26 | private static final Logger logger = LoggerFactory.getLogger(ThreadPoolTest.class); 27 | 28 | // JDK普通线程池 29 | private ExecutorService executorService = Executors.newFixedThreadPool(5); 30 | 31 | // JDK可执行定时任务的线程池 32 | private ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(5); 33 | 34 | // 注入Spring帮我们管理的线程池 35 | // Spring普通线程池 36 | @Autowired 37 | private ThreadPoolTaskExecutor taskExecutor; 38 | 39 | // Spring可执行定时任务的线程池 40 | @Autowired 41 | private ThreadPoolTaskScheduler taskScheduler; 42 | 43 | @Autowired 44 | private AlphaService alphaService; 45 | 46 | private void sleep(long m) { 47 | try { 48 | Thread.sleep(m); 49 | } catch (InterruptedException e) { 50 | e.printStackTrace(); 51 | } 52 | } 53 | 54 | // 1.JDK普通线程池 55 | @Test 56 | public void testExecutorService() { 57 | Runnable task = new Runnable() { 58 | @Override 59 | public void run() { 60 | logger.debug("Hello ExecutorService"); 61 | } 62 | }; 63 | 64 | for (int i = 0; i < 10; i++) { 65 | executorService.submit(task); 66 | } 67 | 68 | sleep(10000); 69 | } 70 | 71 | // 2.JDK定时任务线程池 72 | @Test 73 | public void testScheduledExecutorService() { 74 | Runnable task = new Runnable() { 75 | @Override 76 | public void run() { 77 | logger.debug("Hello ScheduledExecutorService"); 78 | } 79 | }; 80 | 81 | scheduledExecutorService.scheduleAtFixedRate(task, 10000, 1000, TimeUnit.MILLISECONDS); 82 | 83 | sleep(30000); 84 | } 85 | 86 | // 3.Spring普通线程池 87 | @Test 88 | public void testThreadPoolTaskExecutor() { 89 | Runnable task = new Runnable() { 90 | @Override 91 | public void run() { 92 | logger.debug("Hello ThreadPoolTaskExecutor"); 93 | } 94 | }; 95 | 96 | for (int i = 0; i < 10; i++) { 97 | taskExecutor.submit(task); 98 | } 99 | 100 | sleep(10000); 101 | } 102 | 103 | // 4.Spring定时任务线程池 104 | @Test 105 | public void testThreadPoolTaskScheduler() { 106 | Runnable task = new Runnable() { 107 | @Override 108 | public void run() { 109 | logger.debug("Hello ThreadPoolTaskScheduler"); 110 | } 111 | }; 112 | 113 | Date startTime = new Date(System.currentTimeMillis() + 10000); 114 | taskScheduler.scheduleAtFixedRate(task, startTime, 1000); 115 | 116 | sleep(30000); 117 | } 118 | 119 | // 5.Spring普通线程池(简化) 相关配置在 AlphaService 类中 120 | @Test 121 | public void testThreadPoolTaskExecutorSimple() { 122 | for (int i = 0; i < 10; i++) { 123 | alphaService.execute1(); 124 | } 125 | 126 | sleep(10000); 127 | } 128 | 129 | // 6.Spring定时任务线程池(简化) 相关配置在 AlphaService 类中 130 | @Test 131 | public void testThreadPoolTaskSchedulerSimple() { 132 | sleep(30000); 133 | } 134 | 135 | } 136 | -------------------------------------------------------------------------------- /src/test/java/fun/linyuhong/myCommunity/XORUtilTests.java: -------------------------------------------------------------------------------- 1 | package fun.linyuhong.myCommunity; 2 | 3 | 4 | import fun.linyuhong.myCommunity.util.XORUtil; 5 | import org.junit.Assert; 6 | import org.junit.Test; 7 | import org.junit.runner.RunWith; 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.boot.test.context.SpringBootTest; 10 | import org.springframework.test.context.junit4.SpringRunner; 11 | 12 | 13 | 14 | @RunWith(SpringRunner.class) 15 | @SpringBootTest 16 | public class XORUtilTests { 17 | 18 | 19 | @Test 20 | public void test(){ 21 | byte[] b = new byte[]{1, 2}; 22 | System.out.println(XORUtil.encryptId(12, b)); 23 | System.out.println(XORUtil.encryptId(13, b)); 24 | System.out.println(XORUtil.encryptId(14, b)); 25 | System.out.println(XORUtil.encryptId(10, b)); 26 | System.out.println(XORUtil.encryptId(100, b)); 27 | System.out.println(XORUtil.encryptId(1000, b)); 28 | System.out.println(XORUtil.encryptId(10000, b)); 29 | System.out.println(XORUtil.encryptId(100000, b)); 30 | System.out.println(XORUtil.encryptId(1000000, b)); 31 | } 32 | 33 | } 34 | --------------------------------------------------------------------------------