后端系统开发之——功能完善

原文地址:https://blog.yiming1234.cn/?p=830

下面是正文内容:

前言

通过SpringBoot开发用户模块的部分也就差不多要结束了,这一片文章就主要提一些在系统开发中需要注意到的细节部分和功能,也就是剩余的部分。

但是这个专栏只介绍了系统后端用户模块的开发,因为用户模块在大部分java后端的开发上都是通用必须掌握的。

而系统需要实现的具体功能比如说文章的发表,出入库的登记查询等等,则需要另起专栏去进行描述。

正文

获取更新用户信息/更新用户密码

由于这一部分接口的开发,和前面提到的注册登录接口的开发步骤大同小异,所以就合并之后简单描述一下了。

Controller层(UserController.java)

复制代码
@GetMapping("/userinfo")
    public Result<User> userinfo(/*@RequestHeader(name = "Authorization") String token*/){
        //根据用户名查询用户
        /*Map<String, Object> map = JwtUtil.parseToken(token);
        String username = (String) map.get("username");*/
        Map<String, Object> map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User user = userService.findByUserName(username);
        return Result.success(user);
    }

    @PtachMapping("/update")
    public Result<User> update(@RequestBody User user){
        userService.update(user);
        return Result.success();
    }

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

Service层(UserServiceImpl.java)

复制代码
    @Override
    public void update(User user){
        user.setUpdateTime(LocalDateTime.now());
        userMapper.update(user);
    }

    @Override
    public void updateAvatar(String avatarUrl){
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updateAvatar(avatarUrl,id);
    }

    @Override
    public void updatePwd(String newPwd){
        Map<String,Object> map = ThreadLocalUtil.get();
        Integer id = (Integer) map.get("id");
        userMapper.updatePwd(Md5Util.getMD5String(newPwd),id);
    }

Mapper层(UserMapper.java)

复制代码
@Update("update user set nickname=#{nickname},email=#{email},update_time=#{updateTime} where id=#{id}")
    void update(User user);

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

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

更改密码后Jwt令牌的及时失效

需要用到Redis服务。

Redis是一个开源的、基于内存的数据结构存储系统,可以用作数据库、缓存和消息代理。

作为缓存存储系统非常流行,可以将经常访问的数据存储在内存中,以加快读取速度,减轻后端数据库的负载。可用于存储会话数据,以实现分布式会话管理,提高可扩展性和性能。

首先从github上下载redis

Releases · tporadowski/redis · GitHub

解压后cd命令切换至根目录下启动,看到下面的界面

复制代码
redis-server.exe redis.windows.conf

然后在pom.xml中添加下面的字段

复制代码
<!--redis依赖-->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-data-redis</artifactId>
    </dependency>

将application.yml修改为如下

复制代码
spring:
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://localhost:3306/big_event
    username: root
    password: root
  data:
    redis:
      host: localhost
      port: 6379

mybatis:
  configuration:
    map-underscore-to-camel-case: true #开启驼峰命名和下划线命名的自动转换

针对Redis需要做出的修改:登录成功后需要将token储存至Redis中,系统内页面在访问时验证需要从Redis中获取token,密码更新后需要删除Redis中对应的token。

修改UserController.java中的内容

复制代码
@Autowired
    private StringRedisTemplate stringRedisTemplate;
@PostMapping("/login")
    public Result<String> login(@Pattern(regexp = "^\\S{5,16}$") String username, @Pattern(regexp = "^\\S{5,16}$") String password){
        //根据用户名查询用户
        User loginUser = userService.findByUserName(username);
        //判断该用户是否存在
        if (loginUser==null){
            return Result.error("用户名不存在");
        }
        //判断密码是否正确,传入参数加密后与数据库中的密文进行比对
        if (Md5Util.getMD5String(password).equals(loginUser.getPassword())) {
        //登录成功
            Map<String,Object> claims = new HashMap<>();
            claims.put("id",loginUser.getId());
            claims.put("username",loginUser.getUsername());
            String token = JwtUtil.genToken(claims);
            //把token存储到redis中
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            operations.set(token,token, 1, TimeUnit.HOURS);
            return Result.success(token);
        }

        return Result.error("密码错误");
    }

 @PatchMapping("/updatePwd")
    public Result updatePwd(@RequestBody Map<String, String> params,@RequestHeader("Authorization") String token){
        //校验参数
        String oldPwd = params.get("old_pwd");
        String newPwd = params.get("new_pwd");
        String rePwd = params.get("re_pwd");

        if (!StringUtils.hasLength(oldPwd)|| !StringUtils.hasLength(newPwd)|| !StringUtils.hasLength(rePwd)){
            return Result.error("缺少必要的参数");
        }
        //原密码是否正确,调用原密码和old_pwd进行比较
        Map<String, Object> map = ThreadLocalUtil.get();
        String username = (String) map.get("username");
        User loginUser = userService.findByUserName(username);
        if(!loginUser.getPassword().equals(Md5Util.getMD5String(oldPwd))){
            return Result.error("原密码错误");
        }

        if(!rePwd.equals(rePwd)){
            return Result.error("两次填写的密码不一致");
        }
        //调用service完成密码更新
        userService.updatePwd(newPwd);
        //删除redis中对应的token
        ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
        operations.getOperations().delete(token);
        return Result.success("密码修改成功");
    }

