1. Day11 订单结算

2. 订单结算页

2.1. 用户收件地址查询

用户从购物车页面点击结算,跳转到订单结算页,结算页需要加载用户对应的收件地址

11. Day11 订单结算 - 图1

修改changgou-service-user微服务,需改com.changgou.user.service.AddressService接口,添加根据用户名字查询用户收件地址信息

  1. /**
  2. * 根据用户名查询用户的收货地址
  3. */
  4. List<Address> list(String username);

impl

  1. /**
  2. * 根据用户名查询用户的收货地址
  3. */
  4. @Override
  5. public List<Address> list(String username) {
  6. Address address = new Address();
  7. address.setUsername(username);
  8. List<Address> addressList = addressMapper.select(address);
  9. return addressList;
  10. }

因为controller需要动态获取当前登录人信息 我们需要解析token 将changgou-user-oauth模块config的TokenDecode 复制到changgou_service_user下的config文件夹中

然后启动类中添加到bean中

  1. @Bean
  2. public TokenDecode tokenDecode(){
  3. return new TokenDecode();
  4. }

controller

  1. /**
  2. * 传递用户名 查询用户收货地址集合
  3. */
  4. @GetMapping("/list")
  5. public Result<List<Address>> list() {
  6. //获取当前登录人的名称
  7. String username = tokenDecode.getUserInfo().get("username");
  8. //查询登录收货地址集合
  9. List<Address> addressList = addressService.list(username);
  10. return new Result<>(true, StatusCode.OK, "查询成功", addressList);
  11. }

Postman访问 http://localhost:8001/api/address/list

2.2. 页面模板渲染

11. Day11 订单结算 - 图2

