用户详情
获取用户信息
实现逻辑
⽤⼾提交请求,服务器根据是否传⼊Id参数决定返回哪个⽤⼾的详情
1. 不传⽤⼾Id,返回当前登录⽤⼾的详情(从session获取)
2. 传⼊⽤⼾Id,返回指定Id的⽤⼾详情(根据用户id去查)
俩种方式获得用户信息
参数要求
参数名 | 描述 | 类型 | 默认值 | 条件 |
id | ⽤⼾Id | long | 可以为空 |
接口规范
// 请求
GET /user/info HTTP/1.1
GET /user/info?id=1 HTTP/1.1
// 响应
HTTP/1.1 200
Content-type: applicatin/json
{
"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"}}
用户帖子列表
实现逻辑
对应版块中显⽰的帖⼦列表以发布时间降序排列不传⼊版块Id返回所有帖⼦
1. ⽤⼾点击某个版块或⾸⻚时,将版块Id做为参数向服务器发送请求
2. 服务器接收请求,并获取版块Id,查询对应版块下的所有帖⼦
3. 返回查询结果
参数要求
参数名 | 描述 | 类型 | 默认值 | 条件 |
boardId | 版块Id | Long | 可为空 |
接口规范
// 请求
// 返回指定版块下的帖⼦列表
GET http://127.0.0.1:58080/article/getAllByBoardId?boardId=1 HTTP/1.1(以传入的id为准)
// 返回所有的帖⼦列表
GET http://127.0.0.1:58080/article/getAllByBoardId HTTP/1.1 (从session中获取当前登录用户的Id)
// 响应
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
}}]
后端代码实现
1. 在Mapper.xml中编写SQL语句
需要定义一个根据用户Id查询的SQL
2. 在Mapper.java中定义方法
在ArticleMapper里面定义selectByUserId方法

3. 定义Service接口
在IArticleService里面定义selectByUserId接口

4. 实现Service接口
实现步骤
1> 对用户id进行非空校验
2> 调用service, 根据userId校验用户是否存在
3> 调用DAO根据用户id查询用户的帖子
具体代码
@Override
public List<Article> selectByUserId(Long userId) {
// 对userId进行非空校验
if (userId == null || userId <= 0) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 校验用户是否存在
User user = userService.selectById(userId);
if (user == null) {
// 打印日志
log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString() + ", user id = " + userId);
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
}
// 调用DAO
List<Article> articles = articleMapper.selectByUserId(userId);
// 返回结果
return articles;
}
5. 单元测试
返回帖子列表

6. Controller实现方法并对外提供API接口
实现步骤
1> 判断userId是否为空, 是空的就去获取登录用户的userId
2> 调用Service,通过userId查询对应的作者的帖子
具体代码
@Operation(summary = "获取用户的帖子列表")
@GetMapping("/getAllByUserId")
public AppResult<List<Article>> getAllByUserId(HttpServletRequest request,
@Parameter(description = "用户Id") @RequestParam(value = "userId",required = false) Long userId){
// 如果UserId为空, 那么就从sessionId获取当前登录的用户Id
if(userId == null){
// 获取Session
HttpSession session = request.getSession(false);
// 获取User对象
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
userId = user.getId();
}
// 调用Service
List<Article> articles = articleService.selectByUserId(userId);
// 返回结果
return AppResult.success(articles);
}
7. 测试API接口
不管传不传userId都能查询帖子信息
前端代码实现
构造ajax请求
实现结果
个人中心
修改个人信息
实现逻辑
只对⽤⼾的基本信息做修改,不包括密码与头像,修改密码与修改头像提供单独的修改接⼝
1. ⽤⼾打开找修改个⼈信息⻚⾯
2. 填⼊要修改的内容并提交服务器
3. 服务器获取登录⽤⼾的Id(从session中获取),并根据提交的内容修改数据
4. 返回成功或失败,如果成功返回更新后的个⼈信息
5. 浏览器刷新个⼈信息
参数要求
后端可以提供一个统一的接口, 更具用户提供参数来封装要修改的对象
参数名 | 描述 | 类型 | 默认值 | 条件 |
username | ⽤⼾名(⽤于登录) | String | 可选,唯⼀,校验是否存在 | |
nickname | 昵称 | String | 可选, | |
gender | 性别 | Byte | 可选,0⼥, 1男,2保密 | |
邮箱 | String | 可选 | ||
phoneNum | 电话 | String | 可选 | |
remark | 个⼈简介 | String | 可选 |
接口规范
// 请求
POST http://127.0.0.1:58080/user/modifyInfo HTTP/1.1
Content-Type: application/x-www-form-urlencoded
id=1&nickname=bitboy&gender=1&email=bitboy%40bit.com&phoneNum=18888888888&remar
k=%E6%88%91%E6%98%AF%E4%B8%80%E4%B8%AA%E4%BC%98%E7%A7%80%E7%9A%84%E5%A5%BD%E9%9
D%92%E5%B9%B4
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": {
"id": 1,
"username": "bitboy",
"nickname": "bitboy",
"phoneNum": "18888888888",
"email": "bitboy@bit.com",
"gender": 1,
"avatarUrl": null,
"articleCount": 1,
"isAdmin": 1,
"remark": "我是⼀个优秀的好⻘年",
"state": 0,
"createTime": "2023-06-24 11:59:15",
"updateTime": "2023-07-09 03:05:23"
}
}
后端代码编写
1. 在Mapper.xml中编写SQL语句
2. 在Mapper.java中定义方法
直接使用动态SQL,已经生成
3. 定义Service接口
IUserService定义modifyInfo接口

