Day429.订单服务 -谷粒商城

Posted 阿昌喜欢吃黄桃

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Day429.订单服务 -谷粒商城相关的知识,希望对你有一定的参考价值。

订单服务

一、环境搭建

  • 因为我们做的是动静分离,将静态资源上传到nginx

  • 配置本地域名:‪C:\\Windows\\System32\\drivers\\etc\\hosts

  • 配置服务网关:achangmall-gateway/src/main/resources/application.yml
        - id: order_route
          uri: lb://achangmall-order
          predicates:
            - Host=order.achangmall.com
  • 修改静态页面的地址,例如:↓

  • 引入thymeleaf依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
  • 写一个测试的页面跳转的controller

com.achang.achangmall.order.web.HelloController

@Controller
public class HelloController {
    @GetMapping("/{page}.html")
    public String listPage(@PathVariable("page")String page){
        return page;
    }
}
  • 关闭thymeleaf缓存
spring: 
 thymeleaf:
    cache: false
  • 启动订单服务、nginx、网关服务、nacos服务测试

依次访问order.achangmall.com/confirm/detail/list/pay.html,例如:↓

http://order.achangmall.com/confirm.html


二、整合SpringSession

  • 引入依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  • 配置文件
spring:
   session:
     store-type: redis
   redis:
     host: 192.168.109.101
     port: 6379
    
achangmall:
  thread:
    core-size: 20
    max-size: 200
    keep-alive-time: 20
  • 配置类

com.achang.achangmall.order.conf.AchangmallSessionConfig

@Configuration
public class AchangmallSessionConfig {
    @Bean
    public CookieSerializer cookieSerializer() {
        DefaultCookieSerializer cookieSerializer = new DefaultCookieSerializer();
        //放大作用域
        cookieSerializer.setDomainName("achangmall.com");
        cookieSerializer.setCookieName("ACHANGSESSION");
        return cookieSerializer;
    }

    @Bean
    public RedisSerializer<Object> springSessionDefaultRedisSerializer() {
        //使用json存储redis,而不是默认的序列化存储
        return new GenericJackson2JsonRedisSerializer();
    }

}

com.achang.achangmall.order.conf.ThreadPoolConfig

@Configuration
public class ThreadPoolConfig {

    @Bean
    public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool){
        ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(
            pool.getCoreSize(),
            pool.getMaxSize(),
            pool.getKeepAliveTime(),
            TimeUnit.SECONDS, new LinkedBlockingDeque<>(100000),
            Executors.defaultThreadFactory(),
            new ThreadPoolExecutor.AbortPolicy());

        return threadPoolExecutor;
    }

}

com.achang.achangmall.order.conf.ThreadPoolConfigProperties

@Component
@ConfigurationProperties(prefix = "achangmall.thread")
@Data
public class ThreadPoolConfigProperties {
    private Integer coreSize;
    private Integer MaxSize;
    private Integer keepAliveTime;
}
  • 启动类@EnableRedisHttpSession@EnableFeignClients

三、订单基本介绍

1、简介

电商系统涉及到 3 流,分别是信息流资金流物流,而订单系统作为中枢将三者有机的集合起来。

订单模块是电商系统的枢纽,在订单这个环节上需求获取多个模块的数据和信息,同时对这些信息进行加工处理后流向下个环节,这一系列就构成了订单的信息流通。


2、订单构成


3、订单状态

  • 待付款
  • 已付款/ 待发货
  • 待收货/ 已发货
  • 已完成
  • 已取消
  • 售后中

4、订单流程


四、编码

1、订单登录拦截

  • com.achang.achangmall.order.conf.OrderWebConfig
@Configuration
public class OrderWebConfig implements WebMvcConfigurer {

    @Autowired
    private LoginUserInterceptor loginUserInterceptor;

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginUserInterceptor).addPathPatterns("/**");
    }
}
  • com.achang.achangmall.order.interceptor.LoginUserInterceptor
//用户登录拦截器
@Component
public class LoginUserInterceptor implements HandlerInterceptor {

