苍穹外卖day03
店铺状态接口
引入Redis,因为像存储店铺状态这种只有一个字段(没必要存储在数据库),且登录后台就要被访问的数据(加快查询速度,减少数据库压力)
使用步骤:导入相关maven依赖、配置yml、连接工厂和Key序列化器(这里可以创建一个RedisTemplate做这些事并将其交给ioc容器管理,这样就不用每次用到RedisTemplate都去做一些重复的事情)
依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId><version>2.7.3</version> </dependency>
配置yml
spring:redis:host: ${sky.redis.host}port: ${sky.redis.port}password: ${sky.redis.password}
创建redis配置类选择连接工程和做序列化
@Configuration
public class RedisConfiguration {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate redisTemplate = new RedisTemplate();redisTemplate.setConnectionFactory(redisConnectionFactory); //设置连接工程redisTemplate.setKeySerializer(new StringRedisSerializer()); // 设置key序列化方式return redisTemplate;}
}
写接口(读写店铺状态)
@RestController
@RequestMapping("/admin/shop")
@Slf4j
public class ShopController {@Autowiredprivate RedisTemplate redisTemplate;public static final String Key = "SHOP_STATUS";@PutMapping("/{status}")public Result setStatus(@PathVariable Integer status){log.info("设置店铺状态:{}",status);//将店铺状态存入redisredisTemplate.opsForValue().set(Key,status);return Result.success();}
@GetMapping("/status")public Result<Integer> getStatus(){log.info("获取店铺状态");Integer status = (Integer) redisTemplate.opsForValue().get(Key);return Result.success(status);}
}
用户小程序登录
整个流程
用户请求-> 后端服务器 -> 微信官方的服务器 -> 后端服务器(得到响应,老用户就用微信官方返回的结构和查询本地数据库的数据进行对比,新用户就插入数据到本地) -> 响应给用户登录状态
导入依赖
<dependency><groupId>org.apache.httpcomponents</groupId><artifactId>httpclient</artifactId><version>4.5.13</version> </dependency>
配置yml
sky:
wechat:appid: ${sky.wechat.appid}secret: ${sky.wechat.secret}# 设置jwt签名加密时使用的秘钥-用户端jwt:user-secret-key: itheimauser-ttl: 7200000user-token-name: authentication
根据前端传过来的数据设计DTO
Body 参数 application/json code string { "code": "string" }
package com.sky.dto;
import lombok.Data;
import java.io.Serializable;
/*** C端用户登录*/
@Data
public class UserLoginDTO implements Serializable {
private String code;
}
设计VO...
Controller层开发
@Autowiredprivate UserService userService;
// jwt配置属性@Autowiredprivate JwtProperties jwtProperties;
@RequestMapping("/login")public Result login(@RequestBody UserLoginDTO userLoginDTO) {log.info("用户登录:{}", userLoginDTO);//登录实现User user = userService.login(userLoginDTO);
//生成jwt令牌HashMap<String, Object> claims = new HashMap<>();claims.put(JwtClaimsConstant.USER_ID, user.getId());String token = JwtUtil.createJWT(jwtProperties.getUserSecretKey(), jwtProperties.getUserTtl(), claims);UserLoginVO userLoginVO = UserLoginVO.builder().id(user.getId()).openid(user.getOpenid()).token(token).build();return Result.success(userLoginVO);}
UserServiceImpl业务开发
@Autowiredprivate UserMapper userMapper;
// 微信小程序的配置属性类@Autowiredprivate WeChatProperties weChatProperties;
// 向微信后台请求地址public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session";@Overridepublic User login(UserLoginDTO userLoginDTO) {// 1.调用微信接口服务,获取微信用户信息String code = getOpenid(userLoginDTO.getCode());// 2.判断openid是否为空,如果为空则登录失败if(code == null) {throw new LoginFailedException(MessageConstant.LOGIN_FAILED);}// 3.根据openid查询用户是否为新用户User user = userMapper.getByOpenid(code);//新用户, 自动注册if(user == null){user = User.builder().openid(code).createTime(LocalDateTime.now()).build();userMapper.insert(user);}return user;}private String getOpenid(String code){HashMap<String, String> map = new HashMap<>();map.put("appid",weChatProperties.getAppid());map.put("secret",weChatProperties.getSecret());map.put("js_code",code);map.put("grant_type","authorization_code");String json = HttpClientUtil.doGet(WX_LOGIN, map);
JSONObject jsonObject = JSON.parseObject(json);String openid = jsonObject.getString("openid");return openid;
}
注册拦截器
@Autowiredprivate JwtProperties jwtProperties; /*** 校验jwt** @param request* @param response* @param handler* @return* @throws Exception*/public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {//判断当前拦截到的是Controller的方法还是其他资源if (!(handler instanceof HandlerMethod)) {//当前拦截到的不是动态方法,直接放行return true;} //1、从请求头中获取令牌String token = request.getHeader(jwtProperties.getUserTokenName()); //2、校验令牌try {log.info("jwt校验:{}", token);Claims claims = JwtUtil.parseJWT(jwtProperties.getUserSecretKey(), token);Long userId = Long.valueOf(claims.get(JwtClaimsConstant.USER_ID).toString());log.info("当前用户的id:", userId);BaseContext.setCurrentId(userId);//3、通过,放行return true;} catch (Exception ex) {//4、不通过,响应401状态码response.setStatus(401);return false;}}@Autowiredprivate JwtTokenUserInterceptor jwtTokenUserInterceptor; protected void addInterceptors(InterceptorRegistry registry) {registry.addInterceptor(jwtTokenUserInterceptor).addPathPatterns("/user/**").excludePathPatterns("/user/user/login").excludePathPatterns("/user/shop/status");}
购物车开发
购物地址开发
业务为基本增删改查,注意的是在做增删改查的时候首先要考虑好本次数据操作涉及哪几个表,理清整条线的逻辑,再进行业务开发。
订单功能开发
用户下单
该业务就是数据字段相比于前面会大上不少,其它基本和前面一样的增删改查。理清关系就ok。
ServiceImpl
@Override@Transactionalpublic OrderSubmitVO submitOrder(OrdersSubmitDTO ordersSubmitDTO) {// 此业务的数据变更涉及到以下两张表// 订单表是主表,部分数据是从地址表中获取的,所以需要先查询地址数据// 订单详情表为补充订单信息,部分数据是从购物车中获取的,所以需要查询购物车数据// 而返回给前端打印小票等信息的是订单表的部分主要数据 // 下单要完成:// 1. 下单// 拿到用户地址,判断是否地址为空、超出范围AddressBook addressBook = addressBookMapper.getAddressBookById(ordersSubmitDTO.getAddressBookId());if(addressBook == null){throw new RuntimeException("地址为空");}// 购物车是否为空Long userId = BaseContext.getCurrentId();List<ShoppingCart> ShoppingList = shoppingCartMapper.list(userId);if(ShoppingList.size() == 0){throw new RuntimeException("购物车为空");} //封装好上面查询的地址数据Orders order = new Orders();BeanUtils.copyProperties(ordersSubmitDTO, order);order.setPhone(addressBook.getPhone());order.setAddress(addressBook.getDetail());order.setConsignee(addressBook.getConsignee());order.setNumber(String.valueOf(System.currentTimeMillis()));order.setUserId(userId);order.setStatus(Orders.PENDING_PAYMENT);order.setPayStatus(Orders.UN_PAID);order.setOrderTime(LocalDateTime.now());// 向订单表中插入数据orderMapper.insertOrder(order); //封装好上面查询的菜品数据List<OrderDetail> orderDetailList = new ArrayList<>();for(ShoppingCart cart : ShoppingList){OrderDetail orderDetail = new OrderDetail();BeanUtils.copyProperties(cart, orderDetail);orderDetail.setOrderId(order.getId());orderDetailList.add(orderDetail);}// 向订单明细表中插入数据oderDetailMapper.insertBatch(orderDetailList); // 2. 清空购物车shoppingCartMapper.deleteAllByUserId(userId); // 3. 封装返回结果OrderSubmitVO orderSubmitVO = new OrderSubmitVO();orderSubmitVO.setId(order.getId());orderSubmitVO.setOrderNumber(order.getNumber());orderSubmitVO.setOrderAmount(order.getAmount());orderSubmitVO.setOrderTime(order.getOrderTime());return orderSubmitVO;}
相关文章:
苍穹外卖day03
店铺状态接口 引入Redis,因为像存储店铺状态这种只有一个字段(没必要存储在数据库),且登录后台就要被访问的数据(加快查询速度,减少数据库压力) 使用步骤:导入相关maven依赖、配置…...
文件流---------获取文件的内容到控制台
总流程:先创建一个文本文件------->里面写入一些内容(纯字母和字母加文字)-----------> 然后通过输入流获取文件里面的内容,两种方式。 1.第一种,获取单个的字符 ,先创建文件 ,java.txt…...
【PyTorch项目实战】反卷积(Deconvolution)
文章目录 一、卷积(Convolution)二、反卷积(Deconvolution) —— 又称去卷积1. 反卷积(Richardson-Lucy,RL) —— —— 通过不断迭代更新图像估计值2. 转置卷积(Transpose Convoluti…...
SpringBoot无法访问静态资源文件CSS、Js问题
在做一个关于基于IDEASpringBootMaveThymeleaf的系统实现实验时候遇到了这个问题一直无法解决 后来看到一篇博客终于解决了。 springboot项目在自动生成的时候会有两个文件夹,一个是static,一个是templates,如果我们使用 <dependency><groupI…...
powerbi制作中国式复杂报表
今天主要想实现的功能是使用powerbi制作一个中国式的复杂报表,其中需要多表头,另外需要多个度量值如图我们最终要实现的样式是这样的: 错误示范 因为这些作为多表头的维度需要在同一行上作为不同的列显示所以他们需要来自于同一个字段&#…...
CMake中set_property接口及属性作用详解
在 CMake 中,set_property 是一个用于设置 属性(Property) 的核心命令。属性是 CMake 中用于控制构建过程的核心机制之一,可以理解为与特定对象(如目标、目录、源文件等)关联的键值对,用于存储配…...
设计模式——抽象工厂模式总结
理解了前面的工厂模式后,再理解抽象工厂模式就很容易了。 工厂模式:https://blog.csdn.net/inside802/article/details/147170118?spm1011.2415.3001.10575&sharefrommp_manage_link 抽象工厂模式就是工厂模式的更加抽象化,父类不仅不承…...
ChatGPT-如何让AI写作不那么生硬!
在使用聊天机器人撰写文章时,可能会遇到频繁使用“首先”、“其次”、“再次”等转折连接词,这会让文章显得呆板和机械,降低了阅读体验。 解决这个问题可以尝试以下方式! 多样化连接词: 使用更多多样的连接词和过渡短…...
禁止页面滚动的方法-微信小程序
在微信小程序中,有几种方法可以禁止页面滚动: 一、通过页面配置禁止滚动 在页面的JSON配置文件中设置,此方法完全禁止页面的滚动行为: {"disableScroll": true }二、通过 CSS 样式禁止滚动 在页面的WXSS文件中添加&…...
C++——继承、权限对继承的影响
目录 继承基本概念 编程示例 1.基类(父类)Person 代码特点说明 权限对类的影响 编辑 编程示例 1. 公有继承 (public inheritance) 2. 保护继承 (protected inheritance) 3. 私有继承 (private inheritance) 重要规则 实际应用 继承基本概…...
js中 剩余运算符(Rest Operator )(...)和展开运算符(Spread Operator)(...)的区别及用法
1、基本说明 在JavaScript中,剩余运算符(Rest Operator)和展开运算符(Spread Operator)虽然在某些方面有相似之处,但它们各自有不同的用途和功能。下面详细解释这两种运算符的区别: 1.1. 剩余…...
雅思练习总结(二十六)
雅思练习总结(二十六) 本文章是雅思练习总结(二十六),总结了文章《MAKING EVERYDROP COUNT》,内容包括原文精翻,文章脉络总结,单词扩展学习3个部分 1 文章原文及翻译 MAKING EVERYDROP COUNT 翻译:让每一滴水,都充满价值 A The history of human civilisation i…...
华为手机清理大数据的方法
清理手机最大的问题是,手动和自动清理了多次,花费了很长时间,但是只腾挪出来了一点点空间,还是有很大空间无法使用,这篇文章就告诉你怎样做,以花瓣剪辑为例,如下: 删除数据ÿ…...
单元测试原则之——不要过度模拟
什么是过度模拟? 过度模拟(over-mocking)是指在单元测试中,模拟了太多依赖项,甚至模拟了本不需要模拟的简单对象或行为。过度模拟会导致: 测试代码变得复杂,难以阅读和维护。测试逻辑偏离了实际业务逻辑,无法验证真实代码的行为。忽略了被测单元与依赖项之间的真实交互…...
操作系统基础:07 我们的任务
课程回顾与后续规划 上节课我们探讨了操作系统的历史。了解历史能让我们明智,从操作系统的发展历程中,我们总结出两个核心的里程碑式图像:多进程(多任务切换)图像和文件操作图像 。Unix和Windows等系统的成功…...
微服务的服务调用详解以及常见解决方案对比
微服务服务调用详解 1. 服务调用分类 服务调用根据通信方式、同步性、实现模式可分为以下类型: 按通信协议分类 类型典型协议/框架特点RPC(远程过程调用)Dubbo、gRPC、Apache Thrift高性能、二进制协议、强类型定义HTTP/RESTSpring RestTe…...
Verilog:LED呼吸灯
模块接口说明 信号方向描述clk输入系统时钟(100MHz,周期10ns)rst_n输入低电平有效的异步复位信号led_en输入总使能信号(1开启呼吸灯,0关闭)speed_en输入呼吸速度调节使能信号speed[2:0]输入呼吸速度分级&a…...
一个很好用的vue2在线签名组件
在前端开发的日常工作中,我们常常会遇到需要用户进行在线签名的需求,比如电子合同签署、表单确认等场景。最近,我在项目里使用了一款极为好用的 Vue2 在线签名组件,今天就来和大家分享一下使用心得。 效果图 上代码 在 views 下…...
C语言实现TcpDump
一、 在 C 语言中实现 TCP 抓包功能,通常可以使用 libpcap 库。libpcap 是一个广泛使用的网络抓包库,它提供了捕获网络数据包的接口。 libpcap 是一个广泛使用的 C 语言库,用于捕获和过滤网络数据包。它提供了一个通用接口,用于访…...
吴恩达深度学习复盘(14)迁移学习|项目基本周期
迁移学习 迁移学习是一种机器学习技术,它允许我们将从一个任务中学习到的知识应用到另一个相关的任务中。其核心思想在于,很多情况下,从头开始训练一个模型需要大量的数据和计算资源,而迁移学习能够复用在已有数据上训练好的模型…...
【STM32】STemWin库,使用template API
目录 CubeMX配置 工程文件配置 Keil配置 STemwin配置 GUIConf.c LCDConf.c 打点函数 修改屏幕分辨率 GUI_X.c 主函数 添加区域填充函数 移植过程中需要一些参考手册,如下 STemwin使用指南 emWin User Guide & Reference Manual CubeMX配置 参考驱…...
Matlab Add Legend To Graph-图例添加到图
Add Legeng To Graph: Matlab的legend()函数-图例添加到图 将图例添加到图 ,图例是标记绘制在图上的数据序列的有用方法。 下列示例说明如何创建图例并进行一些常见修改,例如更改位置、设置字体大小以及添加标题。您还可以创建具有多列的图…...
AI基础04-日志数据采集
上篇文章我们学习了视频的数据采集,今天主要了解一下日志数据采集的方法。日志数据采集的目的通常是:调试、运维监控和业务分析。调试主要是工程师在程序异常时针对关键环节把相关参数通过日志打印出来,找出哪个环节出现了问题。运维监控主要…...
文章记单词 | 第29篇(六级)
一,单词释义 AI /ˌeɪ ˈaɪ/ abbr. 人工智能(Artificial Intelligence)inventory /ˈɪnvəntri/ n. 存货清单;财产清单;库存货物;存货;v. 编制目录;开列清单;盘存cha…...
Arduino示例代码讲解:String substring() 字符串子链
Arduino示例代码讲解:String substring 字符串子链 String substring() 字符串子链程序功能概述功能:硬件要求:输出:代码结构`setup()` 函数`loop()` 函数创建字符串:提取子字符串:无限循环:运行过程代码输出解释原始字符串:提取子字符串:注意事项String substring() …...
2025年七星棋牌跨平台完整源码解析(200+地方子游戏+APP+H5+小程序支持,附服务器镜像导入思路)
目前市面上成熟的棋牌游戏源码很多,但能做到平台全覆盖、地方玩法丰富、交付方式标准化的系统却不多。今天这套七星棋牌2023完整源码具备安卓/iOS/H5/微信小程序端四端互通能力,附带200多款地方子游戏,还配备了后台管理与自动热更系统&#x…...
Odoo 部署本地 把現時的excel計算表格部署上odoo 教程
要将现有的 Excel 计算表格部署到 Odoo 平台上,您可以按照以下步骤进行操作: 将 Excel 表格中的数据转移到 Odoo 模块中:首先,您需要将 Excel 表格中的数据导出为 CSV 格式,然后可以使用 Odoo 的数据导入功能将这些数据…...
compose map 源码解析
目录 TileCanvas ZoomPanRotateState ZoomPanRotate 布局,手势处理完了,就开始要计算tile了 MapState TileCanvasState telephoto的源码已经分析过了.它的封装好,扩展好,适用于各种view. 最近又看到一个用compose写的map,用不同的方式,有点意思.分析一下它的实现流程与原…...
Go语言--语法基础4--基本数据类型--整数类型
整型是所有编程语言里最基础的数据类型。 Go 语言支持如下所示的这些整型类型。 需要注意的是, int 和 int32 在 Go 语言里被认为是两种不同的类型,编译器也不会帮你自动做类型转换, 比如以下的例子会有编译错误: var value2 in…...
mysql事务脏读 不可重复读 幻读 事务隔离级别关系
看了很多文档,发现针对事务并发执行过程中的数据一致性问题,即脏读、不可重复读、幻读的解释一塌糊涂,这也不能说什么,因为官方SQL标准中的定义也模糊不清。 按照mysql中遵循的事务隔离级别,可以梳理一下其中的关系 隔…...