4. 实现Service接口
实现modifyInfo
实现步骤
1> 对用户进行非空校验
2> 根据用户id查询用户是否存在(不存在直接抛异常)
3> 定义一个标志位
4> 设置一个对象,用来动态更新sql的时候对修改的属性进行操作
5> 对用户名进行校验是否要更改(必须验证时唯一值)
6> 对其他参数校验是否要更改
7> geng'ju
具体代码
@Override
public void modifyInfo(User user) {
// 1. 非空校验
if (user == null || user.getId() == null || user.getId() <= 0) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 2. 根据用户id查询校验用户是否存在
User existsUser = userMapper.selectByPrimaryKey(user.getId());
// 如果查不到, 就直接抛异常
if (existsUser == null) {
// 打印日志
log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
}
// 3. 定义一个标志位
boolean checkAttr = false;// false表示没有校验通过
// 4. 定义一个专门用来更新的对象, 防止用户传入的User对象设置了其他属性
// 当使用动态SQL进行更新的时候, 覆盖了没有经过校验的字段
User updateUser = new User();
// 5. 设置用户Id
updateUser.setId(user.getId());
// 6. 对每一个参数进行校验并赋值
if (!StringUtil.isEmpty(user.getUsername())
&& !user.getUsername().equals(existsUser.getUsername())) {// 用户名不为空并且修改后的用户名和原先用户名不一样
// 更新用户名之前, 需要进行唯一性校验, 去表里面找有没有修改后的用户
User checkUser = userMapper.selectByUserName(user.getUsername());
if (checkUser != null) {
// 用户已存在
log.warn(ResultCode.FAILED_USER_EXISTS.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_EXISTS));
}
// 数据库中没有找到相应的用户, 表示可以修改用户名
updateUser.setUsername(user.getUsername());
// 更新标志位
checkAttr = true;
}
// 7. 校验昵称
if (!StringUtil.isEmpty(user.getNickname())
&& !user.getNickname().equals(existsUser.getNickname())) {
// 设置昵称
updateUser.setNickname(user.getNickname());
// 更新标志位
checkAttr = true;
}
// 8. 校验性别
if (user.getGender() != null && user.getGender() != existsUser.getGender()) {
// 设置性别
updateUser.setGender(user.getGender());
// 合法性校验
if (updateUser.getGender() > 2 || updateUser.getGender() < 0) {
updateUser.setGender((byte) 2);
}
// 更新标志位
checkAttr = true;
}
// 9. 校验邮箱
if (!StringUtil.isEmpty(user.getEmail())
&& !user.getEmail().equals(existsUser.getEmail())) {
// 设置邮箱
updateUser.setEmail(user.getEmail());
// 更新标志位
checkAttr = true;
}
// 9. 校验电话号码
if (!StringUtil.isEmpty(user.getPhoneNum())
&& !user.getPhoneNum().equals(existsUser.getPhoneNum())) {
// 设置电话号码
updateUser.setPhoneNum(user.getPhoneNum());
// 更新标志位
checkAttr = true;
}
// 10. 校验个人简介
if (!StringUtil.isEmpty(user.getRemark())
&& !user.getRemark().equals(existsUser.getRemark())) {
// 设置电话号码
updateUser.setRemark(user.getRemark());
// 更新标志位
checkAttr = true;
}
// 11. 根据标志位来决定是否可以执行更新
if (checkAttr == false) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 12. 调用DAO
int row = userMapper.updateByPrimaryKeySelective(updateUser);
if (row != 1) {
log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1 .");
throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
}
}
5. 单元测试
修改成功

