仿牛客论坛项目(4)
仿牛客论坛项目
- 一、Elasticsearch入门
- 1.1 elasticsearch安装
- 1.2 修改config目录下的elasticsearch.yml配置文件
- 1.3 配置环境变量
- 1.4 下载ik中文分词器
- 1.5 下载postman
- 1.6 启动es
- 1.7 测试是否正确启动
- 1.8 操作ES的RESTful语法
- 1.9 es弹性搜索指南
- 1.10 es中的数据类型
- 二、 Spring整合Elasticsearch
- 2.1 导入依赖
- 2.2 修改配置文件
- 2.3 修改主启动类
- 2.4 实体类添加es注解
- 2.5 数据操作
- DiscussPostRepository
- 2.6 测试
- 三、开发社区搜索功能
- 3.1 业务层
- ElasticSearchService(封装对es的增删改查操作)
- 3.2 控制层
- DiscussPostController
- CommentController
- SearchController
- 3.3 新增事件
- EventConsumer
- 3.4 静态资源
- index.html
- serch.html
- 3.4 测试
一、Elasticsearch入门
1.1 elasticsearch安装
https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-6.4.3.zip
- 下载后直接解压
1.2 修改config目录下的elasticsearch.yml配置文件
1.3 配置环境变量
- 把对应的bin目录配置到环境变量
1.4 下载ik中文分词器
https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.4.3/elasticsearch-analysis-ik-6.4.3.zip
- 下载之后解压到es安装目录下的plugins目录下的ik(自己建)目录下
- 如果需要引入自己建的字典,则自己建一个dic文件,把需要的新词加入进去,然后在下面文件引入即可
1.5 下载postman
https://www.getpostman.com
1.6 启动es
1.7 测试是否正确启动
1.8 操作ES的RESTful语法
1.9 es弹性搜索指南
https://www.elastic.co/guide/en/elasticsearch/reference/6.4/index.html
1.10 es中的数据类型
https://www.elastic.co/guide/en/elasticsearch/reference/6.4/mapping-types.html
二、 Spring整合Elasticsearch
2.1 导入依赖
<!--整合elasticsearch--><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>
2.2 修改配置文件
# ElasticsearchProperties
# 集群名
spring.data.elasticsearch.cluster-name=nowcoder
# tcp端口:9300, http访问端口:9200
spring.data.elasticsearch.cluster-nodes=127.0.0.1:9300
2.3 修改主启动类
- 避免es和redis同时依赖netty起冲突
@SpringBootApplication
@MapperScan("com.nowcoder.community.mapper")
public class CommunityApplication {@PostConstructpublic void init() {// 解决netty启动冲突问题// see Netty4Utils.setAvailableProcessors()System.setProperty("es.set.netty.runtime.available.processors", "false");}public static void main(String[] args) {SpringApplication.run(CommunityApplication.class, args);}}
2.4 实体类添加es注解
- 在程序启动时会自动在es中创建索引
@Data
//index必须为lowercase不然会报错 index:类似mysql中的数据库,type:类似mysql中的表,shards:分片(将数据分成几片),replicas:副本(3份)
@Document(indexName = "discusspost", type = "_doc", shards = 6, replicas = 3)
public class DiscussPost {/*** 自增主键,插入数据后将自增主键设置到对象中*/@TableId(type = IdType.AUTO)@Idprivate Integer id;@Field(type = FieldType.Integer)private Integer userId;//文本类型,将对应的内容拆分成最多的词,搜索时搜索最常见的分词即可@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String title;@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String content;//0-普通; 1-置顶;@Field(type = FieldType.Integer)private Integer type;//0-正常; 1-精华; 2-拉黑;@Field(type = FieldType.Integer)private Integer status;@Field(type = FieldType.Date)private Date createTime;@Field(type = FieldType.Integer)private Integer commentCount;@Field(type = FieldType.Double)private Double score;}
2.5 数据操作
DiscussPostRepository
@Repository
public interface DiscussPostRepository extends ElasticsearchRepository<DiscussPost, Integer> {}
2.6 测试
@RunWith(SpringRunner.class)
@SpringBootTest
@ContextConfiguration(classes = CommunityApplication.class)
public class ElasticsearchTests {@Resourceprivate DiscussPostMapper discussPostMapper;@Resourceprivate DiscussPostService discussPostService;@Resourceprivate DiscussPostRepository discussRepository;@Resourceprivate ElasticsearchTemplate elasticTemplate;/*** 每次添加单条数据*/@Testpublic void testInsert() {discussRepository.save(discussPostMapper.selectById(241));discussRepository.save(discussPostMapper.selectById(242));discussRepository.save(discussPostMapper.selectById(243));}/*** 每次添加多条数据*/@Testpublic void testInsertList() {discussRepository.saveAll(discussPostService.findDiscussPosts(101, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(102, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(103, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(111, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(112, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(131, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(132, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(133, 0, 100));discussRepository.saveAll(discussPostService.findDiscussPosts(134, 0, 100));}/*** 测试更新*/@Testpublic void testUpdate() {DiscussPost post = discussPostMapper.selectById(231);post.setContent("我是新人,使劲灌水.");discussRepository.save(post);}/*** 测试删除*/@Testpublic void testDelete() {// discussRepository.deleteById(231);discussRepository.deleteAll();}/*** 测试查询*/@Testpublic void testSearchByRepository() {SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")).withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(0, 10)).withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();// elasticTemplate.queryForPage(searchQuery, class, SearchResultMapper)// 底层获取得到了高亮显示的值, 但是没有返回.Page<DiscussPost> page = discussRepository.search(searchQuery);System.out.println(page.getTotalElements());System.out.println(page.getTotalPages());System.out.println(page.getNumber());System.out.println(page.getSize());for (DiscussPost post : page) {System.out.println(post);}}@Testpublic void testSearchByTemplate() {SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery("互联网寒冬", "title", "content")).withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(0, 10)).withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();Page<DiscussPost> page = elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {SearchHits hits = response.getHits();if (hits.getTotalHits() <= 0) {return null;}List<DiscussPost> list = new ArrayList<>();for (SearchHit hit : hits) {DiscussPost post = new DiscussPost();String id = hit.getSourceAsMap().get("id").toString();post.setId(Integer.valueOf(id));String userId = hit.getSourceAsMap().get("userId").toString();post.setUserId(Integer.valueOf(userId));String title = hit.getSourceAsMap().get("title").toString();post.setTitle(title);String content = hit.getSourceAsMap().get("content").toString();post.setContent(content);String status = hit.getSourceAsMap().get("status").toString();post.setStatus(Integer.valueOf(status));String createTime = hit.getSourceAsMap().get("createTime").toString();post.setCreateTime(new Date(Long.valueOf(createTime)));String commentCount = hit.getSourceAsMap().get("commentCount").toString();post.setCommentCount(Integer.valueOf(commentCount));// 处理高亮显示的结果HighlightField titleField = hit.getHighlightFields().get("title");if (titleField != null) {post.setTitle(titleField.getFragments()[0].toString());}HighlightField contentField = hit.getHighlightFields().get("content");if (contentField != null) {post.setContent(contentField.getFragments()[0].toString());}list.add(post);}return new AggregatedPageImpl(list, pageable,hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());}});System.out.println(page.getTotalElements());System.out.println(page.getTotalPages());System.out.println(page.getNumber());System.out.println(page.getSize());for (DiscussPost post : page) {System.out.println(post);}}}
测试testinsert()方法和testInsertList()方法
GET: localhost:9200/discusspost/_search: 查询discusspost索引下的文档
查询指定的文档数据
调用修改方法修改上面这条数据的content
测试删除方法
三、开发社区搜索功能
3.1 业务层
ElasticSearchService(封装对es的增删改查操作)
@Service
public class ElasticsearchService {@Resourceprivate DiscussPostRepository discussRepository;@Resourceprivate ElasticsearchTemplate elasticTemplate;public void saveDiscussPost(DiscussPost post) {discussRepository.save(post);}public void deleteDiscussPost(int id) {discussRepository.deleteById(id);}public Page<DiscussPost> searchDiscussPost(String keyword, int current, int limit) {SearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(QueryBuilders.multiMatchQuery(keyword, "title", "content")).withSort(SortBuilders.fieldSort("type").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("score").order(SortOrder.DESC)).withSort(SortBuilders.fieldSort("createTime").order(SortOrder.DESC)).withPageable(PageRequest.of(current, limit)).withHighlightFields(new HighlightBuilder.Field("title").preTags("<em>").postTags("</em>"),new HighlightBuilder.Field("content").preTags("<em>").postTags("</em>")).build();return elasticTemplate.queryForPage(searchQuery, DiscussPost.class, new SearchResultMapper() {@Overridepublic <T> AggregatedPage<T> mapResults(SearchResponse response, Class<T> aClass, Pageable pageable) {SearchHits hits = response.getHits();if (hits.getTotalHits() <= 0) {return null;}List<DiscussPost> list = new ArrayList<>();for (SearchHit hit : hits) {DiscussPost post = new DiscussPost();String id = hit.getSourceAsMap().get("id").toString();post.setId(Integer.valueOf(id));String userId = hit.getSourceAsMap().get("userId").toString();post.setUserId(Integer.valueOf(userId));String title = hit.getSourceAsMap().get("title").toString();post.setTitle(title);String content = hit.getSourceAsMap().get("content").toString();post.setContent(content);String status = hit.getSourceAsMap().get("status").toString();post.setStatus(Integer.valueOf(status));String createTime = hit.getSourceAsMap().get("createTime").toString();post.setCreateTime(new Date(Long.valueOf(createTime)));String commentCount = hit.getSourceAsMap().get("commentCount").toString();post.setCommentCount(Integer.valueOf(commentCount));// 处理高亮显示的结果HighlightField titleField = hit.getHighlightFields().get("title");if (titleField != null) {post.setTitle(titleField.getFragments()[0].toString());}HighlightField contentField = hit.getHighlightFields().get("content");if (contentField != null) {post.setContent(contentField.getFragments()[0].toString());}list.add(post);}return new AggregatedPageImpl(list, pageable,hits.getTotalHits(), response.getAggregations(), response.getScrollId(), hits.getMaxScore());}});}}
3.2 控制层
DiscussPostController
@PostMapping("/add")@ResponseBodypublic String addDiscussPost(String title, String content) {User user = UserThreadLocal.getUser();if (user == null) {return CommunityUtil.getJSONString(403, "你还没有登录喔!");}DiscussPost post = new DiscussPost();post.setUserId(user.getId());post.setTitle(title);post.setContent(content);post.setCreateTime(new Date());post.setType(0);post.setStatus(0);post.setCommentCount(0);discussPostService.addDiscussPost(post);// 触发发帖事件Event event = new Event().setTopic(TOPIC_PUBLISH).setUserId(user.getId()).setEntityType(ENTITY_TYPE_POST).setEntityId(post.getId());eventProducer.fireEvent(event);// 报错的情况,将来统一处理.return CommunityUtil.getJSONString(0, "发布成功!");}
CommentController
@PostMapping("/add/{discussPostId}")public String addComment(@PathVariable("discussPostId") int discussPostId, Comment comment) {comment.setUserId(UserThreadLocal.getUser().getId());comment.setStatus(0);comment.setCreateTime(new Date());commentService.addComment(comment);// 触发评论事件Event event = new Event().setTopic(TOPIC_COMMENT).setUserId(UserThreadLocal.getUser().getId()).setEntityType(comment.getEntityType()).setEntityId(comment.getEntityId()).setData("postId", discussPostId);if (comment.getEntityType() == ENTITY_TYPE_POST) {DiscussPost target = discussPostService.findDiscussPostById(comment.getEntityId());event.setEntityUserId(target.getUserId());} else if (comment.getEntityType() == ENTITY_TYPE_COMMENT) {Comment target = commentService.findCommentById(comment.getEntityId());event.setEntityUserId(target.getUserId());}eventProducer.fireEvent(event);if (comment.getEntityType() == ENTITY_TYPE_POST) {// 触发发帖事件event = new Event().setTopic(TOPIC_PUBLISH).setUserId(comment.getUserId()).setEntityType(ENTITY_TYPE_POST).setEntityId(discussPostId);eventProducer.fireEvent(event);}return "redirect:/discuss/detail/" + discussPostId;}
SearchController
@Controller
public class SearchController implements CommunityConstant {@Resourceprivate ElasticsearchService elasticsearchService;@Resourceprivate UserService userService;@Resourceprivate LikeService likeService;// search?keyword=xxx@GetMapping("/search")public String search(String keyword, Page page, Model model) {// 搜索帖子org.springframework.data.domain.Page<DiscussPost> searchResult =elasticsearchService.searchDiscussPost(keyword, page.getCurrent() - 1, page.getLimit());// 聚合数据List<Map<String, Object>> discussPosts = new ArrayList<>();if (searchResult != null) {for (DiscussPost post : searchResult) {Map<String, Object> map = new HashMap<>();// 帖子map.put("post", post);// 作者map.put("user", userService.findUserById(post.getUserId()));// 点赞数量map.put("likeCount", likeService.findEntityLikeCount(ENTITY_TYPE_POST, post.getId()));discussPosts.add(map);}}model.addAttribute("discussPosts", discussPosts);model.addAttribute("keyword", keyword);// 分页信息page.setPath("/search?keyword=" + keyword);page.setRows(searchResult == null ? 0 : (int) searchResult.getTotalElements());return "/site/search";}}
3.3 新增事件
EventConsumer
// 消费发帖事件@KafkaListener(topics = {TOPIC_PUBLISH})public void handlePublishMessage(ConsumerRecord record) {if (record == null || record.value() == null) {logger.error("消息的内容为空!");return;}Event event = JSONObject.parseObject(record.value().toString(), Event.class);if (event == null) {logger.error("消息格式错误!");return;}DiscussPost post = discussPostService.findDiscussPostById(event.getEntityId());elasticsearchService.saveDiscussPost(post);}
3.4 静态资源
index.html
<!-- 搜索 --><form class="form-inline my-2 my-lg-0" method="get" th:action="@{/search}"><input class="form-control mr-sm-2" type="search" aria-label="Search" name="keyword" th:value="${keyword}"/><button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button></form>
serch.html
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"><link rel="icon" href="https://static.nowcoder.com/images/logo_87_87.png"/><link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" crossorigin="anonymous"><link rel="stylesheet" th:href="@{/css/global.css}" /><title>牛客网-搜索结果</title>
</head>
<body>
<div class="nk-container"><!-- 头部 --><header class="bg-dark sticky-top" th:replace="index::header"><div class="container"><!-- 导航 --><nav class="navbar navbar-expand-lg navbar-dark"><!-- logo --><a class="navbar-brand" href="#"></a><button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation"><span class="navbar-toggler-icon"></span></button><!-- 功能 --><div class="collapse navbar-collapse" id="navbarSupportedContent"><ul class="navbar-nav mr-auto"><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="../index.html">首页</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link position-relative" href="letter.html">消息<span class="badge badge-danger">12</span></a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="register.html">注册</a></li><li class="nav-item ml-3 btn-group-vertical"><a class="nav-link" href="login.html">登录</a></li><li class="nav-item ml-3 btn-group-vertical dropdown"><a class="nav-link dropdown-toggle" href="#" id="navbarDropdown" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false"><img src="http://images.nowcoder.com/head/1t.png" class="rounded-circle" style="width:30px;"/></a><div class="dropdown-menu" aria-labelledby="navbarDropdown"><a class="dropdown-item text-center" href="profile.html">个人主页</a><a class="dropdown-item text-center" href="setting.html">账号设置</a><a class="dropdown-item text-center" href="login.html">退出登录</a><div class="dropdown-divider"></div><span class="dropdown-item text-center text-secondary">nowcoder</span></div></li></ul><!-- 搜索 --><form class="form-inline my-2 my-lg-0" action="search.html"><input class="form-control mr-sm-2" type="search" aria-label="Search" /><button class="btn btn-outline-light my-2 my-sm-0" type="submit">搜索</button></form></div></nav></div></header><!-- 内容 --><div class="main"><div class="container"><h6><b class="square"></b> 相关帖子</h6><!-- 帖子列表 --><ul class="list-unstyled mt-4"><li class="media pb-3 pt-3 mb-3 border-bottom" th:each="map:${discussPosts}"><img th:src="${map.user.headerUrl}" class="mr-4 rounded-circle" alt="用户头像" style="width:50px;height:50px;"><div class="media-body"><h6 class="mt-0 mb-3"><a th:href="@{|/discuss/detail/${map.post.id}|}" th:utext="${map.post.title}">备战<em>春招</em>,面试刷题跟他复习,一个月全搞定!</a></h6><div class="mb-3" th:utext="${map.post.content}">金三银四的金三已经到了,你还沉浸在过年的喜悦中吗? 如果是,那我要让你清醒一下了:目前大部分公司已经开启了内推,正式网申也将在3月份陆续开始,金三银四,<em>春招</em>的求职黄金时期已经来啦!!! 再不准备,作为19应届生的你可能就找不到工作了。。。作为20届实习生的你可能就找不到实习了。。。 现阶段时间紧,任务重,能做到短时间内快速提升的也就只有算法了, 那么算法要怎么复习?重点在哪里?常见笔试面试算法题型和解题思路以及最优代码是怎样的? 跟左程云老师学算法,不仅能解决以上所有问题,还能在短时间内得到最大程度的提升!!!</div><div class="text-muted font-size-12"><u class="mr-3" th:utext="${map.user.username}">寒江雪</u>发布于 <b th:text="${#dates.format(map.post.createTime,'yyyy-MM-dd HH:mm:ss')}">2019-04-15 15:32:18</b><ul class="d-inline float-right"><li class="d-inline ml-2">赞 <i th:text="${map.likeCount}">11</i></li><li class="d-inline ml-2">|</li><li class="d-inline ml-2">回复 <i th:text="${map.post.commentCount}">7</i></li></ul></div></div></li></ul><!-- 分页 --><nav class="mt-5" th:replace="index::pagination"><ul class="pagination justify-content-center"><li class="page-item"><a class="page-link" href="#">首页</a></li><li class="page-item disabled"><a class="page-link" href="#">上一页</a></li><li class="page-item active"><a class="page-link" href="#">1</a></li><li class="page-item"><a class="page-link" href="#">2</a></li><li class="page-item"><a class="page-link" href="#">3</a></li><li class="page-item"><a class="page-link" href="#">4</a></li><li class="page-item"><a class="page-link" href="#">5</a></li><li class="page-item"><a class="page-link" href="#">下一页</a></li><li class="page-item"><a class="page-link" href="#">末页</a></li></ul></nav></div></div><!-- 尾部 --><footer class="bg-dark" th:replace="index::footer"><div class="container"><div class="row"><!-- 二维码 --><div class="col-4 qrcode"><img src="https://uploadfiles.nowcoder.com/app/app_download.png" class="img-thumbnail" style="width:136px;" /></div><!-- 公司信息 --><div class="col-8 detail-info"><div class="row"><div class="col"><ul class="nav"><li class="nav-item"><a class="nav-link text-light" href="#">关于我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">加入我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">意见反馈</a></li><li class="nav-item"><a class="nav-link text-light" href="#">企业服务</a></li><li class="nav-item"><a class="nav-link text-light" href="#">联系我们</a></li><li class="nav-item"><a class="nav-link text-light" href="#">免责声明</a></li><li class="nav-item"><a class="nav-link text-light" href="#">友情链接</a></li></ul></div></div><div class="row"><div class="col"><ul class="nav btn-group-vertical company-info"><li class="nav-item text-white-50">公司地址:北京市朝阳区大屯路东金泉时代3-2708北京牛客科技有限公司</li><li class="nav-item text-white-50">联系方式:010-60728802(电话) admin@nowcoder.com</li><li class="nav-item text-white-50">牛客科技©2018 All rights reserved</li><li class="nav-item text-white-50">京ICP备14055008号-4 <img src="http://static.nowcoder.com/company/images/res/ghs.png" style="width:18px;" />京公网安备 11010502036488号</li></ul></div></div></div></div></div></footer>
</div><script src="https://code.jquery.com/jquery-3.3.1.min.js" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.7/umd/popper.min.js" crossorigin="anonymous"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/js/bootstrap.min.js" crossorigin="anonymous"></script>
<script th:src="@{/js/global.js}"></script>
</body>
</html>
3.4 测试
- 每次发帖和对帖子评论都会使用kafka异步更新es中对应的数据,我们直接新增一个帖子,然后搜索我们刚创建的帖子
仿牛客论坛项目(4)相关推荐
- 仿牛客论坛项目(下)
代码仓库:https://gitee.com/qiuyusy/community 仿牛客论坛项目(上) 仿牛客论坛项目 15.kafka 1.阻塞队列 2.Kafka入门 简介 术语解释 下载 配置 ...
- 仿牛客论坛项目(上)
代码仓库:https://gitee.com/qiuyusy/community 仿牛客论坛项目(下) 仿牛客论坛项目上 1. Spring 在测试类中使用Spring环境 @Primary的作用 @ ...
- 仿牛客论坛项目(3)
仿牛客论坛项目 一.阻塞队列 1.1 测试 二.kafka入门 2.1 kafka下载 2.2 测试 三.Spring整合kafka 3.1 引入依赖 3.2 修改配置文件 3.3 测试 四.发布系统 ...
- 仿牛客论坛项目(5)
仿牛客论坛项目 一.SpringSecurity入门案例 1.1 添加依赖 1.2 配置文件 1.3 工具类 CommunityUtil 1.4 配置类 SecurityConfig 1.5 实体类 ...
- 仿牛客论坛项目全面大总结
1.核心功能: - 发帖.评论.私信.转发: - 点赞.关注.通知.搜索: - 权限.统计.调度.监控: 2.核心技术: - Spring Boot.SSM - Redis.Kafka.Elastic ...
- 仿牛客论坛项目之修改用户头像
前言: 在项目最开始的时候,我们默认从牛客网的静态资源库中选择一张照片作为用户的头像,但在实际开发中,我们还要考虑用户可以自己设置头像. 思路: 上传文件(上传到硬盘服务器上 或者 上传到云服务器上, ...
- SpringBoot仿牛客论坛项目实战
Community 论坛项目 转载请附带原文链接: 1. 环境搭建与技术栈说明 1.0 项目架构图 1.1 技术要求 熟悉快速开发框架:SpringBoot2.3.x 整合 SpringMVC + M ...
- (仿牛客论坛项目)01 - 开发社区首页
文章目录 前言 1.做项目的步骤 2.开发社区首页功能分步 2.1 User 类 2.2 UserMapper 接口 2.3 UserMapper 映射文件 2.4 编写测试类 3.开发社区首页,显示 ...
- 仿牛客论坛项目部署总结
最新文章
- ps制作20种特效文字_ps技巧:给照片制作特效(刀光剑影)
- 学python语言用什么软件-Python是什么?学习Python用什么编译器?
- Google Guava Cache 移除监听器
- show()和exec()的区别
- MyEclipse Tomcat 超链接传参中文乱码
- 如何解决分布式系统中的“幽灵复现”?
- 注释里的诅咒:哪种语言遭受最多的咒骂?
- 华为内部存储转sd卡_华为tit al00怎么将手机存储内容转移到sd卡
- Android资料之-EditText中的inputType
- Ubuntu 第2章 基本命令和文件系统
- 同程旅行 IAST 实践
- display属性详解
- linux下 OOB 炸弹的制作
- 前端点击图片将跳出显示框显示图片
- 受用一生的高效 PyCharm 使用技巧 !
- java报错Error attempting to get column ‘XXX’ from result set. Cause: java.sql.怎么解决
- 如何选择数据拆分方法:不同数据拆分方法的优缺点及原因
- Linux命令----压缩解压缩
- Mac删除键的5种用法
- 虚拟机安装Linux系列教材 (二)- 关闭Hiper-V
热门文章
- 能忍到第5个不笑的是就是高人
- CQUPT Java平时作业02
- Java18线程安全
- 厉害了 全靠经典之作-Java编程思想,把你教的明明白白
- Python实现自动化给视频实时加字幕,软件已打包!
- 2023年浙江省职业院校技能大赛大数据技术与应用专业样题
- 计算机考试桌贴,Word2010邮件合并一页打印多条记录(考务数据、准考证号、桌贴、考试通知单)...
- 【课件制作软件】Focusky教程 | 关于FS字体的部分问题总汇以及解决办法
- 捷联惯导-坐标系-观测值补偿-对准-编排-时间更新-测量更新
- 曲线曲面的基本理论2之曲线曲面表示方法