购物车这块也使用的是模板渲染,用户先请求经过微服务网关,微服务网关转发到订单购物车模板渲染服务,模板渲染服务条用用户微服务和订单购物车微服务查询用户收件地址和购物车清单,然后到页面显示。

  1. 静态页面导入 将资料中的order.html拷贝到changgou-web-order工程的templates中

  2. 在changgou-web-order中创建com.changgou.order.controller.OrderController实现页面跳转 ```java package com.changgou.web.order.controller;

import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping;

@Controller @RequestMapping(“/worder”) public class OrderController { @RequestMapping(“/ready/order”) public String readyOrder() { return “order”; } }

  1. 3.
  2. 修改changgou-gateway-webapplication.yml文件,将订单的路由过滤地址添加上去
  3. <br />![](https://cdn.jsdelivr.net/gh/Iekrwh/images/md-images/1562923496079.png#alt=1562923496079)
  4. 4.
  5. `com.changgou.filter.URLFilter`,在orderFilterPath里添加`/api/worder/**`过滤
  6. <br />![](https://cdn.jsdelivr.net/gh/Iekrwh/images/md-images/1562923799522.png#alt=1562923799522)
  7. <a name="1778a65d"></a>
  8. ### 2.2.1. 信息查询
  9. 因为一会儿要调用changgou-service-user查询用户的收件地址信息,调用changgou-service-order查询购物车清单信息,所以我们需要创建Feign。购物车的Feign之前已经创建过了,所以只需要创建用户地址相关的即可。
  10. 1.
  11. 用户地址信息查询 changgou-service-user-api中创建AddressFeign
  12. ```java
  13. package com.changgou.user.feign;
  14. import com.changgou.entity.Result;
  15. import com.changgou.user.pojo.Address;
  16. import org.springframework.cloud.openfeign.FeignClient;
  17. import org.springframework.web.bind.annotation.GetMapping;
  18. import java.util.List;
  19. @FeignClient(name = "user")
  20. public interface AddressFeign {
  21. @GetMapping("/address/list")
  22. public Result<List<Address>> list();
  23. }
  1. 查询购物车和用户收件地址信息
    修改changgou-web-order中的com.changgou.order.controller.OrderController的readyOrder方法,在该方法中,使用feign调用查询收件地址信息和用户购物车信息 ```java @Autowired private AddressFeign addressFeign;

@Autowired private CartFeign cartFeign;

@RequestMapping(“/ready/order”) public String readyOrder(Model model) { //收件人地址信息 List

addressList = addressFeign.list().getData(); model.addAttribute(“address”, addressList); //购物车信息 Map map = cartFeign.list(); List orderItemList = (List) map.get(“orderItemList”); Integer totalMoney = (Integer) map.get(“totalMoney”); Integer totalNum = (Integer) map.get(“totalNum”); model.addAttribute(“carts”, orderItemList); model.addAttribute(“totalMoney”, totalMoney); model.addAttribute(“totalNum”, totalNum); return “order”; }

  1. <a name="dcfe6e05"></a>
  2. ### 2.2.2. 页面回显
  3. order第84行
  4. ```html
  5. <div class="step-cont">
  6. <div class="addressInfo">
  7. <ul class="addr-detail">
  8. <li class="addr-item">
  9. <div class="choose-address" th:each="addr:${address}">
  10. <div class="con name" th:classappend="${addr.isDefault}==1?'selected':''"><a
  11. href="javascript:;"><em th:text="${addr.contact}"></em><span
  12. title="点击取消选择"></span></a></div>
  13. <div class="con address">
  14. <span class="place"><em th:text="${addr.address}"></em></span>
  15. <span class="phone"><em th:text="${addr.phone}"></em></span>
  16. <span class="base" th:if="${addr.isDefault}==1">默认地址</span>
  17. </div>
  18. <div class="clearfix"></div>
  19. </div>
  20. </li>
  21. </ul>
  22. <!--确认地址-->
  23. </div>
  24. </div>

第132行代码

  1. <div class="sendGoods">
  2. <span>商品清单:</span>
  3. <ul class="yui3-g" th:each="cart:${carts}">
  4. <li class="yui3-u-1-6">
  5. <span><img th:src="${cart.image}" style="max-height: 80px"/></span>
  6. </li>
  7. <li class="yui3-u-7-12">
  8. <div class="desc" th:text="${cart.name}"></div>
  9. <div class="seven">7天无理由退货</div>
  10. </li>
  11. <li class="yui3-u-1-12">
  12. <div class="price"><em th:text="${cart.price}"></em></div>
  13. </li>
  14. <li class="yui3-u-1-12">
  15. <div class="num"><em th:text="${cart.num}"></em></div>
  16. </li>
  17. <li class="yui3-u-1-12">
  18. <div class="exit">有货</div>
  19. </li>
  20. </ul>
  21. </div>

第188行

  1. <div class="list">
  2. <span><i class="number"><em th:text="${totalNum}"></em></i>件商品,总商品金额</span>
  3. <em class="allprice" >¥<em th:text="${totalMoney}"></em></em>
  4. </div>

第203行

  1. <div class="fc-price">应付金额: <span class="price">¥<em th:text="${totalMoney}"></em></span></div>

2.2.3. 记录选中收件人

用户每次点击收件人的时候,我们需要记录收件人信息。我们可以使用Vue,定义一个订单变量,并且每次点击的时候,将该收件人信息传给Vue的一个方法在订单变量中记录选中的用户信息即可。

我们要先引入Vue,在order.html中引入vue

  1. <script type="text/javascript" src="/js/vue.js"></script>
  2. <script type="text/javascript" src="/js/axios.js"></script>

同时在72行左右添加一个id=”app”作为Vue入口标签。

11. Day11 订单结算 - 图3

定义记录用户信息方法

11. Day11 订单结算 - 图4

修改地址列表,每次点击的时候调用上面的方法

11. Day11 订单结算 - 图5

将选中的地址收件人信息回显到页面输出

11. Day11 订单结算 - 图6

默认收件人加载 用户没有手动选择收件人信息的时候,收件人信息没有初始化

11. Day11 订单结算 - 图7

我们可以在后台加载找出默认的收件人信息,前台通过Vue直接绑定给变量即可。

修改com.changgou.order.controller.OrderController,添加默认收件人信息判断

11. Day11 订单结算 - 图8

vue的data原始数据从session获取

注意script标签要加上th:inline 否则无法从session中获取数据

11. Day11 订单结算 - 图9

此时页面可以正常显示用户信息了。

2.2.4. 支付方式选中

在data中的order添加payType对象

11. Day11 订单结算 - 图10

order页面第115行

  1. <ul class="payType">
  2. <li class="selected" th:@click="|order.payType=1|">在线支付<span title="点击取消选择"></span></li>
  3. <li th:@click="|order.payType=0|">货到付款<span title="点击取消选择"></span></li>
  4. </ul>

更改之前购物车点击结算后跳转到order页面

在cart.html第189行

  1. <a class="sum-btn" href="/api/worder/ready/order" target="_blank">结算</a>

3. 下单

点击提交订单的时候,会立即创建订单数据,创建订单数据会将数据存入到2张表中,分别是订单表和订单明细表,此处还需要修改商品对应的库存数量。

11. Day11 订单结算 - 图11

3.1. 下单实现

下单的时候,先往tb_order表中增加数据,再往tb_order_item表中增加数据。

这里先修改changgou-service-order微服务,实现下单操作,这里会生成订单号,我们首先需要在启动类中创建一个IdWorker对象。

  1. @Bean
  2. public IdWorker idWorker(){
  3. return new IdWorker(1,1);
  4. }

控制修改层 changgou-service-order微服务,修改com.changgou.order.controller.OrderController类

  1. /***
  2. * 新增数据
  3. * @param order
  4. * @return
  5. */
  6. @PostMapping
  7. public Result add(@RequestBody Order order){
  8. //获取登陆人名称
  9. String username = tokenDecode.getUserInfo().get("username");
  10. order.setUsername(username);
  11. orderService.add(order);
  12. return new Result(true,StatusCode.OK,"添加成功");
  13. }