6. Controller实现方法并对外提供API接口
实现步骤
1> 接受参数, 对参数进行非空校验
2> 从session中获取要修改的用户Id
3> 封装更新的User对象
4> 调用Service中的方法
5> 查询最新的用户信息
6> 把最新的用户信息设置到session中
7> 返回结果
具体代码
// 修改个人信息
@Operation(summary = "修改个人信息")
@GetMapping("/modifyInfo")
public AppResult modifyInfo(HttpServletRequest request,
@Parameter(description = "用户名") @RequestParam(value = "username", required = false) String username,
@Parameter(description = "昵称") @RequestParam(value = "nickname", required = false) String nickname,
@Parameter(description = "性别") @RequestParam(value = "gender", required = false) Byte gender,
@Parameter(description = "邮箱") @RequestParam(value = "email", required = false) String email,
@Parameter(description = "电话号") @RequestParam(value = "phoneNum", required = false) String phoneNum,
@Parameter(description = "个人简介") @RequestParam(value = "remark", required = false) String remark) {
// 1. 接受参数
// 2. 对参数做非空校验(全部都为空,就返回错误信息)
if (StringUtil.isEmpty(username) && StringUtil.isEmpty(nickname)
&& StringUtil.isEmpty(email) && StringUtil.isEmpty(phoneNum)
&& StringUtil.isEmpty(remark) && gender == null) {
// 返回错误信息
return AppResult.failed("请输入要修改的内容");
}
// 3. 从session中获取用户Id
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
// 4. 封装更新的User对象
User updateUser = new User();
updateUser.setId(user.getId()); // 用户Id
updateUser.setUsername(username); // 用户名
updateUser.setNickname(nickname); // 昵称
updateUser.setGender(gender); // 性别
updateUser.setEmail(email); // 邮箱
updateUser.setPhoneNum(phoneNum); // 电话
updateUser.setRemark(remark); // 个人简介
// 5. 调用Service中的方法
userService.modifyInfo(updateUser);
// 6. 查询最新的用户信息
user = userService.selectById(user.getId());
// 7. 把最新的用户信息设置到session中
session.setAttribute(AppConfig.USER_SESSION,user);
// 8. 返回结果
return AppResult.success(user);
}
7. 测试API接口
前端代码编写
编写ajax请求
最终的结果
修改密码
实现逻辑
• 为修改密码提供⼀个单独的接⼝及操作⻚⾯
1. ⽤⼾打开修改密码⻚⾯
2. 输⼊原密码、新密码、重复新密码并提交服务器
3. 服务器校验原密码是否正确
4. 原密码校验通过更新密码
5. 返回成功或失败
参数要求
参数名 | 描述 | 类型 | 默认值 | 条件 |
oldPassword | 原密码 | String | 必须 | |
newPassword | 新密码 | String | 必须 | |
passwordRepea t | 确认新密码 | String | 必须,与新密码相同 |
接口规范
// 请求
POST http://127.0.0.1:58080/user/modifyPwd HTTP/1.1
Content-Type: application/x-www-form-urlencoded
id=1&oldPassword=123456&newPassword=123456&passwordRepeat=123456
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": null
}
后端代码实现
1. 在Mapper.xml中编写SQL语句
2. 在Mapper.java中定义方法
1.2实现过
3. 定义Service接口
4. 实现Service接口
实现步骤
1> 对id进行非空校验
2> 根据id去数据库查询用户的信息, 并校验用户是否存在
3> 校验老密码是否正确(取出老盐, 然后和老密码进行md5加密)
4> 然后老密码和用户密码进行比对
5> 构造新的盐和新密码
6> 构造要更新的对象
7> 调用DAO
具体代码
/**
* 修改密码
* @param id 用户Id
* @param newPassword 新密码
* @param oldPassword 老密码
*/
@Override
public void modifyPassword(Long id, String newPassword, String oldPassword) {
// 对id进行非空校验
if (id == null || id <= 0 || StringUtil.isEmpty(newPassword) || StringUtil.isEmpty(oldPassword)) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 查询用户的信息
User user = userMapper.selectByPrimaryKey(id);
// 校验用户是否存在
if (user == null || user.getDeleteState() == 1) {
// 打印日志
log.warn(ResultCode.FAILED_USER_NOT_EXISTS.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_USER_NOT_EXISTS));
}
// 校验老密码是否正确
// 对老密码进行加密, 获取密文: 获取旧的盐, 然后让旧的盐和旧的密码拼接, 然后用md5进行加密
String oldEncryptPassword = MD5Util.md5Salt(oldPassword,user.getSalt());
// 和用户当前的密码进行比较
if(!oldEncryptPassword.equalsIgnoreCase(user.getPassword())){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 生成一个新的盐
String salt = UUIDUtil.UUID_32();
// 生成新密码的密文 ((明文)md5加密+扰动字符)md5加密
String encryptPassword = MD5Util.md5Salt(newPassword,salt);
// 构造要更新的对象
User updateUser = new User();
updateUser.setId(user.getId()); // 用户id
updateUser.setSalt(salt);// 新生成的盐
updateUser.setPassword(encryptPassword); // 新密码对应的密文
Date date = new Date();
updateUser.setUpdateTime(date); // 更新时间
// 调用DAO
int row = userMapper.updateByPrimaryKeySelective(updateUser);
if (row != 1) {
log.warn(ResultCode.FAILED.toString() + ", 受影响的行数不等于 1 .");
throw new ApplicationException(AppResult.failed(ResultCode.FAILED));
}
}
5. 单元测试

