BigEvent项目后端学习笔记(一)用户管理模块 | 注册登录与用户信息全流程解析(含优化)

📖 模块概述

  • 用户管理模块是系统的核心基础模块,包含 注册、登录、用户信息维护 等功能。本模块涉及 JWT Token认证、密码加密存储、文件上传 等关键技术点,是理解前后端分离架构中安全与数据交互的典型实践。
  • 本篇对于原项目进行了代码优化,将原先写在 Controller 层的业务逻辑代码迁移至了 Service 层,并且额外定义了全局异常处理类中的参数校验异常处理方法。

🛠️ 技术实现要点

  • 前端:Vue3
  • 后端:SpringBoot3 + MyBatis + JWT
  • 安全:Token身份验证
  • 存储:用户头像上传至OSS(或本地目录)

⚙️ 各层职责与代码规范

🔗 Controller 层

⭐️ 核心职责:

  • 接收HTTP请求参数(@RequestBody/@RequestParam)
  • 调用Service层方法
  • 返回统一响应格式(如Result对象)
@RestController
@RequestMapping("/user")
@Validated
@RequiredArgsConstructor
public class UserController {...
}

💡 注意事项:

  • 禁止在Controller中编写业务逻辑
  • 使用DTO对象接收参数,而非直接传递Entity
  • 统一异常处理(通过@ControllerAdvice)

🔗 Service 层

⭐️ 核心职责:

  • 实现具体业务逻辑(如数据校验、事务管理)
  • 组合多个Mapper操作
  • 处理异常并抛出自定义业务异常
public interface UserService {...
}
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {...
}

💡 最佳实践:

  • 接口与实现分离(UserService + UserServiceImpl)
  • 方法需明确事务边界(@Transactional注解)
  • 避免“上帝Service”(按业务拆分多个Service)

🔗 Mapper 层

⭐️ 核心职责:

  • 定义数据库操作方法(SQL映射)
  • 基于MyBatis/MyBatis-Plus实现CRUD
@Mapper
public interface UserMapper {...
}
<!-- 动态 SQL -->
<select id="getArticleList" resultType="com.itheima.pojo.Article">select * from article<where><if test="categoryId != null">category_id = #{categoryId}</if><if test="state != null">and state = #{state}</if>and create_user = #{userId}</where>
</select>

💡 规范建议:

  • 优先使用MyBatis原生方法
  • 定义数据库操作方法(SQL映射)
  • 基于MyBatis/MyBatis-Plus实现CRUD

🔗 层间交互与依赖注入

推荐使用构造函数注入:

@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {private final UserMapper userMapper;// Lombok自动生成构造函数
}
@RestController
@RequestMapping("/user")
@Validated
@RequiredArgsConstructor
public class UserController {private final UserService userService;// Lombok自动生成构造函数
}
层级对象类型说明
ControllerDTO(请求/响应对象)屏蔽Entity细节,保证接口安全
ServiceEntity、DTO、VO内部处理使用Entity,返回VO
MapperEntity、Query对象直接与数据库表结构对应

🔍 功能实现详解

🎯 注册

🧩 Controller 层

请求路径:/user/register
请求方式:POST
接口描述:该接口用于注册新用户

@PostMapping("/register")
public Result register(@Validated RegisterOrLoginDTO dto) {return userService.register(dto.getUsername(), dto.getPassword());
}

🧩 Service 层

  • 接口
// 根据用户名查询用户
User findByUsername(String username);// 注册
Result register(String username, String password);
  • 实现
/*** 根据用户名查询用户* * @param username 需要查询的用户名* @return 查询到的用户对象*/
@Override
public User findByUsername(String username) {return userMapper.findByUsername(username);
}
/*** 注册* * @param username 需要注册的用户名* @param password 需要注册的密码* @return 注册结果*/@Overridepublic Result register(String username, String password) {User user = findByUsername(username);if (user != null) {return Result.error("用户名已被占用");}userMapper.add(username, Md5Util.getMD5String(password));return Result.success();}

🧩 Mapper 层

// 根据用户名查询用户
@Select("select * from user where username = #{username}")
User findByUsername(String username);

🎯 登录

🧩 Controller 层

请求路径:/user/login
请求方式:POST
接口描述:该接口用于登录

@PostMapping("/login")
public Result login(@Validated RegisterOrLoginDTO dto) {return userService.authenticateUser(dto.getUsername(), dto.getPassword());
}

