springboot实现复杂业务下的更新操作

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: springboot实现复杂业务下的更新操作


前言

现在是2022年5月4日19:25:55!今天写了个这样的功能:

某用户在一天内有多个训练项目,比如:晨跑,有氧训练,跳绳这三个项目。这三个训练项目都在数据库中有记录,每个项目都有开始时间,结束时间以及状态。状态包含:未开始,进行中和已结束。

下图展示的就是训练项目

用户早上醒来之后,点击第一个晨跑的小卡片,那么就开始记录当前时间就是晨跑的开始时间。训练完之后,用户接着点击有氧训练的小卡片,那么此时,晨跑项目的结束时间就是当前时间,有氧训练的开始时间也是当前时间,此时的晨跑状态是已结束,有氧训练的状态是进行中

没明白的话多看几遍就明白了,哈哈哈,这儿有点绕。

因为这是两个系统,训练项目所属一个项目,调用的是另一个项目,下面说一下我的实现方式。

实现思路

  1. 在训练项目的系统上,只实现更新操作,传参:项目id,开始时间,结束时间,状态
  2. 在调用的系统上,编写业务代码块,一共有三个接口。
  3. 接口一:记录第一个训练项目的开始时间,将当前训练项目的id放在redis中。
  4. 接口二:从redis中获取上一个动作的id,传递动作的id,时间戳,然后修改上一个动作的结束时间是当前的时间戳,第一个动作的状态是【已结束】,当前动作的开始时间是当前的时间戳,状态是【训练中】,删除上一个动作的id,将当前动作的id放在redis里面
  5. 接口三:最后一个动作的结束:传递当前方案的id,动作的id,用户id,时间戳,然后修改当前动作的结束时间是时间戳,状态是【已结束】,删掉redis里面的用户id

说明:大家可以看到,我将第一个训练项目的id放在了redis中,这么放的目的就是下一个训练项目传过来之后,更好的知道上一个训练项目是哪个,因为我们需要更改它的状态和结束时间。之后还需要更改当前训练项目的开始时间和状态。以此类推,一直到最后一个。

接口三主要实现的功能就是更新最后一个训练项目的结束时间,这样我们就可以依次更改每个训练项目的开始时间、结束时间以及训练状态

上面还是有点绕,大家可以仔细的看看~

实现代码

下面我来介绍一下代码是怎么实现的。

首先是训练项目中的更新操作,这个方法就一个修改的操作,没啥特别复杂的,下面是代码:

/**
     * 根据项目project的id,修改动作状态和开始时间,结束时间
     * 参数:动作id,状态,开始时间,结束时间
     */
    @PostMapping(value = "/updateProjectById")
    public String updateProjectById(@RequestHeader("Authorization")
                                            String token,
                                            Long pid,
                                            int status,
                                            Long beginTime,
                                            Long endTime) {
        JSONObject jsonObject = new JSONObject();
        //1.验证token
        boolean isOk = TokenUtil.verify(token);
        if (!isOk) {
            jsonObject.put("code", 401);
            jsonObject.put("msg", "令牌失效");
            return jsonObject.toString();
        }
        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        try {
            //查询
            Project project = projectService.getById(pid);
            //训练项目状态,0未开始,1进行中,2已完成
            project.setProjectStatus(status + "");
            //将时间戳转换成时间
            if (beginTime != null) {
                String kstime = DateParseUtils.getTimeChuo(beginTime);
                project.setBeginTime(format.parse(kstime));
            }
            //结束时间
            if (endTime != null) {
                String jstime = DateParseUtils.getTimeChuo(endTime);
                project.setEndTime(format.parse(jstime));
            }
            projectService.updateById(project);
            jsonObject.put("code",200);
            jsonObject.put("msg","操作成功");
            jsonObject.put("status",true);
        } catch (ParseException e) {
            jsonObject.put("code",500);
            jsonObject.put("msg","操作失败");
            jsonObject.put("status",false);
            e.printStackTrace();
        }
        return jsonObject.toJSONString();
    }

可以发现,开始时间和结束时间都不能是必填,因为在更新第一个训练项目的时候,我们只能知道当前的时间是开始时间,并不知道何时结束。同理,在更新第二个训练项目时,我们只需拿着当前时间更新上一个训练项目的结束时间就行。

下面是调用方的实现:

  1. 将参数封装成实体:
package org.springblade.modules.system.vo;
import io.swagger.annotations.ApiModel;
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
 * @author: muxiongxiong
 * @date: 2022年04月30日 20:34
 * 公众号:雄雄的小课堂
 * 博客:https://blog.csdn.net/qq_34137397
 * 个人站:http://www.穆雄雄.com
 * 个人站:http://www.muxiongxiong.cn
 * @Description: 类的描述
 */