6. Controller实现方法并对外提供API接口
实现逻辑
1> 校验新密码和确认密码是否相同
2> 获取当前登录的用户信息
3> 调用Service
4> 销毁session 回到登录页面重新登录
5> 返回结果
具体代码
@Operation(summary = "修改密码")
@PostMapping("/modifyPwd")
public AppResult modifyPassword(HttpServletRequest request,
@Parameter(description = "原密码") @RequestParam("oldPassword")@NonNull String oldPassword,
@Parameter(description = "新密码") @RequestParam("newPassword")@NonNull String newPassword,
@Parameter(description = "确认密码") @RequestParam("passwordRepeat")@NonNull String repeatPassword){
// 1. 校验新密码和确认密码是否相同
if(!newPassword.equalsIgnoreCase(repeatPassword)){
// 返回错误描述
return AppResult.failed(ResultCode.FAILED_TWO_PWD_NOT_SAME);
}
// 2. 获取当前登录的用户信息
HttpSession session = request.getSession(false);
User user =(User) session.getAttribute(AppConfig.USER_SESSION);
// 3. 调用Service
userService.modifyPassword(user.getId(),newPassword,oldPassword);
// 4. 销毁session 回到登录页面重新登录
if (session != null) {
session.invalidate();
}
// 5. 返回结果
return AppResult.success();
}
7. 测试API接口
站内信
发送
实现逻辑
1. 在⽬录⽤⼾详情界⾯,点击发送站内信接钮,进⼊编辑⻚⾯
2. 编写站内信内容,点击发送按钮
3. 提⽰发送结果
参数要求
参数名 | 描述 | 类型 | 条件 | 默认值 |
receiveUserId | 接收⽤⼾Id | Long | 必须 | |
content | 站内信内容 | String | 必须 |
接口规范
// 请求
POST http://127.0.0.1:58080/message/send HTTP/1.1
Content-Type: application/x-www-form-urlencoded
receiveUserId=2&content=%E4%BD%A0%E5%A5%BD%E5%95%8A
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": null
}
后端代码实现
1. 在Mapper.xml中编写SQL语句
2. 在Mapper.java中定义方法
实现过了
3. 定义Service接口
4. 实现Service接口
实现逻辑
1> 对Message进行非空校验
2> 根据message的id查询接收者是否存在
3> 设置默认值
4> 设置创建时间和更新时间
5> 调用DAO,插入message
具体代码
package org.xiaobai.forum.service.impl;
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.MessageMapper;
import org.xiaobai.forum.exception.ApplicationException;
import org.xiaobai.forum.model.Message;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.IMessageService;
import org.xiaobai.forum.service.IUserService;
import org.xiaobai.forum.utils.StringUtil;
import javax.annotation.Resource;
import java.util.Date;
@Service
@Slf4j
public class MessageServiceImpl implements IMessageService {
@Resource
private MessageMapper messageMapper;
@Resource
private IUserService userService;
@Override
public void create(Message message) {
// 非空校验
if (message == null || message.getPostUserId() == null || message.getReceiveUserId() == null
|| StringUtil.isEmpty(message.getContent())) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 校验接收者是否存在
User user = userService.selectById(message.getReceiveUserId());
if (user == null || user.getDeleteState() == 1) {
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 设置默认值
message.setState((byte) 0);
message.setDeleteState((byte) 0);
// 设置创建与更新时间
Date date = new Date();
message.setCreateTime(date);
message.setUpdateTime(date);
// 调用DAO
int row = messageMapper.insertSelective(message);
if (row != 1) {
log.warn(ResultCode.FAILED_CREATE.toString());
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_CREATE));
}
}
}
5. 单元测试

