店铺状态接口
引入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 { @Bean public 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 { @Autowired private RedisTemplate redisTemplate; public static final String Key = "SHOP_STATUS"; @PutMapping("/{status}") public Result setStatus(@PathVariable Integer status){ log.info("设置店铺状态:{}",status); //将店铺状态存入redis redisTemplate.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: itheima user-ttl: 7200000 user-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层开发
@Autowired private UserService userService; // jwt配置属性 @Autowired private 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业务开发
@Autowired private UserMapper userMapper; // 微信小程序的配置属性类 @Autowired private WeChatProperties weChatProperties; // 向微信后台请求地址 public static final String WX_LOGIN = "https://api.weixin.qq.com/sns/jscode2session"; @Override public 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; }
注册拦截器
@Autowired private 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; } }@Autowired private JwtTokenUserInterceptor jwtTokenUserInterceptor; protected void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(jwtTokenUserInterceptor) .addPathPatterns("/user/**") .excludePathPatterns("/user/user/login") .excludePathPatterns("/user/shop/status"); }
购物车开发
购物地址开发
业务为基本增删改查,注意的是在做增删改查的时候首先要考虑好本次数据操作涉及哪几个表,理清整条线的逻辑,再进行业务开发。
订单功能开发
用户下单
该业务就是数据字段相比于前面会大上不少,其它基本和前面一样的增删改查。理清关系就ok。
ServiceImpl
@Override @Transactional public 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; }