文章只提供了部分代码,每一天对应着分支工具类等完整代码都可以去github上获取,地址:https://github.com/kkoneone11/cloud-photo/
用户登录服务功能
流程图
数据库
开发过程
1.基础配置
1.1在application配置类里改一下名字,端口9008
1.2创建启动类,启动类头上配置
@EnableDiscoveryClient @SpringBootApplication @MapperScan(basePackages = {"com.cloud.photo.users.mapper"})
1.3pom文件导入
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <scope>compile</scope> </dependency> <dependency> <groupId>org.example</groupId> <artifactId>cloud-photo-common</artifactId> <version>1.0-SNAPSHOT</version> <scope>compile</scope> </dependency> </dependencies>
1.4通过代码生成器操作user表生成代码
2.接口开发
2.1接口1:根据手机号获取用户信息/user/getUserInfo Get
2.1.1参数分析:根据手机phone可以唯一确定一个用户
2.1.2通过phone查询用户信息
/** * 获得用户信息,根据用户手机获取用户信息 * @param phone * @return resultBody */ @GetMapping("/getUserInfo") public ResultBody getUserInfo(@RequestParam(value = "phone") String phone){ log.info("getUserInfo-phone"+ phone + ",start!"); User user = userService.getOne(new QueryWrapper<User>().eq("phone",phone)); log.info("getUserInfo()-phone=" + phone + ",user=" + user); ResultBody resultBody = (user == null ) ? ResultBody.error(CommonEnum.USER_IS_NULL) : ResultBody.success(user); log.info("getUserInfo()-phone=" + phone + ",resultBody=" + resultBody); return resultBody; }
2.2接口2:用户执行登录/user/login POST
2.2.1参数分析:phone,通过phone参数查询用户
2.2.2通过phone查询是否存在用户,如果
- 不存在则创建一个新用户并设置相关参数
- 存在则更新登录时间然后更新登录的次数
最后将用户更新
注意修改user实体里的LocalDateTime为Date否则会报错
/** * 登录 * @param userBo * @return */ @PostMapping("/login") public ResultBody login(@RequestBody UserBo userBo){ String phone = userBo.getPhone(); //查询用户 User user = userService.getOne(new QueryWrapper<User>().eq("phone", phone), false); //1.用户不存在则生成 if(user == null){ //组装 user = new User(); //复制 BeanUtils.copyProperties(userBo, user); //自定义一个用户ID user.setUserId(RandomUtil.randomString(9)); user.setCreateTime(DateUtil.date()); user.setUpdateTime(DateUtil.date()); user.setLoginCount(0); //普通用户 user.setRole("user"); }else{ //2.存在则更新状态即可 user.setUpdateTime(DateUtil.date()); user.setLoginCount(user.getLoginCount()+1); } //入库 boolean saveOrUpdate = userService.saveOrUpdate(user); return saveOrUpdate ? ResultBody.success() : ResultBody.error(CommonEnum.LOGIN_FAIL); }
2.3接口3:根据手机号检查用户是否存在/user/checkPhone Get
2.3.1参数分析:phone
2.3.2
/** * 查询用户是否存在 * * @param phone 手机号 * @return 查询结果 */ @GetMapping("/checkPhone") public ResultBody checkPhone(@RequestParam(value = "phone") String phone) { User userEntity = userService.getOne(new QueryWrapper<User>().eq("phone", phone)); return userEntity == null ? ResultBody.error(CommonEnum.USER_IS_NULL) : ResultBody.success(); }
2.4接口4:管理员检查账号密码/user/checkAdmin Get
2.4.1参数分析:phone,password。因为是管理员的话需要账号密码去验证
2.4.2拿到phone和password之后去执行一条查询语句看是否存在该用户
/** * 检查管理员登录 * @param userName * @param password * @return */ @GetMapping("/checkAdmin") public ResultBody checkAdmin(@RequestParam(value = "userName")String userName, @RequestParam(value = "password"),String password){ User user = userService.getOne(new QueryWrapper<User>() .eq("user_name", userName) .eq("password", password) .eq("role", CommonConstant.ADMIN)); return user == null ? ResultBody.error(CommonEnum.USERNAME_PASSWORD_ERROR) : ResultBody.success(); }
API接口开发
在api项目中实现
用户登录后前端会给后端一个satoken的认证凭证,
开发过程
1.基础配置
1.1因为需要用到saToken进行鉴权所以先在pom文件导入
<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>cloud-photo-common</artifactId> <version>1.0-SNAPSHOT</version> </dependency> <!-- sa-token 权限框架--> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-boot-starter</artifactId> <version>1.34.0</version> </dependency> <!-- sa-token 开启注解--> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-spring-aop</artifactId> <version>1.34.0</version> </dependency> <!-- sa-token 集成redis--> <dependency> <groupId>cn.dev33</groupId> <artifactId>sa-token-dao-redis-jackson</artifactId> <version>1.34.0</version> </dependency> </dependencies>
1.2在config包加入一个GlobalExceptionHandler全局捕获类,在utils包导入一个MyUtil类最后在nacosdiscovery包导入一个NacosDiscoveryConfiguration类
1.3配置一个启动类,注解备注
@SpringBootApplication @EnableDiscoveryClient @EnableFeignClients(basePackages = {"com.cloud.photo.common.fegin"})
1.4在Controller包和service包下分别创建以下
2.接口开发
2.1接口1:/api/login POST
2.1.1先在LoginController创建一个login()放,然后具体业务在service中实现。
先通过StpUtil工具类的isLogin()通过判断token是否存在且对得上,如果是则直接放行。否则则要看userBo是否为空,为空这返回登录信息为空的错误。然后判断是否是
- 管理员登录,检查是否有账号密码,检查账号密码是否正确,正确则调用doLogin方法放行
- 普通用户登录则检查调用checkPhone()看用户是否存在即可,不存在则表明第一次登录需要填入相关信息,检查是否有填入相关信息。补全了则直接doLogin()放行。
2.2接口2:/api/logout POST
2.2.1:先用StpUtil的isLogin()判断是否已经登录,已经登录了的话再继续找到该账户对应的loginId然后找到session,清空里面的session之后再执行StpUtil的logout()方法
/** * 退出 * @return */ @Override public ResultBody logout() { if(StpUtil.isLogin()){ String loginId = StpUtil.getLoginIdAsString(); SaSession session = StpUtil.getSessionByLoginId(loginId); //清空session session.logout(); StpUtil.logout(); } return ResultBody.success(); }
2.3接口3:/api/getUserInfo POST
2.3.1先看用户是否有登录,没有登录直接返回错误消息,如果已经登录了则先看session里是否有消息,
- session有消息先拿出loginId然后再拿到session,session不为空则继续拿到里面的userInfo,userInfo不为空就返回信息。
- session没有消息则调用users的getUserInfo()方法,然后再将userInfo存入到session里,最后再返回userInfo
2.4接口4:doLogin()
2.4.1参数分析:userBo,phone
2.4.2传入userBo调用users子项目的login方法检验参数是否成功,如果成功则再传入phone执行StpUtil的login()方法登录并产生token,把token返回给前端
2.5接口5:/api/getTransList POST
2.5.1在TransController下写一个getTransList()方法,先通过MyUtils的getUserInfo()拿到用户信息
然后拿到userId,再组装一个匹配前缀给stringRedisTemplate的keys方法拿到所有keys,如果keys不为空则通过stream流遍历所有key拿出里面的value然后再组装回去。最后通过sorted方法按上传时间降序排序即可。
/** * 传输管理相关接口 * @author kkoneone11 */ @RestController @RequestMapping("/api") public class TransController { @Resource private StringRedisTemplate stringRedisTemplate; @PostMapping("/getTransList") public ResultBody getTransList(){ UserBo userInfo = MyUtils.getUserInfo(); //用户信息为空则返回错误 if(userInfo == null){ return ResultBody.error(CommonEnum.USER_IS_NULL); } //不为空则拿到userId String userId = userInfo.getUserId(); //组装匹配前缀 String keyPatten = userId + ":" + "*"; //拿出keys Set<String> keys = stringRedisTemplate.keys(keyPatten); //keys不为空则遍历 if(keys!=null && CollUtil.isNotEmpty(keys)){ List<FileUploadBo> uploadBos = keys.stream().map(key -> { String value = stringRedisTemplate.opsForValue().get(key); return JSONUtil.toBean(value,FileUploadBo.class); }).collect(Collectors.toList()); //按上传时间降序 uploadBos = uploadBos.stream().sorted(Comparator.comparing(FileUploadBo::getUploadTime, Comparator.reverseOrder())).collect(Collectors.toList()); return ResultBody.success(uploadBos); }else{ return ResultBody.error(CommonEnum.TRANS_IS_NULL); } } }
2.6接口6:/api/getAlbumCategorize Get
2.6.1在AlbumController写一个getAlbumCategorize()方法。同样先分局MyUtils的getUserInfo()
从缓存中拿到用户信息,用户信息为空则返回错误。不为空则拿出userId然后构造一个albumPageBo设置相关信息,然后分别设置人物、地点、事物的分类信息然后分别最后getUserAlbumList()方法传入albumPageBo查询并放入jsonObject中最后返回即可
2.6.2在service方法里写一个getUserAlbumList方法调用userFilelist
@Override public ResultBody getUserAlbumList(AlbumPageBo pageBo) { return cloud2TransService.userFilelist(pageBo); }
/** * 获取用户照片分类信息 * @return 带有用户地点、人物、事物 三种标签的缩略图 (三张 按照上传时间排序) */ // @SaCheckLogin @GetMapping("/getAlbumCategorize") public ResultBody getAlbumCategorize(){ //缓存中拿到用户信息 UserBo userInfo = MyUtils.getUserInfo(); if(userInfo == null){ return ResultBody.error(CommonEnum.USER_IS_NULL); } String userId = userInfo.getUserId(); //组装AlbumPageBo AlbumPageBo albumPageBo = new AlbumPageBo(); albumPageBo.setUserId(userId); albumPageBo.setCurrent(1); albumPageBo.setPageSize(3); //再组装一个jsonObject最后面返回 JSONObject jsonObject = new JSONObject(); //人物 albumPageBo.setCategory(CommonConstant.IMAGE_CATEGORY_1); jsonObject.put("person",albumService.getUserAlbumList(albumPageBo)); //地点 albumPageBo.setCategory(CommonConstant.IMAGE_CATEGORY_2); jsonObject.put("location",albumService.getUserAlbumList(albumPageBo)); //事物 albumPageBo.setCategory(CommonConstant.IMAGE_CATEGORY_3); jsonObject.put("thing",albumService.getUserAlbumList(albumPageBo)); return ResultBody.success(jsonObject); }
2.7接口7:/api/getUserAlbumList POST
2.7.1参数分析 AlbumPageBo
2.7.2从缓存中拿到用户信息,用户信息为空则返回错误。不为空则拿出userId然后将albumPageBo设置一个userId最后getUserAlbumList()方法传入albumPageBo
/** * 获取用户所有上传的图片(分页) * @return 返回带有文件信息的实体列表(包括大小图、上传时间等) */ // @SaCheckLogin @PostMapping("/getUserAlbumList") public ResultBody getUserAlbumAll(@RequestBody AlbumPageBo pageBo){ //从缓存拿到用户信息 UserBo userInfo = MyUtils.getUserInfo(); if (userInfo == null){ return ResultBody.error(CommonEnum.USER_IS_NULL); } //用户ID String userId = userInfo.getUserId(); pageBo.setUserId(userId); // 业务实现 return albumService.getUserAlbumList(pageBo); }
2.8接口8:/api/getUserAlbumDetail POST
2.8.1从缓存中拿到用户信息,用户信息为空则返回错误。不为空则拿出userId然后将analyzeBo
设置一个userId然后在albumService里实现getUserAlbumDetail()的逻辑,直接调用cloud2ImageService的getMediaInfo()方法即可。
2.9接口9:/api/previewImage Get
2.9.1参数分析:fileId, iconCode这两样可以唯一确定想要的缩略图
2.9.2先组装一个fileResizeIconBo,往里塞入fileId, iconCode两个参数,然后调用albumService的previewImage()方法,里面的业务逻辑是调用cloud2ImageService的getIconUrl()方法获得缩略图的下载地址,然后判断下载地址是否为空,不为空则转发这个url,否则返回null即可。
/** * 获取用户单个图片格式分析信息 * @return 返回带有文件信息的实体列表(包括大小图、上传时间等) */ // @SaCheckLogin @GetMapping("/previewImage") public ResultBody previewImage(@RequestParam String fileId, @RequestParam String iconCode, HttpServletResponse response){ FileResizeIconBo fileResizeIconBo =new FileResizeIconBo(); fileResizeIconBo.setFileId(fileId); fileResizeIconBo.setIconCode(iconCode); // 业务实现 ResultBody resultBody = albumService.previewImage(fileResizeIconBo); String iconUrl = null; if(resultBody.getData()!=null && StringUtils.isNotBlank(resultBody.getData().toString())){ iconUrl = resultBody.getData().toString(); response.setStatus(302); try { response.sendRedirect(iconUrl); } catch (IOException e) { log.error("response.sendRedirect error!" , e); } } return null; }
2.10接口10:/api/getPutUploadUrl POST
2.10.1参数分析:fileUploadBo,因为需要获得上传地址,所以bo里还有FileName、FileMd5、FileSize
2.10.2从缓存中拿到用户信息,用户信息为空则返回错误。不为空则拿出userId然后fileUploadBo
设置一个userId,最后在service写一个getPutUploadUrl()方法,调用cloud2TransService
的getPutUploadUrl()方法并传入FileId、FileName、FileMd5、FileSize参数,看返回值里的是否成功,成功则看返回的数据里有没有storageObjectId,有则是秒传,往fileUploadBo里塞入即可。否则往fileUploadBo里塞入containerId、objectId、url、base64Md5
@Override public ResultBody getPutUploadUrl(FileUploadBo fileUploadBo) { ResultBody resultBody = cloud2TransService.getPutUploadUrl(fileUploadBo.getUserId(), fileUploadBo.getFileName(), fileUploadBo.getFileMd5(), fileUploadBo.getFileSize()); //存进缓存的Key String key = fileUploadBo.getUserId() + ":" + fileUploadBo.getFileMd5(); if (resultBody.getCode().equals(CommonEnum.SUCCESS.getResultCode())) { //成功 JSONObject obj = JSONUtil.parseObj(resultBody.getData()); if (obj.containsKey("storageObjectId")) { //是秒传 fileUploadBo.setStorageObjectId(obj.getStr("storageObjectId")); } else { //不是秒传 fileUploadBo.setContainerId(obj.getStr("containerId")); fileUploadBo.setObjectId(obj.getStr("objectId")); fileUploadBo.setUploadUrl(obj.getStr("url")); fileUploadBo.setBase64Md5(obj.getStr("base64Md5")); } //成功了状态是传输中 fileUploadBo.setStatus(CommonConstant.FILE_UPLOAD_ING); fileUploadBo.setUploadTime(DateUtil.now()); stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(fileUploadBo), 1, TimeUnit.DAYS); }else { //失败了状态是传输失败 fileUploadBo.setStatus(CommonConstant.FILE_UPLOAD_FAIL); fileUploadBo.setUploadTime(DateUtil.now()); stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(fileUploadBo), 1, TimeUnit.DAYS); } return ResultBody.success(fileUploadBo); }
2.11接口11:/api/commitUpload POST
@Override public ResultBody commitUpload(FileUploadBo bo) { //存进缓存的Key String key = bo.getUserId() + ":" + bo.getFileMd5(); ResultBody commitResultBody = cloud2TransService.commit(bo); if (commitResultBody.getCode().equals(CommonEnum.SUCCESS.getResultCode())){ //成功提交 - 状态更新为传输成功 bo.setStatus(CommonConstant.FILE_UPLOAD_SUCCESS); stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(bo), 1, TimeUnit.DAYS); }else { //提及失败 - 状态更新为传输失败 bo.setStatus(CommonConstant.FILE_UPLOAD_FAIL); stringRedisTemplate.opsForValue().set(key, JSONUtil.toJsonStr(bo), 1, TimeUnit.DAYS); } return commitResultBody; }