6. Controller实现方法并对外提供API接口
实现逻辑
1> 获取当前登录的用户信息
2> 判断当前登录用户的状态, 如果是禁言是不能发送站内信
3> 判断发送人是不是作者(不能自己给自己发送站内信)
4> 根据接收者id校验接收者是否存在
5> 封装发送对象
6> 调用Service, 把message传进去
7> 返回结果
具体代码
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.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.Message;
import org.xiaobai.forum.model.User;
import org.xiaobai.forum.service.IMessageService;
import org.xiaobai.forum.service.IUserService;
import javax.annotation.Resource;
@Tag(name ="站内信接口",description = "给其他用户发送信息")
@RestController
@RequestMapping("/message")
@Slf4j
public class MessageController {
@Resource
private IMessageService messageService;
@Resource
private IUserService userService;
@Operation(summary = "发送站内信")
@PostMapping("/send")
public AppResult send(HttpServletRequest request,
@Parameter(description = "接收者Id")@RequestParam("receiveUserId") @NonNull Long receiveUserId,
@Parameter(description = "内容")@RequestParam("content")@NonNull String content){
// 获取当前登录的用户信息
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
// 1. 当前登录用户的状态, 如果是禁言状态是不能发送站内信
if(user.getState() == 1){
// 返回用户状态异常
return AppResult.failed(ResultCode.FAILED_USER_BANNED);
}
// 2. 不能给自己发送站内信
if(user.getId() == receiveUserId){
return AppResult.failed("不能给自己发送站内信");
}
// 3. 校验接收者是否存在
User receiveUser = userService.selectById(receiveUserId);
// 判断接收者状态是否正常
if (receiveUser == null || receiveUser.getDeleteState() == 1) {
// 返回接收者状态不正常
return AppResult.failed("接收者状态异常");
}
// 4. 封装发送对象
Message message = new Message();
message.setPostUserId(user.getId());// 发送者Id
message.setReceiveUserId(receiveUserId);// 接收者Id
message.setContent(content);// 站内信内容
// 5.调用Service
messageService.create(message);
// 6. 返回结果
return AppResult.success("发送成功");
}
}
7. 测试API接口
未读数
实现逻辑
查询当前登录⽤⼾的未读站内信数量(直接从session中获取用户的Id)
接口规范
// 请求
GET http://127.0.0.1.41:58080/message/getUnreadCount HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{"code":0,"message":"成功","data":1}
后端代码实现
1. 在Mapper.xml中编写SQL语句
根据用户Id查询该用户的未读数量 state = 0(0表示未读)
2. 在Mapper.java中定义方法

3. 定义Service接口

