目录
1.板块
1.1 思路
1.2 实现逻辑
1.3 参数要求
1.4 实现步骤
1.Mapper.xml
2.Mapper.java
3.Service接口
4.Service实现
5.单元测试
6.Controller
7.测试API
8.前后端交互
2.帖子
1.1思路编辑
1.2 参数要求
编辑
1.3 实现步骤
1.Mapper.xml
2.Mapper.java
3.定义Service接口
4.实现Service
5. 单元测试
6.Controller实现
7.测试API接口
8.前后端交互
1.板块
1.1 思路
1.2 实现逻辑
1.3 参数要求
1.4 实现步骤
1.Mapper.xml
2.Mapper.java
3.Service接口
package com.example.forum.services;import com.example.forum.model.Board;import java.util.List;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 15:45*/
public interface IBoardService {/*** 查询num条记录* @param num 要查询的条数* @return*/List<Board> selectByNum(Integer num);}
4.Service实现
package com.example.forum.services.impl;import com.example.forum.common.AppResult;
import com.example.forum.common.ResultCode;
import com.example.forum.dao.BoardMapper;
import com.example.forum.exception.ApplicationException;
import com.example.forum.model.Board;
import com.example.forum.services.IBoardService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.List;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 15:47*/
@Slf4j
@Service
public class IBoardServiceImpl implements IBoardService {@Resourceprivate BoardMapper boardMapper;/*** 查询num条记录** @param num 要查询的条数* @return*/@Overridepublic List<Board> selectByNum(Integer num) {// 非空校验if (num <= 0) {// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 调用DAO查询数据库中的数据List<Board> result = boardMapper.selectByNum(num);// 返回结果return result;}
}
5.单元测试
package com.example.forum.services.impl;import com.example.forum.model.Board;
import com.example.forum.services.IBoardService;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;import javax.annotation.Resource;import java.util.List;import static org.junit.jupiter.api.Assertions.*;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 16:28*/
@SpringBootTest
class IBoardServiceImplTest {@Resourceprivate IBoardService boardService;@Testvoid selectByNum() {List<Board> boards = boardService.selectByNum(1);if (boards != null) {System.out.println(boards);}}
}
6.Controller
package com.example.forum.controller;import com.example.forum.common.AppResult;
import com.example.forum.model.Board;
import com.example.forum.services.IBoardService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 16:38*/
@Api(tags="版块接口")
@Slf4j
@RestController
@RequestMapping("/board")
public class BoardContrller {// 从配置文件中读取值 如果没有配置 默认值为9@Value("${forum.index.board-num:9}")private Integer indexBoardNum;@Resourceprivate IBoardService boardService;/*** 查询首版本列表* @return*/@ApiOperation("获取首页板块列表")@GetMapping("/topList")public AppResult<List<Board>> topList() {log.info("首页板块个数为: " + indexBoardNum);// 调用Service查询结果List<Board> boards = boardService.selectByNum(indexBoardNum);// 判断是否为空if(boards == null) {boards = new ArrayList<>();}// 返回结果return AppResult.success(boards);}
}
7.测试API
8.前后端交互
// ========================= 获取版块信息 =======================// 成功后,调用buildTopBoard()方法,构建版块列表$.ajax({type: 'get',url: 'board/topList',success : function (respData) {if(respData.code == 0) {// 构建版块列表buildTopBoard(respData.data);} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error : function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});//========================= 构造首页版块=======================// 构造首页版块function buildTopBoard(data) {// 版块导航let navBoardListEl = $('#topBoardList');// 遍历版块data.forEach(board => {// 构建版块let itemHtml = '<li class="nav-item">'+ '<a class="nav-link" href="javascript:void(0);">'+ '<span class="nav-link-icon d-md-none d-lg-inline-block">'+ '<svg xmlns="http://www.w3.org/2000/svg" class="icon icon-tabler icon-tabler-point-filled" width="24"'+ 'height="24" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" fill="none"'+ 'stroke-linecap="round" stroke-linejoin="round">'+ '<path stroke="none" d="M0 0h24v24H0z" fill="none"></path>'+ '<path d="M12 7a5 5 0 1 1 -4.995 5.217l-.005 -.217l.005 -.217a5 5 0 0 1 4.995 -4.783z"'+ 'stroke-width="0" fill="currentColor"></path>'+ '</svg>'+ '</span>'+ '<span class="nav-link-title">'+ board.name+ '</span>'+ '</a>'+ '</li>'// 为版块绑定当前版块数据对象以便后续获取let boardItem = $(itemHtml);// 把版块信息绑定到当前导航单元boardItem.data('board', board);console.log('data = ' + boardItem.data('board').name + ', id = ' + boardItem.data('board').id);// 处理点击事件boardItem.click(function () {// alert('data = ' + boardItem.data('board').name + ', id = ' + boardItem.data('board').id);// 激活效果changeNavActive(boardItem);});// 加入版块导航navBoardListEl.append(boardItem);});}
2.帖子
1.1思路
1.2 参数要求
1.3 实现步骤
1.Mapper.xml
2.Mapper.java
3.定义Service接口
IUserService
/*** 更新当前用户的发帖数* @param id 用户 id*/void addOneArticleCountById(Long id);
IBoardService
/*** 根据板块数据查询板块信息* @param id 板块 id* @return*/Board selectById(Long id);/*** 更新当前板块的发帖数* @param id 板块 id*/void addOneArticleCountById(Long id);
IArticleService
package com.example.forum.services;import com.example.forum.model.Article;
import org.springframework.transaction.annotation.Transactional;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 19:20*/
public interface IArticleService {/*** 发布帖子* @param article 要发布的帖子*/@Transactional // 当前方法中的执行过程会被事务管理起来void create(Article article);
}
4.实现Service
UserServiceImpl
/*** 更新当前用户的发帖数** @param id 用户 id*/@Overridepublic void addOneArticleCountById(Long id) {if(id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_USER_ARTICLE_COUNT.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_ARTICLE_COUNT));}// 查询对应的板块User user = userMapper.selectByPrimaryKey(id);if(user == null) {// 打印日志log.warn(ResultCode.ERROR_IS_NULL.toString() + ", user id = " + id);// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));}// 更新帖子数量User updateUser = new User();updateUser.setId(user.getId());updateUser.setArticleCount(user.getArticleCount() + 1);int row = userMapper.updateByPrimaryKeySelective(updateUser);if (row != 1) {log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1");throw new ApplicationException(AppResult.failed(ResultCode.FAILED));}}
IBoardServiceImpl
/*** 根据板块ID查询板块信息** @param id 板块 id* @return*/@Overridepublic Board selectById(Long id) {if(id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_ARTICLE_COUNT));}// 调用DAO查询数据Board board = boardMapper.selectByPrimaryKey(id);// 返回结果return board;}/*** 更新当前板块的发帖数** @param id 板块 id*/@Overridepublic void addOneArticleCountById(Long id) {if(id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_USER_ARTICLE_COUNT.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_ARTICLE_COUNT));}// 查询对应的板块Board board = boardMapper.selectByPrimaryKey(id);if(board == null) {// 打印日志log.warn(ResultCode.ERROR_IS_NULL.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));}// 更新帖子数量Board updateBoard = new Board();updateBoard.setId(board.getId());updateBoard.setArticleCount(board.getArticleCount() + 1);// 调用DAO, 执行更新int row = boardMapper.updateByPrimaryKeySelective(updateBoard);if (row != 1) {log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1");throw new ApplicationException(AppResult.failed(ResultCode.FAILED));}}
ArticleServiceImpl
package com.example.forum.services.impl;import com.example.forum.common.AppResult;
import com.example.forum.common.ResultCode;
import com.example.forum.dao.ArticleMapper;
import com.example.forum.exception.ApplicationException;
import com.example.forum.model.Article;
import com.example.forum.model.Board;
import com.example.forum.model.User;
import com.example.forum.services.IArticleService;
import com.example.forum.services.IBoardService;
import com.example.forum.services.IUserService;
import com.example.forum.utils.StringUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;import javax.annotation.Resource;
import java.util.Date;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 19:20*/
@Slf4j
@Service
public class ArticleServiceImpl implements IArticleService {@Resourceprivate ArticleMapper articleMapper;// 用户和板块的操作@Resourceprivate IUserService userService;@Resourceprivate IBoardService boardService;/*** 发布帖子** @param article 要发布的帖子*/@Overridepublic void create(Article article) {// 非空校验if (article == null || article.getUserId() == null || article.getBoardId() == null|| StringUtils.isEmpty(article.getTitle())|| StringUtils.isEmpty(article.getContent())) {// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 设置默认值article.setVisitCount(0); // 访问数article.setReplyCount(0); // 回复数article.setLikeCount(0); // 点赞数article.setDeleteState((byte)0);article.setState((byte) 0);Date date = new Date();article.setCreateTime(date);article.setUpdateTime(date);// 写入数据库int articleRow = articleMapper.insertSelective(article);if(articleRow <= 0) {log.warn(ResultCode.FAILED_CREATE.toString());throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}// 获取用户信息User user = userService.selectById(article.getUserId());// 没有找到指定的用户信息if(user == null) {log.warn(ResultCode.FAILED_CREATE.toString() + ", 发帖失败 user id = " + article.getUserId());throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}// 更新用户的发帖数userService.addOneArticleCountById(user.getId());// 获取板块信息Board board = boardService.selectById(article.getBoardId());// 是否在数据库中有对应的版块if (board == null) {log.warn(ResultCode.FAILED_CREATE.toString() + ", 发帖失败 board id = " + article.getBoardId());throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}// 更新版块中的数量boardService.addOneArticleCountById(board.getId());// 打印日志log.info(ResultCode.SUCCESS.toString() + ", user id = " + article.getUserId()+ ", board id = " + article.getBoardId() + ", article id = " + article.getId() + "发帖成功");
// throw new ApplicationException(AppResult.failed("测试事务回滚"));}
}
5. 单元测试
6.Controller实现
package com.example.forum.controller;import com.example.forum.common.AppResult;
import com.example.forum.common.ResultCode;
import com.example.forum.config.AppConfig;
import com.example.forum.model.Article;
import com.example.forum.model.Board;
import com.example.forum.model.User;
import com.example.forum.services.IArticleService;
import com.example.forum.services.IBoardService;
import com.sun.istack.internal.NotNull;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;/*** Created with IntelliJ IDEA* Description* User: 王杰* Date: 2025-06-15* Time: 21:42*/
@Api(tags = "文章接口")
@Slf4j
@RestController
@RequestMapping("/article")
public class ArticleController {@Resourceprivate IBoardService boardService;@Resourceprivate IArticleService articleService;/*** 发布新贴* @param boardId 版块Id* @param title 文章标题* @param content 文章内容* @return*/@ApiOperation("发布新帖")@PostMapping("/create")public AppResult create(HttpServletRequest request,@ApiParam("版块Id") @RequestParam("boardId") @NotNull Long boardId,@ApiParam("文章标题") @RequestParam("title") @NotNull String title,@ApiParam("文章内容") @RequestParam("content") @NotNull String content) {// 校验用户是否禁言HttpSession session = request.getSession(false);User user = (User) session.getAttribute(AppConfig.USER_SESSION);if (user.getState() == 1) {// 用户已禁言return AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE);}// 版块的校验Board board = boardService.selectById(boardId.longValue());if(board == null || board.getDeleteState() == 1 || board.getState() == 1) {// 打印日志log.info(ResultCode.FAILED_BOARD_BANNED.toString());return AppResult.failed(ResultCode.FAILED_BOARD_BANNED);}// 封装文章对象Article article = new Article();article.setTitle(title); // 标题article.setContent(content); // 正文article.setBoardId(boardId); // 版块 idarticle.setUserId(user.getId()); // 作者 idarticleService.create(article);return AppResult.success();}
}
7.测试API接口
8.前后端交互
Editor.md - 开源在线 Markdown 编辑器