当前位置: 首页 > news >正文

论坛系统(中-2)

软件开发

实现业务功能

个人中心

页面结构介绍

个人中心的页面结构分为三部分

1> 导航栏

2> 正文部分

3> 页脚部分

index.html 的页面结构

1> 导航栏

2> 正文部分

3> 页脚部分

 获取用户信息

实现逻辑

⽤⼾提交请求,服务器根据是否传⼊Id参数决定返回哪个⽤⼾的详情

1. 不传⽤⼾Id,返回当前登录⽤⼾的详情 从session中获取

2. 传⼊⽤⼾Id,返回指定Id的⽤⼾详情 根据传入的ID从数据库中查询

参数要求
参数名描述类型默认值条件
id⽤⼾Idlong可以为空

接口规范

// 请求
GET /user/info HTTP/1.1
GET /user/info?id=1 HTTP/1.1
// 响应
HTTP/1.1 200
Content-type: applicatin/json

// 下面返回的是具体的User对象
{
"code": 0,
"message": "成功",
"data": {
"id": 25,
"username": "user223",
"nickname": "user223",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null,
"articleCount": 0,
"isAdmin": 0,
"state": 0,
"createTime": "2023-04-08 15:06:10",
"updateTime": "2023-04-08 15:06:10"
}
}

 编写后端代码
1. 在Mapper.xml中编写SQL语句

之前就生成好了


2. 在Mapper.java中定义方法

也是之前生成好的


3. 定义Service接口

在IUserService定义selectById⽅法,如下



4. 实现Service接口

 1> 非空校验