业务层 修改订单微服务添加com.changgou.order.service.impl.OrderServiceImpl

  1. @Autowired
  2. private CartService cartService;
  3. @Autowired
  4. private OrderItemMapper orderItemMapper;
  5. @Autowired
  6. private RedisTemplate redisTemplate;
  7. @Autowired
  8. private IdWorker idWorker;
  9. /**
  10. * 增加
  11. *
  12. * @param order
  13. */
  14. @Override
  15. public void add(Order order) {
  16. //1.获取购物车的数据 从redis中获取
  17. Map cartMap = cartService.list(order.getUsername()); //调用cartService的方法
  18. List<OrderItem> orderItemList = (List<OrderItem>) cartMap.get("orderItemList");
  19. //2.统计计算总金额 和总数量
  20. //3.填充订单数据并保存到tb_order中
  21. order.setTotalNum((Integer) cartMap.get("totalNum")); //总数量
  22. order.setTotalMoney((Integer) cartMap.get("totalMoney")); //总金额
  23. order.setPayMoney((Integer) cartMap.get("totalMoney")); //总支付金额
  24. order.setCreateTime(new Date()); //创建时间
  25. order.setUpdateTime(new Date()); //更新时间
  26. order.setBuyerRate("0"); //是否评价 0未评价 1已评价
  27. order.setSourceType("1"); //来源端 1 web端
  28. order.setOrderStatus("0"); //订单状态 0 未完成 1已完成 2已退货
  29. order.setPayStatus("0"); //支付状态 0未支付 1已支付
  30. order.setConsignStatus("0"); //发货状态 0未发货 1已发货
  31. String orderId = String.valueOf(idWorker.nextId());
  32. order.setId(orderId); //订单id
  33. orderMapper.insert(order);
  34. //4.填充订单项数据并保存到tb_order_item
  35. for (OrderItem orderItem : orderItemList) {
  36. orderItem.setId(String.valueOf(idWorker.nextId()));
  37. orderItem.setIsReturn("0"); //是否退货 0未退货 1已退货
  38. orderItem.setOrderId(orderId); //该商品是依赖订单id
  39. orderItemMapper.insertSelective(orderItem); //向tb_order_item表 插入orderItem
  40. }
  41. //5.删除购物车在redis中的数据
  42. redisTemplate.delete("cart_" + order.getUsername());
  43. }

3.2. 渲染服务对接

11. Day11 订单结算 - 图12

我们需要在模板渲染端调用订单微服务实现下单操作,下单操作需要调用订单微服务,所以需要创建对应的Feign。

Feign创建 修改changgou-service-order-api,添加OrderFeign

  1. package com.changgou.order.feign;
  2. import com.changgou.entity.Result;
  3. import com.changgou.order.pojo.Order;
  4. import org.springframework.cloud.openfeign.FeignClient;
  5. import org.springframework.web.bind.annotation.PostMapping;
  6. import org.springframework.web.bind.annotation.RequestBody;
  7. @FeignClient(name = "order")
  8. public interface OrderFeign {
  9. @PostMapping("/order")
  10. public Result add(@RequestBody Order order);
  11. }

下单调用 修改changgou-web-order的com.changgou.order.controller.OrderController添加下单方法

  1. @Autowired
  2. private OrderFeign orderFeign;
  3. @PostMapping("/add")
  4. @ResponseBody
  5. public Result add(@RequestBody Order order) {
  6. Result result = orderFeign.add(order);
  7. return result;
  8. }

