码神之路网站所使用的博客,项目简单,需求明确,容易上手,非常适合做为练手级项目。
blog.mszlu.com
项目讲解说明:
- 提供前端工程,只需要实现后端接口即可项目以单体架构入手,先快速开发,不考虑项目优化,降低开发负担开发完成后,开始优化项目,提升编程思维能力比如页面静态化,缓存,云存储,日志等docker部署上线云服务器购买,域名购买,域名备案等
项目使用技术 :
springboot + mybatisplus+redis+mysql
1. 工程搭建前端的工程:
npm install npm run build npm run dev1.1 新建maven工程
导入依赖
1.2 配置4.0.0 com.mszlu blog-parent 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-parent 2.5.0 UTF-8 UTF-8 1.8 org.springframework.boot spring-boot-starter org.springframework.boot spring-boot-starter-logging org.springframework.boot spring-boot-starter-log4j2 org.springframework.boot spring-boot-starter-aop org.springframework.boot spring-boot-starter-mail org.springframework.boot spring-boot-starter-web org.springframework.boot spring-boot-starter-test test org.springframework.boot spring-boot-starter-data-redis com.alibaba fastjson 1.2.76 mysql mysql-connector-java org.springframework.boot spring-boot-configuration-processor true org.apache.commons commons-lang3 commons-collections commons-collections 3.2.2 com.baomidou mybatis-plus-boot-starter 3.4.3 org.projectlombok lombok joda-time joda-time 2.10.10 org.springframework.boot spring-boot-maven-plugin
在application.properties配置文件中配置数据库和Mybatisplus的配置
#server server.port= 8888 spring.application.name=mszlu_blog # datasource spring.datasource.url=jdbc:mysql://localhost:3306/blog?useUnicode=true&characterEncoding=UTF-8&serverTimeZone=UTC spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver #mybatis-plus mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl mybatis-plus.global-config.db-config.table-prefix=ms_
创建config包,在包下面创建MybatisPlusConfig类
@Configuration
//扫包,将此包下的接口生成代理实现类,并且注册到spring容器中
@MapperScan("com.mszlu.blog.dao")
public class MybatisPlusConfig {
//分页插件
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor());
return interceptor;
}
}
同样在config包下创建WebConfig类实现WebMvcConfigurer 接口,进行跨域配置
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addCorsMappings(CorsRegistry registry) {
//跨域配置,不可设置为*,不安全, 前后端分离项目,可能域名不一致
//本地测试 端口不一致 也算跨域
registry.addMapping("
private Long authorId;
private Long bodyId;
private Long categoryId;
private int weight = Article_Common;
private Long createDate;
}
创建作者的实体类
package com.mszlu.blog.dao.pojo;
import lombok.Data;
@Data
public class SysUser {
private Long id;
private String account;
private Integer admin;
private String avatar;
private Long createDate;
private Integer deleted;
private String email;
private Long lastLogin;
private String mobilePhoneNumber;
private String nickname;
private String password;
private String salt;
private String status;
}
创建标签的实体类
package com.mszlu.blog.dao.pojo;
import lombok.Data;
@Data
public class Tag {
private Long id;
private String avatar;
private String tagName;
}
2.2.2 Controller
//Json数据进行交互
@RestController
@RequestMapping("/articles")
public class ArticleController {
@Autowired
private ArticleService articleService;
@PostMapping
public result listArticle(@RequestBody PageParams pageParams) {
return articleService.listArticle(pageParams);
}
}
统一最后的结果
package com.mszlu.blog.vo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class result {
private boolean success;
private int code;
private String msg;
private Object data;
public static result success(Object data){
return new result(true,200,"success",data);
}
public static result fail(int code,String msg){
return new result(false,code,msg,null);
}
}
创建ArticleVo用于前端数据展示
@Data
public class ArticleVo {
private Long id;
private String title;
private String summary;
private int commentCounts;
private int viewCounts;
private int weight;
private String createDate;
private String author;
private ArticleBodyVo body;
private List tags;
private List categorys;
}
2.2.3 Service
建立文章的业务层接口
public interface ArticleService {
result listArticle(PageParams pageParams);
}
创建ArticleServiceImpl类实现service业务层接口
@Service
public class ArticleServiceImpl implements ArticleService {
@Autowired
private ArticleMapper articleMapper;
@Autowired
private TagService tagService;
@Autowired
private SysUserService sysUserService;
@Override
public result listArticle(PageParams pageParams) {
Page page = new Page<>(pageParams.getPage(),pageParams.getPageSize());
LambdaQueryWrapper queryWrapper = new LambdaQueryWrapper<>();
//是否置顶
//order by creat_time
queryWrapper.orderByDesc(Article::getWeight,Article::getCreateDate);
Page articlePage = articleMapper.selectPage(page, queryWrapper);
List records = articlePage.getRecords();
//不能直接返回
List articleVoList = copyList(records,true,true);
return result.success(articleVoList);
}
private List copyList(List records,boolean isTag,boolean isAuthor) {
List articleVoList = new ArrayList<>();
for (Article record : records) {
articleVoList.add(copy(record,isTag,isAuthor));
}
return articleVoList;
}
//把文章的字段数据传递给copy用于前端展示
private ArticleVo copy(Article article,boolean isTag,boolean isAuthor){
ArticleVo articleVo = new ArticleVo();
//进行对象之间属性的赋值
BeanUtils.copyProperties(article,articleVo);
articleVo.setCreateDate(new DateTime(article.getCreateDate()).toString("yyyy-mm-dd HH:mm"));
//并不是所有的接口都需要标签,作者信息
if(isTag){
Long articleId = article.getId();
List tagVos = tagService.findTagByArticleId(articleId);
articleVo.setTags(tagVos);
}
if(isAuthor){
Long authorId = article.getAuthorId();
SysUser user = sysUserService.findUserById(authorId);
articleVo.setAuthor(user.getNickname());
}
return articleVo;
}
}
创建用户的业务层接口
public interface SysUserService {
SysUser findUserById(Long id);
}
实现作者的业务层接口
@Service
public class SysUserServiceImpl implements SysUserService {
@Autowired
private SysUserMapper sysUserMapper;
@Override
public SysUser findUserById(Long id) {
SysUser sysUser = sysUserMapper.selectById(id);
//判断查询sysUser对象是否为空,如果为空则给昵称复制,避免空指针异常
if(sysUser == null){
sysUser = new SysUser();
sysUser.setNickname("码神之路");
}
return sysUser;
}
}
创建标签的业务层接口
public interface TagsService {
List findTagsByArticleId(Long id);
}
实现标签的业务层接口
@Service
public class TagsServiceImpl implements TagsService {
@Autowired
private TagMapper tagMapper;
public TagVo copy(Tag tag){
TagVo tagVo = new TagVo();
BeanUtils.copyProperties(tag,tagVo);
return tagVo;
}
public List copyList(List tagList){
List tagVoList = new ArrayList<>();
for (Tag tag : tagList) {
tagVoList.add(copy(tag));
}
return tagVoList;
}
@Override
public List findTagByArticleId(Long articleId) {
//myBatisPlus无法进行多表查询
List tags = tagMapper.findTagsByArticleId(articleId);
return copyList(tags);
}
}
2.2.4 Dao
文章的持久层接口
public interface ArticleMapper extends baseMapper{ }
标签的持久层接口
public interface TagMapper extends baseMapper{ List findTagsByArticleId(Long articleId); }
作者的持久层接口
public interface SysUserMapper extends baseMapper{ }
总的xml文件如下所示
2.2.5 测试 3. 首页-最热标签 3.1 接口说明id,avatar,tag_name as tagName
接口url:/tags/hot
请求方式:GET
请求参数:无
返回数据:
{
"success": true,
"code": 200,
"msg": "success",
"data": [
{
"id":1,
"tagName":"4444"
}
]
}
3.2 编码
3.2.1 Controller
先编写标签的业务层
@RestController
@RequestMapping("/tags")
public class TagController {
@Autowired
private TagService tagService;
@GetMapping("/hot")
private result hot(){
int limit = 6;
return tagService.hots(limit);
}
}
标签与前端交互的Vo
package com.mszlu.blog.vo;
import lombok.Data;
@Data
public class TagVo {
private Long id;
private String tagName;
}
3.2.2 Service
最热标签功能的业务层接口
public interface TagsService {
List hot(int limit);
}
业务层接口的实现
@Override
public result hots(int limit) {
List tagIds = tagMapper.findHotsTagByIds(limit);
if(CollectionUtils.isEmpty(tagIds)){
return result.success(Collections.emptyList());
}
//需求的是 tagId 和 tagName Tag对象
//select * from tag where id in (1,2,3) 所以要判断tagIds是否为空
List tagList = tagMapper.findTagsByTagsId(tagIds);
return result.success(tagList);
}
3.2.3 Dao
持久层接口的编写
public interface TagMapper extends baseMapper{ List findHotsTagByIds(int limit); List findTagsByTagsId(List tagIds); }
标签持久层接口对应的xml文件
3.2.4 测试id,avatar,tag_name as tagName select from ms_tag where id in #{tagId}