    public static ThreadLocal<MemberResponseVo> loginUser = new ThreadLocal<>();

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        HttpSession session = request.getSession();
        MemberResponseVo user = (MemberResponseVo) session.getAttribute(AuthServerConstant.LOGIN_USER);
        if (user!=null){
            //登录了
            loginUser.set(user);
            return true;
        }else {
            //没登录
            session.setAttribute("msg","请先登录");
            response.sendRedirect("http://auth.achangmall.com/login.html");
            return false;
        }
    }

}

2、订单确认页

  • com.achang.achangmall.order.vo.OrderConfirmVo

订单确认页vo

//订单确认页vo
public class OrderConfirmVo {

    @Getter @Setter
    /** 会员收获地址列表 **/
    List<MemberAddressVo> memberAddressVos;

    @Getter @Setter
    /** 所有选中的购物项 **/
    List<OrderItemVo> items;

    /** 发票记录 **/
    @Getter @Setter
    /** 优惠券(会员积分) **/
    private Integer integration;

    /** 防止重复提交的令牌 **/
    @Getter @Setter
    private String orderToken;

    @Getter @Setter
    Map<Long,Boolean> stocks;

    public Integer getCount() {
        Integer count = 0;
        if (items != null && items.size() > 0) {
            for (OrderItemVo item : items) {
                count += item.getCount();
            }
        }
        return count;
    }


    /** 订单总额 **/
    //BigDecimal total;
    //计算订单总额
    public BigDecimal getTotal() {
        BigDecimal totalNum = BigDecimal.ZERO;
        if (items != null && items.size() > 0) {
            for (OrderItemVo item : items) {
                //计算当前商品的总价格
                BigDecimal itemPrice = item.getPrice().multiply(new BigDecimal(item.getCount().toString()));
                //再计算全部商品的总价格
                totalNum = totalNum.add(itemPrice);
            }
        }
        return totalNum;
    }

    /** 应付价格 **/
    //BigDecimal payPrice;
    public BigDecimal getPayPrice() {
        return getTotal();
    }
}
  • com.achang.achangmall.order.vo.MemberAddressVo
@Data
public class MemberAddressVo {
    private Long id;
    /**
     * member_id
     */
    private Long memberId;
    /**
     * 收货人姓名
     */
    private String name;
    /**
     * 电话
     */
    private String phone;
    /**
     * 邮政编码
     */
    private String postCode;
    /**
     * 省份/直辖市
     */
    private String province;
    /**
     * 城市
     */
    private String city;
    /**
     * 区
     */
    private String region;
    /**
     * 详细地址(街道)
     */
    private String detailAddress;
    /**
     * 省市区代码
     */
    private String areacode;
    /**
     * 是否默认
     */
    private Integer defaultStatus;
}
  • com.achang.achangmall.order.vo.OrderItemVo
@Data
public class OrderItemVo {
    private Long skuId;
    private Boolean check;
    private String title;
    private String image;

    /**
     * 商品套餐属性
     */
    private List<String> skuAttrValues;
    private BigDecimal price;
    private Integer count;
    private BigDecimal totalPrice;

    /** 商品重量 **/
    private BigDecimal weight = new BigDecimal("0.085");
}
  • com.achang.achangmall.order.web.OrderWebController
@Controller
public class OrderWebController {
    @Autowired
    private OrderService orderService;
    @GetMapping("/toTrade.html")
    public String toTrade(Model model){
        OrderConfirmVo vo = orderService.confirmOrder();
        model.addAttribute("orderConfirmData",vo);
        return "confirm";
    }
}
  • com.achang.achangmall.order.feign.MemberFeignService

远程调用用户模块获取数据

@FeignClient("achangmall-member")
public interface MemberFeignService {

    /**
     * 查询当前用户的全部收货地址
     */
    @GetMapping(value = "/member/memberreceiveaddress/{memberId}/address")
    List<MemberAddressVo> getAddress(@PathVariable("memberId") Long memberId);
}
  • com.achang.achangmall.member.controller.MemberReceiveAddressController
@RestController
@RequestMapping("member/memberreceiveaddress")
public class MemberReceiveAddressController {
    @Autowired
    private MemberReceiveAddressService memberReceiveAddressService;

    @GetMapping("/{memberId}/address")
    public List<MemberReceiveAddressEntity> getAddress(@PathVariable("memberId") Long memberId){
        List<MemberReceiveAddressEntity> list =  memberReceiveAddressService.getAddress(memberId);
        return list;
    }
}
  • com.achang.achangmall.member.service.impl.MemberReceiveAddressServiceImpl