4. 实现Service接口
实现逻辑
1> 对receiverUserId进行参数非空校验
2> 直接调用DAO,通过receiverUserId查询该用户未读的信息数
3> 检查count
具体代码
/**
* 根据用户Id查询该用户的未读数
* @param receiveUserId 用户id
* @return 未读数
*/
@Override
public Integer selectUnreadCount(Long receiveUserId) {
// 参数非空校验
if(receiveUserId == null || receiveUserId <=0){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 直接调用DAO
Integer count = messageMapper.selectUnreadCount(receiveUserId);
// 检查count
if (count == null) {
log.warn(ResultCode.ERROR_SERVICES.toString());
throw new ApplicationException(AppResult.failed(ResultCode.ERROR_SERVICES));
}
// 返回结果
return count;
}
5. 单元测试

6. Controller实现方法并对外提供API接口
实现逻辑
1> 根据session获取当前登录的用户
2> 调用Service, 根据用户Id获取用户站内信未读数
3> 返回结果集
具体代码
@Operation(summary = "获取未读数")
@GetMapping("/getUnreadCount")
public AppResult<Integer> getUnreadCount(HttpServletRequest request){
// 1. 获取当前登录的用户
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
// 2. 调用Service
Integer count = messageService.selectUnreadCount(user.getId());// 当前登录的id就是接收者id
// 3. 返回结果
return AppResult.success(count);
}
7. 测试API接口
列表
实现逻辑
⽤⼾访问API,服务器响应当前登录⽤⼾的站内信
接口规范
// 请求
GET http://127.0.0.1:58080/message/getAll HTTP/1.1
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": [{
"id": 11,
"postUserId": 32,
"receiveUserId": 3,
"content": "真的可以发出去吗\n",
"state": 2,
"createTime": "2023-06-20 11:21:09",
"updateTime": "2023-06-25 11:24:38",
"postUser": {
"id": 32,
"nickname": "ljl",
"phoneNum": null,
"email": null,
"gender": 2,
"avatarUrl": null
}
}]}
后端代码实现
1. 在Mapper.xml中编写SQL语句


2. 在Mapper.java中定义方法

3. 定义Service接口

4. 实现Service接口
实现逻辑
1> 参数非空校验
2> 调用DAO
3> 返回结果
具体代码
@Override
public List<Message> selectByReceiveUserId(Long receiveUserId) {
// 参数非空校验
if(receiveUserId == null || receiveUserId <=0){
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 调用DAO
List<Message> messages = messageMapper.selectByReceiveUserId(receiveUserId);
// 返回结果
return messages;
}
5. 单元测试

6. Controller实现方法并对外提供API接口
具体逻辑
1> 获取当前登录的用户
2> 根据用户的id获取用户的站内信列表
3> 返回结果
代码编写
@Operation(summary = "查询用户的所有站内信")
@GetMapping("/getAll")
public AppResult<List<Message>> getAll(HttpServletRequest request){
// 1. 获取当前登录的用户
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
// 2. 调用Service
List<Message> messages = messageService.selectByReceiveUserId(user.getId());
// 3. 返回结果
return AppResult.success(messages);
}
7. 测试API接口
更新状态
实现逻辑
1. ⽤⼾点击站内信,显⽰详情⻚⾯
2. 更新未读状态的站内信为已读
参数要求
参数名 | 描述 | 类型 | 条件 | 默认值 |
id | 站内信Id | long | 必须 |
接口规范
// 请求
POST http://127.0.0.1:58080/message/markRead HTTP/1.1
Content-Type: application/x-www-form-urlencoAded
id=1
// 响应
HTTP/1.1 200
Content-Type: application/json
{"code": 0,
"message": "成功",
"data": null
}
后端代码实现
1. 在Mapper.xml中编写SQL语句
2. 在Mapper.java中定义方法
之前写了
3. 定义Service接口

4. 实现Service接口
实现逻辑
1> 对id, state进行校验
2> 动态更新, 构造更新对象
3> 调用DAO,更新是否已读
具体代码
@Override
public void updateStateById(Long id, Byte state) {
// 对id进行非空校验, state: 0 未读 1 已读 2 已回复
if (id == null || id <= 0 || state < 0 || state > 2) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());// 更新帖子数失败
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 构造更新对象
Message updateMessage = new Message();
updateMessage.setId(id);
updateMessage.setState(state);
Date date = new Date();
updateMessage.setUpdateTime(date);
// 调用DAO
int row = messageMapper.updateByPrimaryKeySelective(updateMessage);
if(row !=1){
log.warn(ResultCode.ERROR_SERVICES.toString());
throw new ApplicationException(AppResult.failed(ResultCode.ERROR_SERVICES));
}
}
5. 单元测试

