前言
现在是2022年5月4日19:25:55!今天写了个这样的功能:
某用户在一天内有多个训练项目,比如:晨跑,有氧训练,跳绳这三个项目。这三个训练项目都在数据库中有记录,每个项目都有开始时间,结束时间以及状态。状态包含:未开始,进行中和已结束。
下图展示的就是训练项目
用户早上醒来之后,点击第一个晨跑的小卡片,那么就开始记录当前时间就是晨跑的开始时间。训练完之后,用户接着点击有氧训练的小卡片,那么此时,晨跑项目的结束时间就是当前时间,有氧训练的开始时间也是当前时间,此时的晨跑状态是已结束,有氧训练的状态是进行中
没明白的话多看几遍就明白了,哈哈哈,这儿有点绕。
因为这是两个系统,训练项目所属一个项目,调用的是另一个项目,下面说一下我的实现方式。
实现思路
- 在训练项目的系统上,只实现更新操作,传参:项目id,开始时间,结束时间,状态
- 在调用的系统上,编写业务代码块,一共有三个接口。
- 接口一:记录第一个训练项目的开始时间,将当前训练项目的id放在redis中。
- 接口二:从
redis
中获取上一个动作的id
,传递动作的id
,时间戳,然后修改上一个动作的结束时间是当前的时间戳,第一个动作的状态是【已结束】,当前动作的开始时间是当前的时间戳,状态是【训练中】,删除上一个动作的id
,将当前动作的id
放在redis
里面 - 接口三:最后一个动作的结束:传递当前方案的
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(); }
可以发现,开始时间和结束时间都不能是必填,因为在更新第一个训练项目的时候,我们只能知道当前的时间是开始时间,并不知道何时结束。同理,在更新第二个训练项目时,我们只需拿着当前时间更新上一个训练项目的结束时间就行。
下面是调用方的实现:
- 将参数封装成实体:
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; }
- 公共接口,也就是更新的接口:
/** * 公共的接口 * 调用训练方案的 * 更新动作的状态和时间 * * @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.当前动作的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.从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.传递当前方案的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); }
以上就是整个流程的实现,其实写代码难的不是代码,而是业务,当你明白了业务是什么,随之而来的就是实现方式,所以,业务是重中之重的~