🧩 Service 层

  • 接口
// 登录验证
Result authenticateUser(String username, String password);
  • 实现
/*** 登录** @param username 用户名* @param password 密码* @return 登录结果*/
@Override
public Result authenticateUser(String username, String password) {User user = findByUsername(username);if (user == null) {return Result.error("用户不存在");}if (Md5Util.getMD5String(password).equals(user.getPassword())) {// 登录成功Map<String, Object> claims = new HashMap<>();claims.put("id", user.getId());claims.put("username", user.getUsername());String token = JwtUtil.genToken(claims);return Result.success(token);}return Result.error("密码错误!");
}

🧩 Mapper 层

// 无

🎯 获取用户详细信息

🧩 Controller 层

请求路径:/user/userInfo
请求方式:GET
接口描述:该接口用于获取当前已登录用户的详细信息

@GetMapping("/userInfo")
public Result<User> getUserInfo() {return userService.getUserInfo();
}

🧩 Service 层

  • 接口
// 获取用户信息
Result<User> getUserInfo();
  • 实现
/*** 获取用户信息** @return 用户信息*/
@Override
public Result<User> getUserInfo() {Map<String, Object> map = ThreadLocalUtil.get();String username = (String) map.get("username");User user = findByUsername(username);return Result.success(user);
}

🧩 Mapper 层

// 无

🎯 更新用户基本信息

🧩 Controller 层

请求路径:/user/update
请求方式:PUT
接口描述:该接口用于更新已登录用户的基本信息(除头像和密码)

@PutMapping("/update")
public Result update(@RequestBody @Validated User user) {return userService.update(user);
}

🧩 Service 层

  • 接口
// 更新
Result update(User user);
  • 实现
/*** 更新用户昵称和邮箱** @param user 用户对象*/
@Override
public Result update(User user) {user.setUpdateTime(LocalDateTime.now());userMapper.update(user);return Result.success();
}

🧩 Mapper 层

// 修改昵称和邮箱
@Update("update user set nickname = #{nickname}, email = #{email}, update_time = #{updateTime} where id = #{id}")
void update(User user);

🎯 更新用户头像

🧩 Controller 层

请求路径:/user/updateAvatar
请求方式:PATCH
接口描述:该接口用于更新已登录用户的头像

@PatchMapping("updateAvatar")
public Result updateAvatar(@RequestParam @URL String avatarUrl) {return userService.updateAvatar(avatarUrl);
}

🧩 Service 层

  • 接口
// 更新头像
Result updateAvatar(String avatarUrl);
  • 实现
/*** 更新用户头像** @param avatarUrl 用户头像地址*/
@Override
public Result updateAvatar(String avatarUrl) {Map<String, Object> map = ThreadLocalUtil.get();Integer id = (Integer) map.get("id");userMapper.updateAvatar(avatarUrl, id);return Result.success();
}

🧩 Mapper 层

// 修改头像
@Update("update user set user_pic = #{avatarUrl}, update_time = now() where id = #{id}")
void updateAvatar(String avatarUrl, Integer id);

🎯 更新用户密码

🧩 Controller 层

请求路径:/user/updatePwd
请求方式:PATCH
接口描述:该接口用于更新已登录用户的密码

@PatchMapping("/updatePwd")
public Result updatePwd(@RequestBody @Validated PwdUpdateDTO dto) {return userService.updatePwd(dto);
}

🧩 Service 层

  • 接口
// 更新密码
Result updatePwd(PwdUpdateDTO dto);
  • 实现
/*** 更新用户密码** @param dto 密码传输对象* @return 更新密码结果*/
@Override
public Result updatePwd(PwdUpdateDTO dto) {// 1.校验参数if (!StringUtils.hasLength(dto.getOldPwd())|| !StringUtils.hasLength(dto.getNewPwd())|| !StringUtils.hasLength(dto.getRePwd())) {return Result.error("缺少必要的参数");}// 2.校验旧密码Map<String, Object> map = ThreadLocalUtil.get();String username = (String) map.get("username");User loginUser = findByUsername(username);if (loginUser == null) {return Result.error("用户不存在");}if (!loginUser.getPassword().equals(Md5Util.getMD5String(dto.getOldPwd()))) {return Result.error("旧密码填写错误");}if (!dto.getNewPwd().equals(dto.getRePwd())) {return Result.error("两次密码不一致");}if (loginUser.getPassword().equals(Md5Util.getMD5String(dto.getNewPwd()))) {return Result.error("新密码不能与旧密码相同");}// 3.更新密码userMapper.updatePwd(Md5Util.getMD5String(dto.getNewPwd()), loginUser.getId());return Result.success();
}

🧩 Mapper 层

// 修改密码
@Update("update user set password = #{md5String}, update_time = now() where id = #{id}")
void updatePwd(String md5String, Integer id);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/73594.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

c#:使用串口通讯实现数据的发送和接收

串口通讯&#xff08;Serial Communication&#xff09;是一种常见的硬件设备与计算机之间的数据传输方式&#xff0c;广泛应用于工业控制、嵌入式系统、传感器数据采集等领域。本文将详细介绍如何使用C#实现基于串口通讯的数据发送和接收&#xff0c;并结合代码示例解析其实现…

k8s面经

最近面了几场&#xff0c;k8s技能几乎成了运维的必备品了。 容器一直pending可能的原因调度问题。容器的调度策略&#xff0c;比如标签选择、容忍度或者亲和度&#xff0c;导致没有合适的节点可供调度 资源问题&#xff0c;比如宿主机的CPU内存磁盘不足&#xff0c;或者要挂载…

网络通信(传输层协议:TCP/IP ,UDP):

Socket是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端&#xff0c;提供了应用层进程利用网络协议交换数据的机制。 网络协议&#xff1a;一台电脑的数据怎么传递给另一台电脑&#xff0c;是由网络协议来规定的 端口号&#…

DeepSeek进阶应用(二):结合Kimi制作PPT(双AI协作教程)

&#x1f31f;引言&#xff1a; DeepSeek作为国产AI大模型&#xff0c;以强大的逻辑推理和结构化内容生成能力著称&#xff0c;擅长根据用户需求生成PPT大纲或Markdown文本&#xff1b;Kimi的PPT助手则能解析结构化内容并套用模板快速生成美观的PPT&#xff0c;两者结合实现“内…

卷积神经网络(知识点)

一、为了使特征图变小&#xff1a; 由两种方法&#xff1a;1.增大步长&#xff1a;卷积的时候不是一次一步&#xff0c;而是一次多步&#xff0c;类似一张图片&#xff0c;在原来的像素基础上&#xff0c;每隔一个取一个像素点。 其中S就是步长 注意&#xff1a;扩大步长不经…

考研系列-408真题计算机网络篇(18-23)

写在前面 此文章是本人在备考过程中408真题计算机网络部分&#xff08;2018年-2023年&#xff09;的易错题及相应的知识点整理&#xff0c;后期复习也常常用到&#xff0c;对于知识提炼归纳理解起到了很大的作用&#xff0c;分享出来希望帮助到大家~ # 2018 1.停止-等待协议的…

std::invoke详解

基础介绍 c17版本引入了std::invoke特性&#xff0c;这是一个通用的调用包装器&#xff0c;可以统一调用&#xff1a; 普通函数成员函数函数对象Lambda表达式指向成员的指针 它的主要作用是提供一个统一的方式来调用各种可调用对象。 std::invoke依赖的头文件&#xff1a;#…

使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b或DeepSeek-r1:1.5b 模型(完成度80%)

原文&#xff1a;&#x1f6a3;‍♂️ 使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b 模型 &#x1f6a3; — PaddleNLP 文档 使用 PaddleNLP 在 CPU(支持 AVX 指令)下跑通 llama2-7b 模型 &#x1f6a3; PaddleNLP 在支持 AVX 指令的 CPU 上对 llama 系列模型进行了…

Kotlin高效实现 Android ViewPager2 顶部导航:动态配置与性能优化指南

高效实现&#xff1a;强调代码的性能优化。Android ViewPager2&#xff1a;明确技术栈。顶部导航&#xff1a;核心功能点。动态配置与性能优化指南&#xff1a;突出动态配置的灵活性和性能优化的重点。 在 Android 开发中&#xff0c;使用 ViewPager2 实现高效的顶部导航&…

六种最新优化算法(TOC、MSO、AE、DOA、GOA、OX)求解多个无人机协同路径规划(可以自定义无人机数量及起始点),MATLAB代码

一、算法简介 &#xff08;一&#xff09;阿尔法进化&#xff08;Alpha Evolution&#xff0c;AE&#xff09;算法 阿尔法进化&#xff08;Alpha Evolution&#xff0c;AE&#xff09;算法是2024年提出的一种新型进化算法&#xff0c;其核心在于通过自适应基向量和随机步长的…

上传本地项目到GitHub

一、在GitHub上创建仓库 1.点击右上角头像–>点击Your repositories 2.点击New 3.创建仓库 网址复制一下&#xff0c;在后面git上传时会用到 二、打开Git Bash 1.cd 进入项目所在路径 2.输入git init 在当前项目的目录中生成本地的git管理&#xff08;当前目录下出现.…

14.使用各种读写包操作 Excel 文件:辅助模块

一 各种读写包 这些是 pandas 在底层使用的各种读写包。无须安装 pandas&#xff0c;直接使用这些读写包就能够读写 Excel 工作簿。可以尽可能地使用 pandas 来解决这类问题&#xff0c;只在 pandas 没有提供你所需要的功能时才用到读写包。 表中没有 xlwings &#xff0c;因为…

ubuntu ollama+dify实践

安装ollama 官网的指令太慢了&#xff0c;使用以下指令加速&#xff1a; export OLLAMA_MIRROR"https://ghproxy.cn/https://github.com/ollama/ollama/releases/latest/download" curl -fsSL https://ollama.com/install.sh | sed "s|https://ollama.com/dow…

spring boot+mybaits多条件模糊查询和分页查询

我们首先写一下多条件的模糊查询&#xff0c;首先在controller里面写一个接口&#xff0c;进行传参&#xff0c;我们这里要注意&#xff0c;之前写修改和增加的时候用的注解都是RequestBody,也就是说&#xff01;前端传过来一个json&#xff0c;数组也行&#xff0c;然后我们后…

HarmonyOS NEXT - 电商App实例四(登录界面)

登录界面是用户进入App的第一步&#xff0c;因此需要简洁明了&#xff0c;同时保持品牌风格的一致性。如&#xff1a;顶部区域为品牌LOGO展示&#xff0c;增加品牌识别度&#xff1b;中间区域为登录表单&#xff0c;包含输入框和按钮&#xff1b;底部区域为其他登录方式、注册入…

探索ima.copilot:个人知识库搭建的AI新利器

在信息爆炸的时代&#xff0c;知识的积累与管理成为了个人发展的关键。面对海量的科研文献、工作资料和各类信息&#xff0c;如何高效地构建属于自己的知识体系&#xff0c;是许多人面临的挑战。ima.copilot这款AI工具的出现&#xff0c;为解决这一难题提供了新的思路。它凭借强…

图解多头注意力机制:维度变化一镜到底

目录 一、多头注意力机制概述二、代码实现1. pyTorch 实现2. tensorFlow实现 三、维度变化全流程详解1. 参数设定2. 维度变化流程图3. 关键步骤维度变化 四、关键实现细节解析1. 多头拆分与合并2. 注意力分数计算3. 掩码处理技巧 五、完整运行示例六、总结与常见问题1. 核心优势…

Interview preparation.md

Vue 1.1 响应式系统 Vue 3 使用 Proxy 代替 Vue 2 中的 Object.defineProperty 来实现响应式系统。Proxy 可以监听对象的所有操作&#xff0c;包括属性的添加和删除&#xff0c;从而解决了 Vue 2 的一些局限性。 Vue 2&#xff1a;使用 Vue.set 添加响应式属性 new Vue({el…

2.8滑动窗口专题:最小覆盖子串

1. 题目链接 LeetCode 76. 最小覆盖子串 2. 题目描述 给定字符串 s 和 t&#xff0c;要求找到 s 中最小的窗口&#xff0c;使得该窗口包含 t 的所有字符&#xff08;包括出现次数&#xff09;。若不存在&#xff0c;返回空字符串。 示例&#xff1a; 输入&#xff1a;s &quo…

【数据分析大屏】基于Django+Vue汽车销售数据分析可视化大屏(完整系统源码+数据库+开发笔记+详细部署教程+虚拟机分布式启动教程)✅

目录 一、项目背景 二、项目创新点 三、项目功能 四、开发技术介绍 五、项目功能展示 六、权威视频链接 一、项目背景 汽车行业数字化转型加速&#xff0c;销售数据多维分析需求激增。本项目针对传统报表系统交互性弱、实时性差等痛点&#xff0c;基于DjangoVue架构构建…