2> 调用DAO查询数据库并且获取对象
具体代码

    @Overridepublic User selectById(Long id) {// 1. 非空校验if (id == null) {// 打印日志// 参数校验失败log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 2. 调用DAO查询数据库并获取对象User user = userMapper.selectByPrimaryKey(id);// 返回结果return user;}
5. 单元测试

查询成功

6. Controller实现方法并对外提供API接口

步骤

1> 根据id的值来判断是通过那种方式来获取User对象

2> 如果id为空就从session中进行获取用户信息

3> 如果id不为空, 就用id去调用service通过id来查询用户信息

4> 判断返回的用户对象是不是空的

5> 返回正确的结果

具体代码

    @Operation(summary = "获取用户信息")@GetMapping("/info")public AppResult<User> getUserInfo(HttpServletRequest request,@Parameter(description = "用户id") @RequestParam(value = "id", required = false) Long id) {// 定义返回的User对象User user = null;// 根据Id的值判断User对象的获取if (id == null) {// 1. 如果id为空, 从session中获取当登录的信息HttpSession session = request.getSession(false);// 如果session没有,不会创建新的session// 判断session和用户信息是否有效if (session == null || session.getAttribute(AppConfig.USER_SESSION) == null) {// 用户没有登录, 直接返回错误信息return AppResult.failed(ResultCode.FAILED_FORBIDDEN);// 禁止访问}// 有效, 就从session中获取当前登录的用户信息user = (User) session.getAttribute(AppConfig.USER_SESSION);} else {// 2. 如果id不为空, 那么就从数据库中按照Id查询用户信息user = userService.selectById(id);}// 判断返回的用户对象是不是空的if (user == null) {return AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS);//用户不存在}// 返回正确的结果return AppResult.success(user);}
}

7. 测试API接口

分别测试登录和没登陆带id的情况

我们观察返回结果,发现我们的用户名, 密码, 创建跟新事件, 删除字段都返回了, 这个是不对的, 会造成信息泄露, 因此,我们需要进行处理

8. 修复返回值存在的缺陷

通过观察登录成功的返回结果发现,⽤⼾信息中的password, salt, deletState不应该返回给前

台,在User类中的对应属性上加@JsonIgnore(指定类中的属性不参与JSON字符串)注解,可以使对应的字段不参与JSON的序列化

序列化: JAVA对象--> JSON字符串

反序列化: JSON字符串-->JAVA对象

把@JsonIgnore过滤字段

对于日期, 我们需要去yml来进行设置正确的日期格式

修改⽇期格式为yyyy-MM-dd HH:mm:ss, 在application.yml中添加配置
# 在spring下加⼊⼦节点
spring:
# JSON序列化配置
jackson:
default-property-inclusion: NON_NULL # 不为null时序列化
然后我们在对应字段上加上@JsonFormate注解

 测试: 发现密码和盐过滤掉了, 然后创建和更新时间是正确的

但是上述的配置会影响我登录的返回结果, 我的data没有了, 此时我需要对统一返回结果进行强制序列化返回

 加上@JsonInclude注解, 设置不管什么情况都会进行序列化返回

再进行登录测试: 发现data成功显示

 

编写前端代码

根据后端的响应数据, 来显示在前端

编写ajax请求

//========================= 获取用户信息 =======================// 成功后,手动设置用户信息// $('#index_nav_avatar').css('background-image', 'url(' + user.avatarUrl + ')');$.ajax({// 请求的方法type: 'get',// 没有参数,表示获取当前登录用户的信息url: 'user/info',// 成功回调success: function (respData) {// 判断响应的状态码if (respData.code == 0) {// 设置页面上用户的信息let user = respData.data;// 判断用户头像是否有效if (!user.avatarUrl) {// 设置默认的头像地址user.avatarUrl = avatarUrl;}// 设置页面上的头像$('#index_nav_avatar').css('background-image', 'url(' + user.avatarUrl + ')');// 用户昵称$('#index_nav_nickname').html(user.nickname);// 设置用户组let subName = user.isAdmin == 1 ? '管理员' : '普通用户';$('#index_nav_name_sub').html(subName);currentUserId = user.id;} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},// 失败回调error: function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});

成功显示

 退出功能

实现流程

• 流程⾮常简单,具体的实现逻辑如下:

1. ⽤⼾访问退出接⼝

2. 服务器注销Session

3. 返回成功或失败

4. 如果返回成功浏览器跳转到相应⻚⾯

5. 结束

• 退出后跳转到哪个⻚⾯交给前端⾃⼰处理,建议跳转到登录⻚⾯。
 

接口规范

// 请求
GET http://127.0.0.1:58080/user/logout HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":null}

后端代码编写

销毁session对象并且解绑所有的用户数据

具体代码

 @PostMapping("/logout")@Operation(summary = "用户退出")public AppResult logout (HttpServletRequest request){// 获取session对象HttpSession session = request.getSession();if(session != null){// 打印日志log.info("退出成功");// 注销sessionsession.invalidate();}// 退出成功响应return AppResult.success();}
前端代码编写

我们需要对a标签绑定事件

编写ajax请求

        $('#index_user_logout').click(function () {$.ajax({type: 'get',url: 'user/logout',complete: function () {// 当前请求完成时,不论成功与失败都跳转到登录页面location.assign('/sign-in.html');}});});

 complete的解释

测试

配置拦截器

为什么要配置拦截器

为了达到封装复用的效果, 不每一个接口都进行判断session是否有效, 统一的校验工作抽取出来, 用拦截器对请求进行过滤(除了登录之外的的url都不准进行访问)

在interceptor包下创建LoginInterceptor

自定义登录拦截器

1> 类上实现HanlderInterceptor接口

2> 重写前置处理方法preHandle

3> 获取session对象, 并对session进行有效性验证

4> 验证不通过就跳转到登录页面, response.sendRedirect

package org.xiaobai.forum.interceptor;import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.servlet.http.HttpSession;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import org.xiaobai.forum.config.AppConfig;/*** 登录拦截器*/
@Component
public class LoginInterceptor implements HandlerInterceptor {@Value("${forum.login.url}")private String defaultURL;/***  前置处理(对请求进行预处理)* @param request* @param response* @param handler* @return true: 继续流程<br/> false: 流程终端* @throws Exception*/@Overridepublic boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {// 获取session 对象HttpSession session = request.getSession(false);// 获取不到session也不创建新的session// 判断session是否有效if (session != null && session.getAttribute(AppConfig.USER_SESSION) != null) {// 用户为已经登录, 校验通过return true;}// 校验url是否正确if (!defaultURL.startsWith("/")) {defaultURL = "/" + defaultURL;}// 校验不通过, 跳转到登录页面response.sendRedirect(defaultURL);// 终止流程return false;}
}

注意 

1> 对url进行有效性判断

2> 使用yml把目标页面放在配置文件中, 降低耦合性


修改application.yml配置⽂件,添加跳转页面
# 项⽬⾃定义相关配置
forum:login:url: sign-in.html # 未登录状况下强制跳转⻚⾯
在interceptor包下创建AppInterceptorConfigurer

步骤

1> 类上实现WebMvcConfigurer

2> 注入自定义的登录拦截器

3> 添加登录拦截器

4> 添加拦截的路径

具体代码

package org.xiaobai.forum.interceptor;import jakarta.annotation.Resource;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;/***  注册拦截器*  配置拦截规则**/
// 表示一个配置类, 注入到spring
@Configuration
public class AppInterceptorConfigurer implements WebMvcConfigurer {//注入自定义的登录拦截器@Resourceprivate LoginInterceptor loginInterceptor;// 配置拦截路径@Overridepublic void addInterceptors(InterceptorRegistry registry) { WebMvcConfigurer.super.addInterceptors(registry);// 添加登录拦截器// 添加登录拦截器registry.addInterceptor(loginInterceptor)       // 添加用户登录拦截器.addPathPatterns("/**")                 // 拦截所有请求.excludePathPatterns("/sign-in.html")   // 排除登录HTML.excludePathPatterns("/sign-up.html")   // 排除注册HTML.excludePathPatterns("/user/login")     // 排除登录api接口.excludePathPatterns("/user/register")  // 排除注册api接口.excludePathPatterns("/user/logout")    // 排除退出api接口.excludePathPatterns("/swagger*/**")    // 排除登录swagger下所有.excludePathPatterns("/v3*/**")         // 排除登录v3下所有,与swagger相关.excludePathPatterns("/dist/**")        // 排除所有静态文件.excludePathPatterns("/image/**").excludePathPatterns("/js/**").excludePathPatterns("/**.ico");}
}

获取在首页中显示的板块

实现方式

• 在⾸⻚显⽰的版块信息,可以通过以下两种⽅式解决

• ⽅法⼀:单独提供查询前N条记录的接⼝,⽤来控制⾸⻚中版块的个数(按照sort进行升序排序)

• ⽅法⼆:通过配置表中state字段来实现,提供查询所有版块的接⼝

• 两种⽅式任选⼀个都可以,项⽬中使⽤⽅法⼀

实现逻辑

1. 用户访问首页

2. 服务器查询有效的版块并按排序字段排序

3. 返回版块集合

没有参数要求

接口规范

// 请求
GET http://127.0.0.1:58080/board/topList HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": [
{
"id": 1,
"name": "Java",
"articleCount": 5,
"sort": 1,
"state": 0,
"createTime": "2023-01-14 11:02:18",
"updateTime": "2023-01-14 11:02:18"
},
{
"id": 2,
"name": "C++",
"articleCount": 1,
"sort": 2,
"state": 0,
"createTime": "2023-01-14 11:02:41",
"updateTime": "2023-01-14 11:02:41"
},
{
"id": 3,
"name": "前端技术",
"articleCount": 0,
"sort": 3,
"state": 0,
"createTime": "2023-01-14 11:02:52",
"updateTime": "2023-01-14 11:02:52"
}....}]}
 

后端代码实现
把板块信息写入
-- 写⼊版块信息数据
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (1, 'Java', 0, 1, 0, 0,
'2023-01-14 19:02:18', '2023-01-14 19:02:18');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (2, 'C++', 0, 2, 0, 0, '2023-
01-14 19:02:41', '2023-01-14 19:02:41');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (3, '前端技术', 0, 3, 0, 0,
'2023-01-14 19:02:52', '2023-01-14 19:02:52');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (4, 'MySQL', 0, 4, 0, 0,
'2023-01-14 19:03:02', '2023-01-14 19:03:02');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (5, '⾯试宝典', 0, 5, 0, 0,
'2023-01-14 19:03:24', '2023-01-14 19:03:24');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (6, '经验分享', 0, 6, 0, 0,
'2023-01-14 19:03:48', '2023-01-14 19:03:48');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (7, '招聘信息', 0, 7, 0, 0,
'2023-01-25 21:25:33', '2023-01-25 21:25:33');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (8, '福利待遇', 0, 8, 0, 0,
'2023-01-25 21:25:58', '2023-01-25 21:25:58');
INSERT INTO `t_board` (`id`, `name`, `articleCount`, `sort`, `state`,
`deleteState`, `createTime`, `updateTime`) VALUES (9, '灌⽔区', 0, 9, 0, 0,
'2023-01-25 21:26:12', '2023-01-25 21:26:12');

数据库显示插入成功

1. 在Mapper.xml中编写SQL语句

• 在src/main/resources/mapper/extension⽬录下新建 BoardExtMapper.xml( 注意命名空间和自动生成的.xml保持一致)

selectByNum 根据sort字段进行排序

编写mapper.xml 

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.xiaobai.forum.dao.BoardMapper"><select id="selectByNum" resultMap="BaseResultMap" parameterType="java.lang.Integer">select<include refid="Base_Column_List"/>from t_boardwhere state = 0 and deleteState = 0order by sort asclimit 0,#{num,jdbcType=INTEGER}</select>
</mapper>

2. 在Mapper.java中定义方法

•org.xiaobai.forum.dao包下的BoardMapper中添加⽅法声明


3. 定义Service接口

• 在IBoradService定义⽅法,如下:

package org.xiaobai.forum.service;import org.xiaobai.forum.model.Board;import java.util.List;public interface IBoradService {/***  查询n挑记录* @param num 要查询的条数* @return*/List<Board> selectByNum(Integer num);
}

4. 实现Service接口

• 在BoardServiceImpl中实现IBoardService中新增的⽅法

实现步骤

1> 注入BoardMapper

2> 对num进行非空校验

3> 调用DAO查询数据库中的数据

4> 返回结果

package org.xiaobai.forum.service.impl;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;
import org.xiaobai.forum.dao.BoardMapper;
import org.xiaobai.forum.exception.ApplicationException;
import org.xiaobai.forum.model.Board;
import org.xiaobai.forum.service.IBoradService;import java.util.List;@Slf4j
@Service
public class IBoardService implements IBoradService {// 注入boardMapper@Resourceprivate BoardMapper boardMapper;@Overridepublic List<Board> selectByNum(Integer num) {// 1. 进行非空校验if (num <= 0) {// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 抛出异常throw new ApplicationException(AppResult.failed((ResultCode.FAILED_PARAMS_VALIDATE)));// 参数校验异常}// 2. 调用DAO查询数据库中的数据List<Board> rs = boardMapper.selectByNum(num);// 3. 返回结果return rs;}
}

5. 单元测试

查询成功


6. Controller实现方法并对外提供API接口

• 在BoradController中提供对外的API接⼝

application.yml中添加配置

# 项⽬⾃定义相关配置(顶格写)

forum:

        index: # ⾸⻚配置节点

                board-num: 9 # ⾸⻚中显⽰的版块个数

实现步骤

1> 注入IBoardService 

2> 调用Service获取查询结果

3> 判断查询结果是否为空

4> 返回结果

具体代码

package org.xiaobai.forum.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
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 org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.model.Board;
import org.xiaobai.forum.service.IBoradService;import java.util.ArrayList;
import java.util.List;@Tag(name = "板块接口", description = "主页中展示有多少个板块")
@Slf4j
@RestController
@RequestMapping("/Borad")
public class BoradController {// 从配置文件中读取值, 如果没有配置, 默认值为9@Value("${form.index.board-num:9}")private Integer indexBoardNum;@Resourceprivate IBoradService boradService;@Operation(summary = "获取首页板块列表")@GetMapping("/topList")public AppResult<List<Board>> topList() {log.info("首页板块数为: " + indexBoardNum);// 调用Service 获取查询结果List<Board> boards = boradService.selectByNum(indexBoardNum);// 判断是否为空if (boards == null) {boards = new ArrayList<>();}// 返回结果return AppResult.success(boards);}
}

7. 测试API接口

在登录条件下访问该接口

前端代码实现 

根据后端响应值来构造板块信息

编写ajax

 $.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'});}});

测试: 成功显示

帖子操作 

发布帖子

实现逻辑

1. ⽤⼾点击发新帖按钮,进⼊发帖⻚⾯

2. 选择版块,填⼊标题、正⽂后提交服务器

3. 服务器校验信息并写⼊数据库(文章表的insert)

4. 更新⽤⼾发帖数与版块帖⼦数(更新用户表和板块表)

5. 返回结果

3. 4要用数据库的事务

参数要求
参数名描述类型默认值条件
boardId版块Idlong必须
title⽂章标题String必须
content帖⼦内容String必须
接口规范

// 请求
POST http://127.0.0.1:58080/article/create HTTP/1.1
Content-Type: application/x-www-form-urlencoded
boardId=1&title=%E6%B5%8B%E8%AF%95%E6%96%B0%E5%B8%96%E5%AD%90%E6%A0%87%E9%A2%98
&content=%E6%B5%8B%E8%AF%95%E6%96%B0%E5%B8%96%E5%AD%90%E5%86%85%E5%AE%B9%E6%B5%
8B%E8%AF%95%E6%96%B0%E5%B8%96%E5%AD%90%E5%86%85%E5%AE%B9
// 响应
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":null}

实现后端代码
1. 在Mapper.xml中编写SQL语句

2. 在Mapper.java中定义方法

关于文章表的insert操作, 已经自动生成, 可以直接使用

关于更新用户表和文章表 update. 在更新用户和板块的帖子数的时候, 可以使用动态更新的方法只设置Id和要更新的帖子数量即可


3. 定义Service接口

 对于update文章表在IArticleService定义⽅法

对于update用户表在IBoardService定义⽅法更新帖子数量

更具板块id查询板块信息

对于insert, 在IUserService定义⽅法


4. 实现Service接口

实现IBoardServiceImpl

实现步骤

1> 对id进行非空校验

2> 查询相应的板块

3> 更新帖子数量(此时要重新创建一个对象, 然后把要更新的字段进行设置

4> 调用DAO, 执行更新操作

5> 判断受影响行数

/*** 更新板块中帖子的数量** @param id 板块id*/@Overridepublic void addOneArticleCountById(Long id) {// 对id进行非空校验if (id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());// 更新帖子数失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_BOARD_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); // 调用mapper更新帖子// 判断受影响行数if (row != 1) {log.warn(ResultCode.FAILED.toString() + ", 受影响行数不等于1 .");throw new ApplicationException(AppResult.failed(ResultCode.FAILED));}}

 根据板块id查询板块信息

    @Overridepublic Board selectById(Long id) {// 对id进行非空校验if (id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());// 更新帖子数失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_BOARD_ARTICLE_COUNT));}Board board = boardMapper.selectByPrimaryKey(id);return board;}

动态更新需要重新创建对象 

 实现UserServiceImpl

步骤

1> 对id进行非空校验

2> 查询用户信息

3> 更新用户的发帖数量

4> 动态更新sql

5> 调用DAO, 执行更新操作

具体代码

 /*** 更新当前用户帖子数量* @param id 用户id*/@Overridepublic void addOneArticleCountById(Long id) {// 对id进行非空校验if (id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_BOARD_ARTICLE_COUNT.toString());// 更新帖子数失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_BOARD_ARTICLE_COUNT));}// 查询用户信息User user = userMapper.selectByPrimaryKey(id);if (user == null) {// 打印日志log.warn(ResultCode.ERROR_IS_NULL.toString());// 更新用户数失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.ERROR_IS_NULL));}// 更新用户的发帖数量User updateUser = new User();// 动态更新sql, 只需要把更新的字段进行设置即可updateUser.setId(user.getId());updateUser.setArticleCount(user.getArticleCount() + 1);// 调用DAO, 执行更新操作int row = userMapper.updateByPrimaryKeySelective(updateUser);if (row != 1) {log.warn(ResultCode.FAILED.toString() + ", 受影响行数不等于1 .");throw new ApplicationException(AppResult.failed(ResultCode.FAILED));}}

实现ArticleServiceImpl
步骤

1> 把和新增文章相关的mapper注入, 把和更新用户表和板块表的文章数的service进行注入

2> 对传入的Article 进行非空校验

3> 设置文章的默认值(访问数, 回复数, 点赞数...)

4> 把设置好默认值的article插入到数据库

5> 根据article的用户id获取用户的信息

6> 对用户进行非空校验(是否找到指定的用户)

7> 如果找到了, 那就更新用户的发帖数目

8> 根据article的板块id获取板块的信息

9> 对板块进行非空校验(是否找到指定的板块)

10> 如果找到了, 那就更新板块中的发帖数

具体代码

package org.xiaobai.forum.service.impl;import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;
import org.xiaobai.forum.dao.ArticleMapper;
import org.xiaobai.forum.exception.ApplicationException;
import org.xiaobai.forum.model.Article;
import org.xiaobai.forum.model.Board;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.IArticleService;
import org.xiaobai.forum.service.IBoardService;
import org.xiaobai.forum.service.IUserService;
import org.xiaobai.forum.utils.StringUtil;import java.util.Date;@Slf4j
@Service
public class ArticleServiceImpl implements IArticleService {// 注入mapper, 注意service之间注入不能成环@Resourceprivate ArticleMapper articleMapper;@Resourceprivate IUserService userService;@Resourceprivate IBoardService boardService;@Overridepublic void create(Article article) {// 进行非空校验if (article == null || article.getUserId() == null || article.getBoardId() == null|| StringUtil.isEmpty(article.getTitle())|| StringUtil.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.getUserId());// 新增失败throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));}// 更新板块中的帖子数量boardService.addOneArticleCountById(board.getId());// 打印日志log.info(ResultCode.SUCCESS.toString() + ", user id = " + article.getUserId()+ ", board if = " + article.getBoardId() + ", article id = " + article.getId() + "发帖成功");}
}

注意: service里不能出现service之间的循环调用


5. 单元测试

发现用户表里面的文章数量,板块表里面的文章数量都增加了

article表里面的数据增加了

同时用户表和板块表里面的文章数也增加了

测试代码

    @Resourceprivate IArticleService articleService;@Testvoid create() {Article article = new Article();article.setUserId(1L);// asdarticle.setBoardId(1L);// java板块article.setTitle("测试新增文章接口");article.setContent("文章的内容...");articleService.create(article);}

6. Controller实现方法并对外提供API接口

• 在 ArticleController 中提供对外的API接⼝
步骤

1> 根据session获取用户

2> 判断用户是否禁言

3> 对板块进行校验(通过板块id校验板块是不是存在)

4> 构造文章对象

5> 调用service, 进行插入操作(插入后文章, 板块,用户的文章数量都会增加,因为是一个事务的)

6> 响应

具体代码

package org.xiaobai.forum.controller;import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpSession;
import lombok.NonNull;
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 org.xiaobai.forum.common.AppResult;
import org.xiaobai.forum.common.ResultCode;
import org.xiaobai.forum.config.AppConfig;
import org.xiaobai.forum.model.Article;
import org.xiaobai.forum.model.Board;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.IArticleService;
import org.xiaobai.forum.service.IBoardService;@Tag(name = "文章接口", description = "对帖子进行发布,点赞,修改,删除操作...操作")
@Slf4j
@RestController
@RequestMapping("/article")
public class ArticleController {// 注入service@Resourceprivate IArticleService articleService;@Resourceprivate IBoardService boardService;@Operation(summary = "发布新帖")@PostMapping("/create")public AppResult create(HttpServletRequest request,@Parameter(description = "板块id") @RequestParam("boardId") @NonNull Long boardId,@Parameter(description = "文章标题") @RequestParam("title") @NonNull String title,@Parameter(description = "文章内容") @RequestParam("content") @NonNull String content) {// 获取用户HttpSession session = request.getSession(false);User user = (User) session.getAttribute(AppConfig.USER_SESSION);// 判断用户是否禁言if (user.getState() == 1) {// 用户已禁言return AppResult.failed(ResultCode.FAILED_USER_BANNED);// 禁言}// 板块校验Board board = boardService.selectById(boardId);if (board == null || board.getDeleteState() == 1 || board.getState() == 1) {// 打印日志log.warn(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());// 作者id// 调用ServicearticleService.create(article);// 响应return AppResult.success();}}

7. 测试API接口

插入成功

实现前端代码 

editor.md实现输入文章标题内容的设置

editor.md的使用

Editor.md - 开源在线 Markdown 编辑器

获取表单数据

ajax请求

 // 构造帖子对象let postData = {boardId : boardIdEl.val(),title : titleEl.val(),content : contentEl.val()};// 提交, 成功后调用changeNavActive($('#nav_board_index'));回到首页并加载帖子列表// contentType: 'application/x-www-form-urlencoded'$.ajax({type: 'post',url : 'article/create',contentType : 'application/x-www-form-urlencoded',data : postData,// 回调success : function (respData) {if (respData.code == 0) {// 提示信息$.toast({heading: '成功',text: '发帖成功',icon: 'success'});// 发布成功changeNavActive($('#nav_board_index'));} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}}, error : function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});

帖子列表

板块帖子列表

对应版块中显⽰的帖⼦列表以发布时间降序排列(点击不同的板块, 每个板块显示自己板块下所有的帖子)

不传⼊版块Id返回所有帖⼦

帖子列表中显示所有的帖子

实现逻辑

1. ⽤⼾点击某个版块或⾸⻚时,将版块Id做为参数向服务器发送请求

2. 服务器接收请求,并获取版块Id,查询对应版块下的所有帖⼦

3. 返回查询结果

参数要求
参数名描述类型默认值条件
boardId版块IdLong

可为空(根据传入的Board来执行不同的查询

selectAll(), selectByBoardId(Long boardId)

接口规范

// 请求
// 返回指定版块下的帖⼦列表
GET http://127.0.0.1:58080/article/getAllByBoardId?boardId=1 HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": [
{
"id": 17,
"boardId": 1,
"userId": 1,
"title": "测试删除",
"visitCount": 8,
"replyCount": 1,
"likeCount": 1,
"state": 0,
"createTime": "2023-07-05 04:10:46",
"updateTime": "2023-07-05 11:22:43",
"board": {
"id": 1,
"name": "Java"
},
"user": {
"id": 1,
"nickname": "bitboy",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null
},
"own": false
},
{
"id": 15,
"boardId": 1,
"userId": 2,
"title": "单元测试",
"visitCount": 11,
"replyCount": 0,
"likeCount": 6,
"state": 0,
"createTime": "2023-07-03 11:30:36",
"updateTime": "2023-07-04 10:31:00",
"board": {
"id": 1,
"name": "Java"
},
"user": {
"id": 2,
"nickname": "bitgirl",
"phoneNum": null,
"email": null,
"gender": 2,
"avatarUrl": null
},
"own": false
},
{
"id": 11,
"boardId": 1,
"userId": 1,
"title": "testtest222",
"visitCount": 4,
"replyCount": 0,
"likeCount": 0,
"state": 0,
"createTime": "2023-07-02 09:19:00",
"updateTime": "2023-07-02 09:19:00",
"board": {
"id": 1,
"name": "Java"
},
"user": {
"id": 1,
"nickname": "bitboy",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null
},
"own": false
},
{
"id": 10,
"boardId": 1,
"userId": 1,
"title": "测试⻚⾯发帖",
"visitCount": 1,
"replyCount": 1,
"likeCount": 0,
"state": 0,
"createTime": "2023-07-02 09:17:47",
"updateTime": "2023-07-05 10:51:43",
"board": {
"id": 1,
"name": "Java"
},
"user": {
"id": 1,
"nickname": "bitboy",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null
},
"own": false
},
{
"id": 1,
"boardId": 1,
"userId": 1,
"title": "单元测试",
"visitCount": 13,
"replyCount": 2,
"likeCount": 3,
"state": 0,
"createTime": "2023-07-02 06:46:32",
"updateTime": "2023-07-05 10:16:43",
"board": {
"id": 1,
"name": "Java"
},
"user": {
"id": 1,
"nickname": "bitboy",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null
},
"own": false}]}

显示首页所有列表的后端代码实现
分析sql

 结果必须包含作者信息, 帖子简要信息, 因此我们需要把俩个表关联起来

1. 在Mapper.xml中编写SQL语句

创建ArticleExtMapper.xml并写内容

当前这个结果集没有一个对应的ResultMap或者ResultType接收它, 所以要自定义一个拓展的ResultMap

不同map的关系分析

具体的实现(在Article实体类里面写User这个字段


2. 在Mapper.java中定义方法

在dao层定义方法


3. 定义Service接口

在 IArticleService 添加selectAll方法


4. 实现Service接口

在ArticleServiceImpl里面实现selectAll方法

直接查询mapper里面的数据,返回结果即可

    /*** 查询所有的帖子列表* @return*/@Overridepublic List<Article> selectAll() {// 调用DAOList<Article> articles = articleMapper.selectAll();// 返回结果return articles;}

注意, 为了防止我们的avatarUrl因为为null而被我们的设置的json序列化条件给过滤掉, 我们也要加上@JsonInclude,注解设置不管为不为null都要进行序列化


5. 单元测试

查询出来了所有的帖子信息


6. Controller实现方法并对外提供API接口

在ArticleController中提供对外的API接⼝

    /*** 通过查询板块id返回所有帖子信息* @param boardId* @return*/@Operation(summary = "获取帖子列表")@GetMapping("/getAllByBoardId")public AppResult<List<Article>> getAllByBoardId(@Parameter(description = "板块id") @RequestParam(value = "boardId",required = false)Long boardId){// 查询所有的帖子List<Article> articles = articleService.selectAll();if(articles == null){// 如果结果集为空, 那么就创建一个空集合articles = new ArrayList<>();}// 返回响应结果return AppResult.success(articles);}

注意: article==null, 此时article是"null"  articles = new ArrayList<>(); 里面的值是[] 


7. 测试API接口

查询成功

前端代码实现

article_list.html

编写ajax请求

$.ajax({type : 'get',url : 'article/getAllByBoardId' + queryString,// 回调success : function (respData) {if (respData.code == 0) {// 成功listBuildArticleList(respData.data);} else {// 失败$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error : function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});

实现成功, 但是我们要的是不同板块显示该板块的帖子和板块信息, 需要把上面的前后端都进行小改一下 

显示对应板块显示该板块下的所有帖子后端代码实现
1. 在Mapper.xml中编写SQL语句

在selectAll的基础上加上板块的条件

在AriticleExMapper.xml中添加下面代码

    <!--   根据板块ID查询所有未被删除的帖子列表, 不包含content--><select id="selectAllByBoardId" resultMap="AllInfoBaseResultMap" parameterType="java.lang.Long">selectu.id u_id,u.avatarUrl u_avatarUrl,u.nickname u_nickname,a.id,a.boardId,a.userId,a.title,a.visitCount,a.replyCount,a.likeCount,a.state,a.createTime,a.updateTimefrom t_article a,t_user uwhere a.userId = u.idand a.deleteState = 0and a.boardId = #{boardId,jdbcType=BIGINT}order by a.createTime desc;</select>

2. 在Mapper.java中定义方法

3. 定义Service接口

在IArticleService里面定义接口


4. 实现Service接口

在ArticleServiceImpl里面实现接口

实现步骤

1> 对boardId进行非空校验

2> 根据boardId查询板块信息

3> 对板块信息进行非空校验

4> 不为空就调用mapper, 通过boardId把所有和板块相关的帖子进行返回

具体代码

    /***  根据板块id查询所有的帖子列表* @param boardId 板块id* @return*/@Overridepublic List<Article> selectAllByBoardId(Long boardId) {// 非空校验if(boardId == null || boardId <=0){// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 参数校验失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 校验板块是否存在Board board = boardService.selectById(boardId);// 非空校验if(board == null){// 打印日志log.warn(ResultCode.FAILED_BOARD_NOT_EXISTS.toString()+", board id = "+boardId);// 抛出异常throw new ApplicationException(AppResult.failed((ResultCode.FAILED_BOARD_NOT_EXISTS)));}// 调用DAO, 进行查询List<Article> articles = articleMapper.selectAllByBoardId(boardId);// 返回结果return articles;}

5. 单元测试

成功返回了板块的文章信息


6. Controller实现方法并对外提供API接口

实现的具体步骤

1> 根据是否传入boardId来进行分情况讨论

用户如果传入boardId, 则根据boardId查询板块下的所有帖子

用户如果没有传入boardId, 查询的是所有帖子

2> 对帖子列表集合进行非空校验

3> 返回响应结果

代码

    /*** 通过查询板块id返回所有帖子信息* @param boardId* @return*/@Operation(summary = "获取帖子列表")@GetMapping("/getAllByBoardId")public AppResult<List<Article>> getAllByBoardId(@Parameter(description = "板块id") @RequestParam(value = "boardId",required = false)Long boardId){List<Article> articles;if(boardId == null){// 查询所有查询所有的帖子articles = articleService.selectAll();}else {// 有boardId就查询和boardId相关的帖子集合articles = articleService.selectAllByBoardId(boardId);}// 判断结果是否为空if(articles == null){// 如果结果集为空, 那么就创建一个空集合articles = new ArrayList<>();}// 返回响应结果return AppResult.success(articles);}

7. 测试API接口

成功返回指定的板块信息

编写前端代码

 article_list.html

后端是根据是否传入boardId来进行返回不同的板块信息, 现在的问题是怎么把boardId加入到url, 并且确定这个参数值

实现流程

ajax请求

成功显示

获取指定板块信息

实现逻辑

客⼾端发送请求传⼊版块Id,服务器响应对应版本的详情

参数要求
参数名描述类型默认值条件
id版块Idlong必须
接口规范

// 请求
GET http://127.0.0.1:58080/board/getById?id=1 HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": {
"id": 1,
"name": "Java",
"articleCount": 5,
"sort": 1,
"state": 0,
"createTime": "2023-01-14 11:02:18",
"updateTime": "2023-01-14 11:02:18"
}
}

后端代码实现
1. 在Mapper.xml中编写SQL语句

2. 在Mapper.java中定义方法

3. 定义Service接口

4. 实现Service接口

5. 单元测试

6. Controller实现方法并对外提供API接口

实现步骤

1> 直接调用service

2> 对查看结果进行校验

3> 返回结果

具体代码

    @Operation(summary = "获取板块信息")@GetMapping("/getById")public AppResult<Board> getById(@Parameter(description = "板块Id") @RequestParam("id") @NonNull Long id){// 因为id的非空校验已经由lombook给校验过了, 因此直接调用serviceBoard board = boradService.selectById(id);// 对查看结果进行校验// 表示板块记录在数据库中不存在, 或者已删除状态if(board == null || board.getDeleteState()==1){// 打印日志log.warn(ResultCode.FAILED_NOT_EXISTS.toString());// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_NOT_EXISTS));}// 返回结果return AppResult.success(board);}

7. 测试API接口

成功返回结果

前端代码实现

在相应的板块上添加帖子的数量

编写ajax

  $.ajax({type : 'get',url : 'board/getById?id=' + boardId,// 回调success : function (respData) {if(respData.code == 0) {let board = respData.data;// 成功时,更新页面的内容console.log(board)console.log(board.name)console.log(board.articleCount)$('#article_list_board_title').html(board.name); // 版块名$('#article_list_count_board').html('帖子数量: ' +board.articleCount);} else {// 失败$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error : function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});

实现结果

获取帖子详情

实现逻辑

1. ⽤⼾点击帖⼦,将帖⼦Id做为参数向服务器发送请求

2. 服务器查询帖⼦信息

3. 帖⼦访问次数加1

4. 返回查询结果

参数要求
参数名描述类型默认值条件
id帖⼦Idlong必须
接口规范

// 请求
GET http://127.0.0.1:58080/article/getById?id=1 HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": {
"id": 1,
"boardId": 1,
"userId": 1,
"title": "单元测试",
"visitCount": 14,
"replyCount": 2,
"likeCount": 3,
"state": 0,
"createTime": "2023-07-02 06:46:32",
"updateTime": "2023-07-05 10:16:43",
"content": "测试内容",
"board": {
"id": 1,D
"name": "Java"
},
"user": {
"id": 1,
"nickname": "bitboy",
"phoneNum": null,
"email": null,
"gender": 1,
"avatarUrl": null
},
"own": true
}
}

帖子详情后端代码编写
1. 在Mapper.xml中编写SQL语句

根据帖子Id查询帖子详情

在selectAll的基础上加入过滤条件, 帖子的id是用户点击的板块的帖子id,也要把板块也进行显示,就可以满足需求

ArticleExtmapper.xml

<!--    自定义结果集映射--><resultMap id="AllInfoBaseResultMap" type="org.xiaobai.forum.model.Article" extends="ResultMapWithBLOBs">
<!--    关联的用户的映射--><association property="user" resultMap="org.xiaobai.forum.dao.UserMapper.BaseResultMap" columnPrefix="u_"/>
<!--    关联板块的映射--><association property="board" resultMap="org.xiaobai.forum.dao.BoardMapper.BaseResultMap"columnPrefix="b_"></association></resultMap>    
<select id="selectById" resultMap="AllInfoBaseResultMap">SELECTu.id AS u_id,u.avatarUrl AS u_avatarUrl,u.nickname AS u_nickname,u.gender AS u_gender,u.isAdmin AS u_isAdmin,u.state AS u_state,u.deleteState AS u_deleteState,b.id AS b_id,b.name AS b_name,b.state As b_state,b.deleteState AS b_deleteState,a.id,a.boardId,a.userId,a.title,a.content,a.visitCount,a.replyCount,a.likeCount,a.state,a.createTime,a.updateTimeFROMt_article a,t_user u,t_board bWHEREa.userId = u.idAND a.deleteState = 0AND a.id = #{id,jdbcType=BIGINT}AND a.boardId = b.id</select>

注意:

1> 根据帖子id查询帖子详情

2> 补充要返回的结果, 并且要返回的信息包含板块, 因此要进行三表关联

3> 定义关联对象


2. 在Mapper.java中定义方法


3. 定义Service接口

为了避免歧义,修改一下xml的id名字, selectDetailById

在IArticleService里面添加方法


4. 实现Service接口

实现步骤

1> 对id进行非空校验

2> 调用DAO, 根据用户id查询返回文章信息

3> 创建一个新的帖子对象, 然后更新里面的访问次数

4> 把新的帖子对象更新到数据库

5> 更新原始返回对象的访问次数

6> 返回原始对象

具体代码

 /*** 根据帖子Id查询详情* @param id 帖子Id* @return 帖子详情*/@Overridepublic Article selectDetailById(Long id) {// 非空校验if (id == null || id <= 0) {// 打印日志log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 参数校验失败// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));}// 调用DAOArticle article = articleMapper.selectDetailById(id);// 判断结果是否为空if (article == null) {// 打印日志log.warn(ResultCode.FAILED_ARTICLE_NOT_EXISTS.toString());// 帖子不存在// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.FAILED_ARTICLE_NOT_EXISTS));}// 更新帖子的访问次数Article updateArticle = new Article();updateArticle.setId(article.getId());// 获取文章作者idupdateArticle.setVisitCount(article.getVisitCount() + 1);// 访问次数+1// 保存到数据库int row = articleMapper.updateByPrimaryKeySelective(updateArticle);if (row != 1) {// 打印日志log.warn(ResultCode.ERROR_SERVICES.toString());// 服务器内部错误// 抛出异常throw new ApplicationException(AppResult.failed(ResultCode.ERROR_SERVICES));}// 更新返回对象的访问次数article.setVisitCount(article.getVisitCount() + 1);// 返回帖子详情return article;}

5. 单元测试

返回了我们需要的信息


6. Controller实现方法并对外提供API接口

步骤

1> 调用Service, 获取帖子详情

2> 判断结果是否为空

3> 返回结果

具体代码

    @Operation(summary = "根据帖子Id获取详情")@GetMapping("/details")public AppResult<Article> getDetails(@Parameter(description = "帖子Id") @RequestParam("id") @NonNull Long id){// 调用Service, 获取帖子详情Article article = articleService.selectDetailById(id);// 判断结果是否为空if(article == null){// 返回错误信息return AppResult.failed(ResultCode.FAILED_ARTICLE_NOT_EXISTS);}// 返回结果return AppResult.success(article);}


7. 测试API接口

前端代码编写

点击帖子的标题就会来到帖子的详细界面

构造ajax请求

    $.ajax({type : 'get',url : 'article/details?id=' + currentArticle.id,// 回调success : function (respData) {if (respData.code == 0) {// 成功initArticleDetails(respData.data);} else {// 提示信息$.toast({heading: '警告',text: respData.message,icon: 'warning'});}},error : function () {// 提示信息$.toast({heading: '错误',text: '访问出现问题,请与管理员联系.',icon: 'error'});}});

帖子详情页面

帖子详情显示编辑与删除按钮

能否删除(显示删除按钮)的判断条件: 当前登录的用户与帖子作者是不是为同一个用户

有俩种方法: 前端/后端

后端代码编写

在Article类里面添加一个isOwn属性来判断是否是作者

controller里面添加是否当前的帖子的id和作者id一样

测试

编写前端

http://www.xdnf.cn/news/531415.html

相关文章:

  • 【HTML】【面试提问】HTML面试提问总结
  • 网球机器人自动捡球机械结构设计与创新研究
  • 如何git clone下来自定义文件名
  • Java设计模式之享元模式:从基础到高级的全面解析
  • Python集合
  • 第35周Zookkeeper+Dubbo 面试题精讲
  • RISC-V 开发板 MUSE Pi Pro PCIE 测试以及 fio 崩溃问题解决
  • Spring Boot 集成 druid,实现 SQL 监控
  • C语言实现android/linux按键模拟
  • 纸上流年:Linux基础IO的文件理解与操作
  • 本地部署dify+ragflow+deepseek ,结合小模型实现故障预测,并结合本地知识库和大模型给出维修建议
  • Node.js聊天室开发:从零到上线的完整指南
  • Httphelper: Http请求webapi小记
  • 达梦数据库对json字段进行操作
  • 【Git】分支管理
  • Go语言八股文之Mysql优化
  • 【Golang笔记02】函数、方法、泛型、接口学习笔记
  • AI在网络安全中的应用之钓鱼邮件检测
  • 网络安全-等级保护(等保) 2-7 GB/T 25058—2019 《信息安全技术 网络安全等级保护实施指南》-2019-08-30发布【现行】
  • Canvas设计图片编辑器全讲解(一)Canvas基础(万字图文讲解)
  • OSCP备战-pwnlab_init靶机详细步骤
  • 办公效率提升:一键合并多个Word文档的解决方案
  • 【聚类】K-means++
  • Spring Cloud初探之spring cloud gateway实现转发、鉴权及负载均衡(六)
  • spring中yml配置上下文与tomcat等外部容器不一致问题
  • Yocto和Buildroot功能和区别
  • 数据库连接池技术与 Druid 连接工具类实现
  • w~自动驾驶合集1
  • 腾讯云Mysql实现远程链接
  • idea2024 不知道安装了什么插件,界面都是中文的了,不习惯,怎么修改各个选项改回英文