修改LoginInterceptor.java中的内容

复制代码
    @Autowired
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //令牌验证
        String token = request.getHeader("Authorization");
        //验证token
        try {
            //从redis中获取相同的token
            ValueOperations<String, String> operations = stringRedisTemplate.opsForValue();
            String redisToken = operations.get(token);
            if (redisToken==null){
                throw new RuntimeException();
            }
            Map<String, Object> claims = JwtUtil.parseToken(token);

            //把业务数据储存到ThreadLocal中
            ThreadLocalUtil.set(claims);
            //放行
            return true;
        } catch (Exception e) {
            response.setStatus(401);
            //不放行
            return false;
        }

SpringBoot项目部署

SpringBoot项目在后端程序员电脑上正常运行之后,就需要打包上传至公司服务器,进行项目部署。

在pom.xml中 <dependecies>和</project>之间添加下面的字段用于打包

复制代码
<build>
    <plugins>
      <!--打包操作-->
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
        <version>3.2.3</version>
      </plugin>
    </plugins>
 </build>

在右侧边栏找到package字样,点击运行等程序跑完。

这里需要注意的是:SpringBoot在进行自动化打包的时候,是会默认测试Test环境下的java类能否正常运行的。要么在打包的时候选择skip跳过,要么就确保测试类能够正常运行。

如果配置了Redis那么也同时需要把redis打开。

还有就是打包的时候要把项目关了,不然就会报错8080端口占用。

可以看到已经打包成功了,这里就不上传服务器,假设本地Windows电脑为服务端进行测试

jdk需要至少版本17,找到打包好的jar包,直接java -jar system-1.0-SNAPSHOT.jar启动

由于还没有编写前端页面,所以还是只能通过Apifox来进行试验,这里就进行演示了。

结束

其实还有一些比如说多环境(测试,开发,生产)下的开发,本地电脑属性的配置等等内容没有介绍,但由于是小项目没有这个必要,所以也就不进行过多的赘述了。

关于后端系统开发的专栏就暂且告一段落了。

尾声

后面开始就要进行Vue.js的工程化部署以及前后端的连接了,大家可以点个关注跟进一下。

相关推荐
码兄科技3 小时前
实战:基于Spring Boot + UniApp的地理信息小程序开发
spring boot·后端·uni-app
腾讯云云开发4 小时前
腾讯云 CloudBase 登上 WAIC:我们为 Agent 重新设计了云的生产线
后端
星栈4 小时前
从装一堆工具到看懂 Node 工程化思维:我的项目复盘记录
后端·node.js
65岁退休Coder5 小时前
LangChain v1.3.4 笔记 - 05 Agent 上下文记忆
后端
颜酱5 小时前
04 | 召回前置准备:搭好召回所需的四个数据库
前端·人工智能·后端
JaneConan5 小时前
鸿蒙 韶非 UI 系列:能力调用 startAbilityForResult,跳能力拿回参,鸿蒙能力路由入门
后端·harmonyos
晴空了无痕5 小时前
从 Go 基础到 K8s:一条可落地的 Go 服务端成长路线
开发语言·后端·golang·kubernetes
用户8356290780515 小时前
如何使用 Python 在 Excel 中添加、编辑和删除超链接
后端·python
花椒技术5 小时前
原本要 2 天的服务端冒烟前置审查,为什么 3 分半就能出报告?|QA 质量交付实践(三)
后端·ai编程·测试
达达尼昂5 小时前
AI 编程的工程化实践:Flutter AI Harness 的设计与落地
人工智能·后端·全栈