页面调用 修改order.html,增加下单js方法,并且在页面点击下单调用

  1. add: function () {
  2. axios.post('/api/worder/add', this.order).then( function (res){
  3. if (res.data.flag){
  4. //添加订单成功
  5. alert("添加订单成功")
  6. }else {
  7. alert("添加订单失败")
  8. }
  9. })
  10. }

11. Day11 订单结算 - 图13

点击事件

  1. <a class="sui-btn btn-danger btn-xlarge" href="javascript:void(0)" @click="add()">提交订单</a>

11. Day11 订单结算 - 图14

3.3. 库存变更

上面操作只实现了下单操作,但对应的库存还没跟着一起减少,我们在下单之后,应该调用商品微服务,将下单的商品库存减少,销量增加。每次订单微服务只需要将用户名传到商品微服务,商品微服务通过用户名到Redis中查询对应的购物车数据,然后执行库存减少,库存减少需要控制当前商品库存>=销售数量。

11. Day11 订单结算 - 图15

如何控制库存数量>=购买数量呢?其实可以通过SQL语句实现,每次减少数量之前,加个条件判断。

where num>=#{num}即可。

商品服务需要查询购物车数据,所以需要引入订单的api 在changgou_service_goods模块导入changgou_service_order_api

  1. <!--order api 依赖-->
  2. <dependency>
  3. <groupId>com.changgou</groupId>
  4. <artifactId>changgou_service_order_api</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </dependency>

Dao层 修改changgou-service-goods微服务的com.changgou.goods.dao.SkuMapper接口,增加库存递减方法

  1. package com.changgou.goods.dao;
  2. import com.changgou.goods.pojo.Sku;
  3. import com.changgou.order.pojo.OrderItem;
  4. import org.apache.ibatis.annotations.Update;
  5. import tk.mybatis.mapper.common.Mapper;
  6. public interface SkuMapper extends Mapper<Sku> {
  7. //扣减库存并增加销量
  8. @Update("update tb_sku set num=num-#{num},sale_num=sale_num+#{num} where id=#{skuId} and num >=#{num}")
  9. int decrCount(OrderItem orderItem);
  10. }

业务层 在changgou-service-order微服务的com.changgou.goods.service.SkuService接口添加decrCount方法

  1. //扣减库存并增加销量
  2. void decrCount(String username);

修改changgou-service-order微服务的com.changgou.goods.service.impl.SkuServiceImpl实现类,添加一个实现方法

  1. //扣减库存并增加销量
  2. @Override
  3. public void decrCount (String username){
  4. //1.获取购物车中的数据
  5. List<OrderItem> orderItemList = redisTemplate.boundHashOps("cart_" + username).values();
  6. //循环扣减库存并增加销量
  7. for (OrderItem orderItem : orderItemList) {
  8. int count = skuMapper.decrCount(orderItem); //影响行数
  9. if (count <= 0 ){
  10. throw new RuntimeException("库存不足,请重试");
  11. }
  12. }
  13. }

控制层 修改changgou-service-goods的com.changgou.goods.controller.SkuController类,添加库存递减方法

  1. @PostMapping("/decr/count")
  2. public Result decrCount(@RequestParam("usernaem") String username) {
  3. skuService.decrCount(username);
  4. return new Result(true, StatusCode.OK, "库存扣减成功");
  5. }

创建feign 同时在changgou-service-goods-api工程添加com.changgou.goods.feign.SkuFeign的实现

  1. @PostMapping("/sku/decr/count")
  2. public Result decrCount(@RequestParam("usernaem") String username);

3.3.1. 调用库存递减

修改changgou-service-order微服务的启动类 添加feign的加载

  1. @Bean
  2. public FeignInterceptor feignInterceptor() {
  3. return new FeignInterceptor();
  4. }

修改changgou-service-order微服务的com.changgou.order.service.impl.OrderServiceImpl类的add方法,增加库存递减的调用。

先注入SkuFeign

  1. @Autowired
  2. private SkuFeign skuFeign;

再调用库存递减方法

  1. //扣减库存并增加销量
  2. skuFeign.decrCount(order.getUsername());

11. Day11 订单结算 - 图16

修改changgou-service-goods微服务的application 配置 添加redis 的host

  1. spring:
  2. redis:
  3. host: 192.168.130.128

3.4. 增加积分