6. Controller实现方法并对外提供API接口
实现逻辑
1> 根据Id查询站内信,并且查询站内信是否存在
2> 判断站内信是不是自己
3> 调用Service把id所对应的Message更新为已读状态
具体代码
@Operation(summary = "更新为已读")
@PostMapping("/markedRead")
public AppResult markedRead(HttpServletRequest request,
@Parameter(description = "站内信Id") @RequestParam("id") @NonNull Long id) {
//1. 根据Id查询站内信
Message message = messageService.selectById(id);
//2. 站内信是否存在
if (message == null || message.getDeleteState() == 1) {
// 返回错误信息
return AppResult.failed(ResultCode.FAILED_MESSAGE_NOT_EXISTS);
}
//3. 站内信是不是自己的
HttpSession session = request.getSession(false);
User user = (User) session.getAttribute(AppConfig.USER_SESSION);
// 判断站内信的收件人是不是当前的登录
if (user.getId() != message.getReceiveUserId()) {
// 返回错误信息
return AppResult.failed(ResultCode.FAILED_FORBIDDEN);
}
// 调用Service, 把状态更新为已读
messageService.updateStateById(id,(byte)1);
// 返回结果
return AppResult.success();
}
7. 测试API接口
回复
实现逻辑
1. ⽤⼾在站内信详情⻚⾯点击回复按钮,显⽰回复区
2. 填写回复内容并提交到服务器
3. ⽤⼾不能回复接收者不是⾃⼰的站内信
4. 站内信状态置为已回复(并且要在数据库中新增一条站内信记录, 俩个数据的修改操作在一个业务逻辑中, 因此要用事务进行管理)
参数要求
参数名 | 描述 | 类型 | 条件 | 默认值 |
repliedId(要回复的站内信id) | 站内信Id | Long | 必须 | |
content | 内容 | String | 必须 |
接口规范
// 请求
POST http://127.0.0.1:58080/message/reply HTTP/1.1
Content-Type: application/x-www-form-urlencoded
repliedId=1&receiveUserId=2&content=%E4%BD%A0%E5%A5%BD%E5%95%8A
// 响应
HTTP/1.1 200
Content-Type: application/json
{
"code": 0,
"message": "成功",
"data": null
}
后端代码实现
1. 在Mapper.xml中编写SQL语句
2. 在Mapper.java中定义方法
3. 定义Service接口

4. 实现Service接口
实现步骤
1> 对repliedId进行非空校验
2> 校验repliedId是不是存在
3> 更新状态为已读
4> 回复的内容写入数据库
具体代码
/**
* 回复站内信
* @param repliedId 要回复的站内信Id
* @param message 回复的对象
*/
@Override
public void reply(Long repliedId, Message message) {
// 对repliedId进行非空校验
if (repliedId == null || repliedId <= 0) {
// 打印日志
log.warn(ResultCode.FAILED_PARAMS_VALIDATE.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_PARAMS_VALIDATE));
}
// 校验repliedId是不是存在
Message existsMessage = messageMapper.selectByPrimaryKey(repliedId);
// 信息是否存在
if (existsMessage == null || existsMessage.getDeleteState() == 1) {
// 打印日志
log.warn(ResultCode.FAILED_MESSAGE_NOT_EXISTS.toString());
// 抛出异常
throw new ApplicationException(AppResult.failed(ResultCode.FAILED_MESSAGE_NOT_EXISTS));
}
// 更新状态为已回复
updateStateById(repliedId,(byte)2);
// 回复的内容写入数据库
create(message);
}
5. 单元测试

6. Controller实现方法并对外提供API接口
实现步骤
1> 校验当前登录用户的状态(是否禁言)
2> 校验要恢复的站内信状态(是否有站内信)
3> 校验是不是给自己回复
4> 构造回复对象
5> 调用Service
6> 返回结果
具体代码
@Operation(summary = "回复站内信")
@PostMapping("/reply")
public AppResult reply(HttpServletRequest request,
@Parameter(description = "要回复的站内信Id") @RequestParam("repliedId") @NonNull Long repliedId,
@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);
}
// 校验要回复的站内信状态
Message existsMessage = messageService.selectById(repliedId);
if (existsMessage == null || existsMessage.getDeleteState() == 1) {
// 返回错误描述
return AppResult.failed(ResultCode.FAILED_MESSAGE_NOT_EXISTS);
}
// 不能给自己回复
if (user.getId() == existsMessage.getPostUserId()) {
// 返回错误描述
return AppResult.failed("不能回复自己的站内信");
}
// 构造对象
Message message = new Message();
message.setPostUserId(user.getId()); // 发送者
message.setReceiveUserId(existsMessage.getPostUserId()); // 接收者
message.setContent(content); // 内容
// 调用Service
messageService.reply(repliedId, message);
// 返回结果
return AppResult.success();
}