@Data
@ApiModel(value = "TrainingVO对象", description = "TrainingVO对象")
public class TrainingVO {
    /**
     * 动作id
     */
    private Long projectId;
    /**
     * 方案id
     */
    private Long programmeId;
    /**
     * 用户id
     */
    private Long userId;
    /**
     * 动作状态
     */
    private Integer status;
    /**
     * 时间戳 接收前台传过来的时间戳
     */
    private Long time;
    /**
     * 业务上处理的动作,开始时间
     */
    private Long beginTime;
    /**
     * 业务上处理的动作,结束时间
     */
    private Long endTime;
}
  1. 公共接口,也就是更新的接口:
/**
     * 公共的接口
     * 调用训练方案的
     * 更新动作的状态和时间
     *
     * @param trainingVO
     * @return
     */
    @Override
    public R updateTrainingProgrammeAndProjectStatusAndTime(TrainingVO trainingVO) {
        //判断是否为空
        R rt = getToken();
        String token = "";
        if (rt != null) {
            JSONObject jsonObject = (JSONObject) rt.getData();
            if (jsonObject != null) {
                token = jsonObject.getStr("access_token");
            }
        } else {
            return R.fail(500, "获取token失败");
        }
        if (StringUtil.isEmpty(token)) {
            return R.fail(500, "获取token失败");
        }
        //请求接口地址
        String url = TrainingSchemeConstant.updateProjectById;
        Map paramMap = new HashMap();
        //动作id
        if (trainingVO.getProjectId() != null) {
            paramMap.put("pid", trainingVO.getProjectId());
        }
        //开始时间
        if (trainingVO.getBeginTime() != null) {
            paramMap.put("beginTime", trainingVO.getBeginTime());
        }
        //状态
        if (trainingVO.getStatus() != null) {
            paramMap.put("status", trainingVO.getStatus());
        }
        //结束时间
        if (trainingVO.getEndTime() != null) {
            paramMap.put("endTime", trainingVO.getEndTime());
        }
        try {
            String body = HttpUtil.createPost(url).header("Authorization", token).form(paramMap).execute().body();
            if (StringUtils.isBlank(body)) {
                return R.status(false);
            }
            JSONObject obj = JSONUtil.parseObj(body);
            if (obj == null) {
                return R.status(false);
            }
            boolean flag = Boolean.parseBoolean(obj.get("status").toString());
            return R.status(flag);
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println(e.getMessage());
            return R.status(false);
        }
    }
  1. 接口一,更新第一个训练项目的开始时间状态:
/**
     * 接口一:第一个动作的开始:
     * 1.当前动作的id,时间戳,然后将当前动作的开始时间修改,当前动作的状态该成 【训练中】
     * 2.将当前动做的id放在redis里面。
     * 参数:用户id,方案id,主要是放在redis的时候需要用
     *
     * @param trainingVO
     * @return
     */
    @PostMapping("/updateProjectBeginTime")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "threePlant-第一个动作的更新", notes = "传入userId,programmeId,projectId,time")
    public R updateProjectBeginTime(@ApiParam(value = "训练方案业务对象", required = true) @RequestBody  TrainingVO trainingVO) {
        //第一个动作的开始时间
        trainingVO.setBeginTime(trainingVO.getTime());
        //第一个动作:训练中
        trainingVO.setStatus(1);
        //将第一个动作放在redis的缓存中
        bladeRedis.set("training:updateTrainingStatusAndTime:" +
            trainingVO.getUserId() + ":" +
            trainingVO.getProgrammeId(), trainingVO.getProjectId());
        //调用更新的借口
        return trainingSchemeUrlService.updateTrainingProgrammeAndProjectStatusAndTime(trainingVO);
    }
  1. 接口二,更新当前动作的开始时间,状态和上一个动作的结束时间、状态。
/**
     * 接口二:中间的动作操作:
     * 1.从redis中获取上一个动作的id。
     * 2.传递动作的id,时间戳,然后修改上一个动作的结束时间是当前的时间戳,第一个动作的状态是【已结束】,当前动作的开始时间是当前的时间戳,状态是【训练中】
     * 3.删除上一个动作的id,将当前动作的id放在redis里面
     *
     * @param trainingVO
     * @return
     */
    @PostMapping("/updateProjectBeginTimeAndEndTime")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "threePlant-中间动作的更新", notes = "传入userId,programmeId,projectId,time")
    public R updateProjectBeginTimeAndEndTime(@ApiParam(value = "训练方案业务对象", required = true) @RequestBody TrainingVO trainingVO) {
        TrainingVO trainingVONew = new TrainingVO();
        //先从redis中获取上一个动作的id
        Long beforePid = bladeRedis.get("training:updateTrainingStatusAndTime:" + trainingVO.getUserId() + ":" + trainingVO.getProgrammeId());
        //用完之后删掉redis里面的数据
        bladeRedis.del("training:updateTrainingStatusAndTime:" + trainingVO.getUserId() + ":" + trainingVO.getProgrammeId());
        //修改上个动作的结束时间是当前的时间戳
        trainingVONew.setEndTime(trainingVO.getTime());
        //上个动作的状态是已结束
        trainingVONew.setStatus(2);
        trainingVONew.setProjectId(beforePid);
        //调用更新的借口
        R resultR = trainingSchemeUrlService.updateTrainingProgrammeAndProjectStatusAndTime(trainingVONew);
        if (resultR.getCode() == 200) {
            //更新没啥问题
            //开始修改当前状态
            //状态是训练中
            trainingVO.setStatus(1);
            //开始时间是时间戳
            trainingVO.setBeginTime(trainingVO.getTime());
            //将当前的动作在放在redis里面
            bladeRedis.set("training:updateTrainingStatusAndTime:" +
                trainingVO.getUserId() + ":" +
                trainingVO.getProgrammeId(), trainingVO.getProjectId());
            //调用更新的方法
            return trainingSchemeUrlService.updateTrainingProgrammeAndProjectStatusAndTime(trainingVO);
        } else {
            return R.status(false);
        }
    }
  1. 接口三,更新最后一个训练项目的结束时间和训练状态:
/**
     * 接口三:最后一个动作的结束:
     * 1.传递当前方案的id,动作的id,用户id,时间戳,然后修改当前动作的结束时间是时间戳,状态是【已结束】
     * 2.删掉redis里面的用户id
     *
     * @param trainingVO
     * @return
     */
    @PostMapping("/updateProjectEndTime")
    @ApiOperationSupport(order = 3)
    @ApiOperation(value = "threePlant-最后一个动作的更新", notes = "传入userId,programmeId,projectId,time")
    public R updateProjectEndTime(@ApiParam(value = "训练方案业务对象", required = true) @RequestBody TrainingVO trainingVO) {
        //最后一个动作的结束时间是时间戳
        trainingVO.setEndTime(trainingVO.getTime());
        //最后一个动作是已结束
        trainingVO.setStatus(2);
        //删掉redis中的动作id
        bladeRedis.del("training:updateTrainingStatusAndTime:" +
            trainingVO.getUserId() + ":" +
            trainingVO.getProgrammeId());
        return trainingSchemeUrlService.updateTrainingProgrammeAndProjectStatusAndTime(trainingVO);
    }
        trainingVO.setStatus(2);
        //删掉redis中的动作id
        bladeRedis.del("training:updateTrainingStatusAndTime:" +
            trainingVO.getUserId() + ":" +
            trainingVO.getProgrammeId());
        return trainingSchemeUrlService.updateTrainingProgrammeAndProjectStatusAndTime(trainingVO);
    }

以上就是整个流程的实现,其实写代码难的不是代码,而是业务,当你明白了业务是什么,随之而来的就是实现方式,所以,业务是重中之重的~

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore     ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库 ECS 实例和一台目标数据库 RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
6月前
|
NoSQL Java Redis
SpringBoot 配置Redis操作
SpringBoot 配置Redis操作
74 0
|
5月前
|
Java 应用服务中间件 Maven
浅谈后端整合Springboot框架后操作基础配置
浅谈后端整合Springboot框架后操作基础配置
36 3
|
5月前
|
NoSQL Java 数据库
SpringBoot实用开发篇第三章(数据层解决方案操作)
SpringBoot实用开发篇第三章(数据层解决方案操作)
|
5月前
|
Java 数据库连接 开发者
SpringBoot实用开发篇第一章(yml配置文件操作)
SpringBoot实用开发篇第一章(yml配置文件操作)
|
5月前
|
JSON Java 测试技术
SpringBoot实用开发篇第二章(测试操作)
SpringBoot实用开发篇第二章(测试操作)
|
6月前
|
NoSQL Java Redis
SpringBoot 操作 Redis
SpringBoot 操作 Redis
60 2
|
6月前
|
安全 Java API
SpringBoot 实现 elasticsearch 索引操作(RestHighLevelClient 的应用)
SpringBoot 实现 elasticsearch 索引操作(RestHighLevelClient 的应用)
92 1
|
6月前
|
自然语言处理 Java 索引
SpringBoot 实现 elasticsearch 查询操作(RestHighLevelClient 的案例实战)
SpringBoot 实现 elasticsearch 查询操作(RestHighLevelClient 的案例实战)
253 1
|
6月前
|
存储 关系型数据库 MySQL
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
【mybatis-plus】Springboot+AOP+自定义注解实现多数据源操作(数据源信息存在数据库)
|
6月前
|
SQL Java 数据库连接
Springboot框架整合Spring JDBC操作数据
JDBC是Java数据库连接API,用于执行SQL并访问多种关系数据库。它包括一系列Java类和接口,用于建立数据库连接、创建数据库操作对象、定义SQL语句、执行操作并处理结果集。直接使用JDBC涉及七个步骤,包括加载驱动、建立连接、创建对象、定义SQL、执行操作、处理结果和关闭资源。Spring Boot的`spring-boot-starter-jdbc`简化了这些步骤,提供了一个在Spring生态中更便捷使用JDBC的封装。集成Spring JDBC需要添加相关依赖,配置数据库连接信息,并通过JdbcTemplate进行数据库操作,如插入、更新、删除和查询。
70 0