比如每次下单完成之后,给用户增加10个积分,支付完成后赠送优惠券,优惠券可用于支付时再次抵扣。我们先完成增加积分功能。如下表:points表示用户积分

  1. CREATE TABLE `tb_user` (
  2. `username` varchar(50) NOT NULL COMMENT '用户名',
  3. `password` varchar(100) NOT NULL COMMENT '密码,加密存储',
  4. `phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
  5. `email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
  6. `created` datetime NOT NULL COMMENT '创建时间',
  7. `updated` datetime NOT NULL COMMENT '修改时间',
  8. `source_type` varchar(1) DEFAULT NULL COMMENT '会员来源:1:PC,2:H5,3:Android,4:IOS',
  9. `nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
  10. `name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
  11. `status` varchar(1) DEFAULT NULL COMMENT '使用状态(1正常 0非正常)',
  12. `head_pic` varchar(150) DEFAULT NULL COMMENT '头像地址',
  13. `qq` varchar(20) DEFAULT NULL COMMENT 'QQ号码',
  14. `is_mobile_check` varchar(1) DEFAULT '0' COMMENT '手机是否验证 (0否 1是)',
  15. `is_email_check` varchar(1) DEFAULT '0' COMMENT '邮箱是否检测(0否 1是)',
  16. `sex` varchar(1) DEFAULT '1' COMMENT '性别,1男,0女',
  17. `user_level` int(11) DEFAULT NULL COMMENT '会员等级',
  18. `points` int(11) DEFAULT NULL COMMENT '积分',
  19. `experience_value` int(11) DEFAULT NULL COMMENT '经验值',
  20. `birthday` datetime DEFAULT NULL COMMENT '出生年月日',
  21. `last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
  22. PRIMARY KEY (`username`),
  23. UNIQUE KEY `username` (`username`) USING BTREE
  24. ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';

dao层 修改changgou-service-user微服务的com.changgou.user.dao.UserMapper接口,增加用户积分方法

  1. /***
  2. * 增加用户积分
  3. * @param username
  4. * @param pint
  5. * @return
  6. */
  7. @Update("UPDATE tb_user SET points=points+#{point} WHERE username=#{username}")
  8. int addUserPoints(@Param("username") String username, @Param("point") Integer pint);

业务层 修改changgou-service-user微服务的com.changgou.user.service.UserService接口

  1. /***
  2. * 添加用户积分
  3. * @param username
  4. * @param pint
  5. * @return
  6. */
  7. int addUserPoints(String username,Integer pint);

修改changgou-service-user微服务的com.changgou.user.service.impl.UserServiceImpl,增加添加积分方法实现

  1. /***
  2. * 修改用户积分
  3. * @param username
  4. * @param pint
  5. * @return
  6. */
  7. @Override
  8. public int addUserPoints(String username, Integer pint) {
  9. return userMapper.addUserPoints(username,pint);
  10. }

控制层 修改changgou-service-user微服务的com.changgou.user.controller.UserController,添加增加用户积分方法

  1. @Autowired
  2. private TokenDecode tokenDecode;
  3. /***
  4. * 增加用户积分
  5. * @param points:要添加的积分
  6. */
  7. @GetMapping(value = "/points/add")
  8. public Result addPoints(Integer points){
  9. //获取用户名
  10. Map<String, String> userMap = tokenDecode.getUserInfo();
  11. String username = userMap.get("username");
  12. //添加积分
  13. userService.addUserPoints(username,points);
  14. return new Result(true,StatusCode.OK,"添加积分成功!");
  15. }

Feign添加 修改changgou-service-user-api工程,修改com.changgou.user.feign.UserFeign,添加增加用户积分方法

  1. /***
  2. * 添加用户积分
  3. * @param points
  4. * @return
  5. */
  6. @GetMapping(value = "/points/add")
  7. Result addPoints(@RequestParam(value = "points")Integer points);

3.4.1. 增加积分调用

修改changgou-service-order,添加changgou-service-user-api的依赖

  1. <!--user api 依赖-->
  2. <dependency>
  3. <groupId>com.changgou</groupId>
  4. <artifactId>changgou-service-user-api</artifactId>
  5. <version>1.0-SNAPSHOT</version>
  6. </dependency>

在增加订单的时候,同时添加用户积分,修改changgou-service-order微服务的com.changgou.order.service.impl.OrderServiceImpl下单方法,增加调用添加积分方法

11. Day11 订单结算 - 图17

修改changgou-service-order的启动类com.changgou.OrderApplication,添加feign的包路径

11. Day11 订单结算 - 图18