@Service("memberReceiveAddressService")
public class MemberReceiveAddressServiceImpl extends ServiceImpl<MemberReceiveAddressDao, MemberReceiveAddressEntity> implements MemberReceiveAddressService {
    
    @Override
    public List<MemberReceiveAddressEntity> getAddress(Long memberId) {
        List<MemberReceiveAddressEntity> list = this.list(new QueryWrapper<MemberReceiveAddressEntity>().eq("member_id", memberId));
        return list;
    }

}
  • com.achang.achangmall.order.feign.CartFeignService
@FeignClient("achangmall-cart")
public interface CartFeignService {

    /**
     * 查询当前用户购物车选中的商品项
     * @return
     */
    @GetMapping(value = "/currentUserCartItems")
    List<OrderItemVo> getCurrentCartItems();

}
@Controller
public class CartController {

    @Resource
    private CartService cartService;

    /**
     * 获取当前用户的购物车商品项
     */
    @GetMapping(value = "/currentUserCartItems")
    @ResponseBody
    public List<CartItemVo> getCurrentCartItems() {
        List<CartItemVo> cartItemVoList = cartService.getUserCartItems();
        return cartItemVoList;
    }
}
  • com.achang.achangmall.cart.service.impl.CartServiceImpl
@Override
public List<CartItemVo> getUserCartItems() {

    List<CartItemVo> cartItemVoList = new ArrayList<>();
    //获取当前用户登录的信息
    UserInfoTo userInfoTo = CartIntercept.toThreadLocal.get();
    //如果用户未登录直接返回null
    if (userInfoTo.getUserId() == null) {
        return null;
    } else {
        //获取购物车项
        String cartKey = CART_PREFIX + userInfoTo.getUserId();
        //获取所有的
        List<CartItemVo> cartItems = getCartItems(cartKey);
        if (cartItems == null) {
            throw new CartExceptionHandler();
        }
        //筛选出选中的
        cartItemVoList = cartItems.stream()
            .filter(items -> items.getCheck())
            .map(item -> {
                //更新为最新的价格(查询数据库)
                BigDecimal price = productFeignService.getPrice(item.getSkuId());
                item.setPrice(price);
                return item;
            })
            .collect(Collectors.toList());
    }

    return cartItemVoList;
}
  • com.achang.achangmall.cart.feign.ProductFeignService
@FeignClient("achangmall-product")
public interface ProductFeignService {
    /**
     * 根据skuId查询当前商品的最新价格
     * @param skuId
     * @return
     */
    @GetMapping(value = "/product/skuinfo/{skuId}/price")
    BigDecimal getPrice(@PathVariable("skuId") Long skuId);
}
  • com.achang.achangmall.product.app.SpuInfoController
@RestController
@RequestMapping("product/spuinfo")
public class SpuInfoController {
    @Autowired
    private SkuInfoService skuInfoService;

    /**
     * 根据skuId查询当前商品的价格
     */
    @GetMapping(value = "/{skuId}/price")
    public BigDecimal getPrice(@PathVariable("skuId") Long skuId) {
        //获取当前商品的信息
        SkuInfoEntity skuInfo = skuInfoService.getById(skuId);
        //获取商品的价格
        BigDecimal price = skuInfo.getPrice();
        return price;
    }
}
  • com.achang.achangmall.order.service.impl.OrderServiceImpl
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {

    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartFeignService cartFeignService;

    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo vo = new OrderConfirmVo();
        MemberResponseVo user = LoginUserInterceptor.loginUser.get();
        //远程获取 对应用户收货地址
        List<MemberAddressVo> address = memberFeignServi

以上是关于Day429.订单服务 -谷粒商城的主要内容,如果未能解决你的问题,请参考以下文章

Java SSM 项目实战 day03 功能介绍,订单的操作,订单的增删改查

Java SSM 项目实战 day04 功能介绍,订单的操作,订单的增删改查,实现登录功能

nodejs 服务器模拟异常状态码429,以及前端vue axios捕获状态码

每天一道大厂SQL题Day03订单量统计

每天一道大厂SQL题Day03订单量统计

Java SSM 项目实战 day05 用户操作