所需资料
功能主体说明:
- 问题发布
- 列表展示
- 分页(mybatis-plus)
数据库新建表
用户表
CREATE TABLE `user` (
`fd_id` varchar(30) NOT NULL,
`fd_name` varchar(200) DEFAULT NULL,
`fd_token` varchar(36) DEFAULT NULL,
`gmt_create_time` mediumblob,
`gmt_alter_time` mediumblob,
`fd_account_id` varchar(50) DEFAULT NULL,
`avatar_url` varchar(1000) DEFAULT NULL,
PRIMARY KEY (`fd_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
问题表
CREATE TABLE `question` (
`id` varchar(30) NOT NULL,
`title` varchar(100) DEFAULT NULL,
`description` text,
`gmt_create` bigint(20) DEFAULT NULL,
`gmt_modified` bigint(20) DEFAULT NULL,
`creator` varchar(30) DEFAULT NULL,
`comment_count` int(11) DEFAULT '0',
`view_count` int(11) DEFAULT '0',
`like_count` int(11) DEFAULT '0',
`tag` varchar(256) DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
新建User model对象
package com.majiang.community.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
/**
* @date 2022-04-20
*/
@Data
public class User {
@TableId(value = "fd_id",type = IdType.ID_WORKER_STR)
private String fdId;
/**
* 姓名
*/
private String fdName;
/**
* 账号id
*/
private String fdAccountId;
/**
* 授权标识
*/
private String fdToken;
/**
* 图片地址
*/
private String avatarUrl;
/**
* 创建时间
*/
private Long gmtCreateTime;
/**
* 修改时间
*/
private Long gmtAlterTime;
}
新建Question 对象
package com.majiang.community.model;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import lombok.Data;
/**
* @date 2022-04-21
*/
@Data
public class Question {
@TableId(value = "id",type = IdType.ID_WORKER_STR)
private String id;
/**
* 标题
*/
private String title;
/**
* 描述
*/
private String description;
/**
* 创建时间
*/
private Long gmtCreate;
/**
* 修改时间
*/
private Long gmtModified;
/**
* 创建人
*/
private String creator;
/**
* 评论量
*/
private Integer commentCount;
/**
* 查看量
*/
private Integer viewCount;
/**
* 收藏量
*/
private Integer likeCount;
/**
* 标签
*/
private String tag;
}
QuestionDTO
package com.majiang.community.dto;
import com.majiang.community.model.User;
import lombok.Data;
/**
* @date 2022-04-22
*/
@Data
public class QuestionDTO {
private String id;
private String title;
private String description;
private Long gmtCreate;
private Long gmtModified;
private String creator;
private Integer commentCount;
private Integer viewCount;
private Integer likeCount;
private String tag;
private User user;
}
**UserMapper **
package com.majiang.community.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.majiang.community.model.User;
public interface UserMapper extends BaseMapper<User> {
}
***QuestionMapper ***
package com.majiang.community.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.majiang.community.model.Question;
public interface QuestionMapper extends BaseMapper<Question> {
}
问题发布 PublishController
package com.majiang.community.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.majiang.community.mapper.QuestionMapper;
import com.majiang.community.mapper.UserMapper;
import com.majiang.community.model.Question;
import com.majiang.community.model.User;
import com.majiang.community.util.CookieUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
/**
* @date 2022-04-21
*/
@Controller
public class PublishController {
@Autowired
private CookieUtil cookieUtil;
@Autowired(required = false)
private QuestionMapper questionMapper;
@Autowired(required = false)
private UserMapper userMapper;
@GetMapping("/publish")
public String createQuestion(HttpServletRequest request){
String cookieValue = cookieUtil.getCookieValue(request);
if(StrUtil.isEmpty(cookieValue)){
return "index";
}
return "publish";
}
@PostMapping("/publish")
public String saveQuestion(@RequestParam(name = "title")String title,
@RequestParam(name="description")String description,
@RequestParam(name = "tag")String tag,
HttpServletRequest request,
Model model){
model.addAttribute("title", title);
model.addAttribute("description", description);
model.addAttribute("tag", tag);
if(StrUtil.isEmpty(title)){
model.addAttribute("error", "标题不能为空");
return "publish";
}
if(StrUtil.isEmpty(description)){
model.addAttribute("error", "问题描述不能为空");
return "publish";
}
if(StrUtil.isEmpty(tag)){
model.addAttribute("error", "标签不能为空");
return "publish";
}
String cookieValue = cookieUtil.getCookieValue(request);
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.eq("fd_token", cookieValue);
User user = userMapper.selectOne(queryWrapper);
Question question=new Question();
question.setTitle(title);
question.setDescription(description);
question.setTag(tag);
question.setCreator(user.getFdAccountId());
question.setGmtCreate(System.currentTimeMillis());
question.setGmtModified(System.currentTimeMillis());
questionMapper.insert(question);
return "redirect:/";
}
}
问题添加成功回调首页
在首页显示问题列表
package com.majiang.community.controller;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.majiang.community.dto.QuestionDTO;
import com.majiang.community.mapper.UserMapper;
import com.majiang.community.model.User;
import com.majiang.community.service.IQuestionService;
import com.majiang.community.util.CookieUtil;
import com.majiang.community.util.PageUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import java.util.Date;
import java.util.List;
@Controller
public class IndexController {
@Autowired(required = false)
private UserMapper userMapper;
@Autowired
private CookieUtil cookieUtil;
@Autowired
IQuestionService questionService;
@Value("${gitee.client.id}")
private String client_id;
@Value("${gitee.redirect_uri}")
private String redirect_uri;
@GetMapping("/")
public String index(@RequestParam(name = "current",defaultValue = "0")long current,
@RequestParam(name="size",defaultValue = "3")long size, HttpServletRequest request, Model model){
String cookieValue = cookieUtil.getCookieValue(request);
if(StrUtil.isNotEmpty(cookieValue)){
QueryWrapper queryWrapper=new QueryWrapper();
queryWrapper.eq("fd_token", cookieValue);
User user = userMapper.selectOne(queryWrapper);
request.getSession().setAttribute("user", user);
}else{
request.getSession().setAttribute("user", null);
}
model.addAttribute("client_id", client_id);
model.addAttribute("redirect_uri",redirect_uri);
PageUtils page = questionService.getQuestionPage(current,size);
model.addAttribute("questionPageList",page);
//未进行分页
/*List<QuestionDTO> questionDTOList = questionService.getQuestionList();
System.out.println(questionDTOList.size());
model.addAttribute("questionDTOList",questionDTOList);*/
return "index";
}
}
引入分页插件
package com.majiang.community.config;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.transaction.annotation.EnableTransactionManagement;
/**
* @date 2022-04-26
*/
@Configuration
@EnableTransactionManagement //开启事务
@MapperScan("com.majiang.community.mapper")
public class MyBatisConfig {
//引入分页插件
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
// 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求 默认false
paginationInterceptor.setOverflow(true);
// 设置最大单页限制数量,默认 500 条,-1 不受限制
paginationInterceptor.setLimit(100);
return paginationInterceptor;
}
}
定义接口
package com.majiang.community.service;
import com.majiang.community.dto.QuestionDTO;
import com.majiang.community.util.PageUtils;
import java.util.List;
/**
* @date 2022-04-22
*/
public interface IQuestionService {
List<QuestionDTO> getQuestionList();
PageUtils getQuestionPage(long current, long size);
}
接口实现类
package com.majiang.community.service.spring;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.majiang.community.dto.QuestionDTO;
import com.majiang.community.mapper.QuestionMapper;
import com.majiang.community.mapper.UserMapper;
import com.majiang.community.model.Question;
import com.majiang.community.model.User;
import com.majiang.community.service.IQuestionService;
import com.majiang.community.util.PageUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @date 2022-04-22
*/
@Service
public class QuestionServiceImp extends ServiceImpl<QuestionMapper,Question> implements IQuestionService {
@Autowired(required = false)
QuestionMapper questionMapper;
@Autowired(required = false)
UserMapper userMapper;
@Override
public List<QuestionDTO> getQuestionList() {
List<QuestionDTO> questionDTOList=new ArrayList<>();
List<Question> questionList = questionMapper.selectList(null);
for(Question question:questionList){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("fd_account_id", question.getCreator());
User user = userMapper.selectOne(userQueryWrapper);
QuestionDTO questionDTO = new QuestionDTO();
BeanUtils.copyProperties(question, questionDTO);
questionDTO.setUser(user);
questionDTOList.add(questionDTO);
}
return questionDTOList;
}
@Override
public PageUtils getQuestionPage(long current, long size) {
Page page=new Page(current,size);
//查询出所有的问题列表
IPage iPage = questionMapper.selectPage(page, null);
List<Question> questionList= iPage.getRecords();
//
List<QuestionDTO> questionDTOList=new ArrayList<>();
for (Question question:questionList){
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();
userQueryWrapper.eq("fd_account_id", question.getCreator());
User user = userMapper.selectOne(userQueryWrapper);
QuestionDTO questionDTO = new QuestionDTO();
BeanUtils.copyProperties(question, questionDTO);
questionDTO.setUser(user);
questionDTOList.add(questionDTO);
}
iPage.setRecords(questionDTOList);
return new PageUtils(iPage);
}
}
分页工具类
package com.majiang.community.util;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.majiang.community.dto.QuestionDTO;
import com.majiang.community.model.Question;
import lombok.Data;
import org.springframework.beans.BeanUtils;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;
/**
* @date 2022-04-26
*/
@Data
public class PageUtils implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 总记录数
*/
private Integer totalCount;
/**
* 每页记录数
*/
private Integer pageSize;
/**
* 总页数
*/
private Integer totalPage;
/**
* 当前页数
*/
private Integer currPage;
/**
* 列表数据
*/
private List<QuestionDTO> list;
/**
*
*/
private List<Integer> pageNumber;
/**
* 分页
* @param list 列表数据
* @param totalCount 总记录数
* @param pageSize 每页记录数
* @param currPage 当前页数
*/
public PageUtils(List<QuestionDTO> list, int totalCount, int pageSize, int currPage) {
this.list = list;
this.totalCount = totalCount;
this.pageSize = pageSize;
this.currPage = currPage;
this.totalPage = (int) Math.ceil((double) totalCount / pageSize);
for (int i = 1; i <= 3; i++) {
if (currPage - i > 0) {
this.pageNumber.add(0, currPage - i);
}
if (currPage + i <= totalPage) {
this.pageNumber.add(currPage + i);
}
}
}
/**
* 分页
*/
public PageUtils(IPage<QuestionDTO> page) {
this.list = page.getRecords();
this.totalCount = (int) page.getTotal();
this.pageSize = (int) page.getSize();
this.currPage = (int) page.getCurrent();
this.totalPage = (int) page.getPages();
List<Integer> pageNumbers=new ArrayList();
pageNumbers.add(currPage);
for (int i = 1; i <= 3; i++) {
if (this.currPage - i > 0) {
pageNumbers.add(0, this.currPage - i);
}
if (this.currPage + i <= this.totalPage) {
pageNumbers.add(this.currPage + i);
}
}
this.pageNumber=pageNumbers;
}
}
定义一个模板 navigation.html
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<body>
<div class="container-fluid" th:fragment="navigation">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<button type="button" class="navbar-toggle collapsed" data-toggle="collapse"
data-target="#bs-example-navbar-collapse-1" aria-expanded="false">
<span class="sr-only">码匠社区</span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
<span class="icon-bar"></span>
</button>
<a class="navbar-brand" href="#">码匠社区</a>
</div>
<div class="collapse navbar-collapse" id="bs-example-navbar-collapse-1">
<form class="navbar-form navbar-left">
<div class="form-group">
<input type="text" class="form-control" placeholder="搜索问题">
</div>
<button type="submit" class="btn btn-default">搜索</button>
</form>
<ul class="nav navbar-nav navbar-right">
<li th:if="${session.user !=null}">
<a href="/publish" class="btn btn-default">发布</a>
</li>
<li class="dropdown" th:if="${session.user!=null}">
<a href="#" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true"
aria-expanded="false">
<span th:text="${session.user.getFdName()}"></span>
<span class="caret"></span>
</a>
<ul class="dropdown-menu">
<li><a href="#">消息中心</a></li>
<li><a href="#">个人信息</a></li>
<li><a href="#">退出登录</a></li>
</ul>
</li>
<li th:if="${session.user==null}"><a
th:href="@{'https://gitee.com/oauth/authorize'+'?client_id='+${client_id}+'&redirect_uri='+${redirect_uri}+'&response_type=code&state=1'}">登录</a>
</li>
</ul>
</div>
</div>
</body>
</html>
** 在index.html中引入模板 th:replace=“navigation::navigation”**
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>码匠问题社区</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<!--<script src="http://apps.bdimg.com/libs/jquery/1.9.1/jquery.min.js"></script>-->
<!--<script type="text/javascript" src="/js/jquery.js"></script>-->
<link rel="stylesheet" href="css/bootstrap.min.css"/>
<link rel="stylesheet" href="css/community.css"/>
<script src="/js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap-theme.min.css"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid" th:replace="navigation::navigation"></div>
</nav>
<div class="container-fluid main">
<div class="row">
<div class="col-md-9 col-md-12 col-sm-12 col-xs-12">
<h2><span class="glyphicon glyphicon-list"/>发现</h2>
<hr>
<div class="media" th:each="questionDTO :${questionPageList.getList()}">
<div class="media-left">
<a href="#">
<img class="img-rounded"
th:src="${questionDTO.user.getAvatarUrl()}">
</a>
</div>
<div class="media-body">
<h4 class="media-heading" th:text="${questionDTO.getTitle()}"></h4>
<span th:text="${questionDTO.tag}"></span><br>
<span class="text-desc">
• <span th:text="${questionDTO.getUser().getFdName()}"></span>发表了文章
• <span th:text="${questionDTO.getCommentCount()}"></span> 个评论
• <span th:text="${questionDTO.getLikeCount()}"></span> 次浏览
•<span th:text="${#dates.format(questionDTO.getGmtCreate(),'yyyy-MM-dd')}"></span>发布
</span>
</div>
</div>
<nav aria-label="Page navigation">
<ul class="pagination">
<li th:if="${questionPageList.getCurrPage()!=1}">
<a th:href="@{/(current=1,size=${questionPageList.getPageSize()})}" aria-label="Previous">
<span aria-hidden="true"><<</span><!--首页-->
</a>
</li>
<li th:if="${questionPageList.getCurrPage()!=1}">
<a th:href="@{/(current=${questionPageList.getCurrPage()-1},size=${questionPageList.getPageSize()})}" aria-label="Previous">
<span aria-hidden="true"><</span><!--上一页-->
</a>
</li>
<li th:each="page:${questionPageList.getPageNumber()}" th:class="${questionPageList.getCurrPage()==page}?'active' : ''">
<a th:href="@{/(current=${page},size=${questionPageList.getPageSize()})}" th:text="${page}"></a>
</li>
<li th:if="${questionPageList.getCurrPage()!=questionPageList.getTotalPage()}">
<a th:href="@{/(current=${questionPageList.getCurrPage()+1},size=${questionPageList.getPageSize()})}" aria-label="Previous">
<span aria-hidden="true">></span><!--下一页-->
</a>
</li>
<li th:if="${questionPageList.getCurrPage()!=questionPageList.getTotalPage()}">
<a th:href="@{/(current=${questionPageList.getTotalPage()},size=${questionPageList.getPageSize()})}" aria-label="Previous">
<span aria-hidden="true">>></span><!--尾页-->
</a>
</li>
</ul>
</nav>
</div>
<div class="col-md-3 col-md-12 col-sm-12 col-xs-12">
<h3>问题发起指南</h3>
• 问题标题: 请用精简的语言描述您发布的问题,不超过25字 <br>
• 问题补充: 详细补充您的问题内容,并确保问题描述清晰直观, 并提供一些相关的资料<br>
• 选择标签: 选择一个或者多个合适的标签,用逗号隔开,每个标签不超过10个字<br>
</div>
</div>
</div>
</body>
</html>
问题发布页面
<!DOCTYPE HTML>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>码匠问题社区</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" href="css/bootstrap.min.css" />
<link rel="stylesheet" href="css/community.css">
<script src="/js/jquery.min.js"></script>
<script src="js/bootstrap.min.js"></script>
<link rel="stylesheet" href="css/bootstrap-theme.min.css"/>
</head>
<body>
<nav class="navbar navbar-default">
<div class="container-fluid" th:replace="navigation::navigation"></div>
</nav>
<div class="container-fluid main">
<div class="row">
<div class="col-md-9 col-md-12 col-sm-12 col-xs-12">
<h2><span class="glyphicon glyphicon-plus"/>发起</h2>
<hr>
<form action="/publish" method="post">
<input type="hidden" name="id" th:value="${id}">
<div class="form-group">
<label for="title">问题标题(简单扼要):</label>
<input type="text" class="form-control" th:value="${title}" id="title" name="title"
placeholder="问题标题……"
autocomplete="off">
</div>
<div class="form-group" id="question-editor">
<label for="description">问题补充 (必填,请参照右侧提示):</label>
<textarea name="description" id="description" th:text="${description}"
class="form-control"
cols="30"
rows="10"></textarea>
</div>
<div class="form-group">
<label for="title">添加标签:</label>
<input type="text" class="form-control" th:value="${tag}" id="tag" name="tag"
placeholder="问题标题……">
</div>
<div class="container-fluid main ">
<div class="row">
<div class="col-lg-9 col-md-12 col-sm-12 col-xs-12">
<div class="alert alert-danger col-lg-12 col-md-12 col-sm-12 col-xs-12"
th:text="${error}"
th:if="${error != null}">
</div>
</div>
<div class="col-lg-3 col-md-12 col-sm-12 col-xs-12">
<button type="submit" class="btn btn-success btn-publish ">
发布
</button>
</div>
</div>
</div>
</form>
</div>
<div class="col-md-3 col-md-12 col-sm-12 col-xs-12">
<h3>问题发起指南</h3>
• 问题标题: 请用精简的语言描述您发布的问题,不超过25字 <br>
• 问题补充: 详细补充您的问题内容,并确保问题描述清晰直观, 并提供一些相关的资料<br>
• 选择标签: 选择一个或者多个合适的标签,用逗号隔开,每个标签不超过10个字<br>
</div>
</div>
</div>
</body>
</html>