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

修改 changgou-service-user 微服务,需改 com.changgou.user.service.AddressService 接口,添加根据用户名字查询用户收件地址信息
/**
* 根据用户名查询用户的收货地址
*/
List<Address> list(String username);
2
3
4
impl
/**
* 根据用户名查询用户的收货地址
*/
@Override
public List<Address> list(String username) {
Address address = new Address();
address.setUsername(username);
List<Address> addressList = addressMapper.select(address);
return addressList;
}
2
3
4
5
6
7
8
9
10
因为 controller 需要动态获取当前登录人信息 我们需要解析 token 将 changgou-user-oauth 模块 config 的 TokenDecode 复制到 changgou_service_user 下的 config 文件夹中
然后启动类中添加到 bean 中
@Bean
public TokenDecode tokenDecode(){
return new TokenDecode();
}
2
3
4
controller
/**
* 传递用户名 查询用户收货地址集合
*/
@GetMapping("/list")
public Result<List<Address>> list() {
//获取当前登录人的名称
String username = tokenDecode.getUserInfo().get("username");
//查询登录收货地址集合
List<Address> addressList = addressService.list(username);
return new Result<>(true, StatusCode.OK, "查询成功", addressList);
}
2
3
4
5
6
7
8
9
10
11
Postman 访问 http://localhost:8001/api/address/list
# 页面模板渲染

购物车这块也使用的是模板渲染,用户先请求经过微服务网关,微服务网关转发到订单购物车模板渲染服务,模板渲染服务条用用户微服务和订单购物车微服务查询用户收件地址和购物车清单,然后到页面显示。
静态页面导入 将资料中的
order.html拷贝到changgou-web-order工程的 templates 中在 changgou-web-order 中创建
com.changgou.order.controller.OrderController实现页面跳转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
2
3
4
5
6
7
8
9
10
11
12
13修改 changgou-gateway-web 的 application.yml 文件,将订单的路由过滤地址添加上去

改
com.changgou.filter.URLFilter,在 orderFilterPath 里添加/api/worder/**过滤
# 信息查询
因为一会儿要调用 changgou-service-user 查询用户的收件地址信息,调用 changgou-service-order 查询购物车清单信息,所以我们需要创建 Feign。购物车的 Feign 之前已经创建过了,所以只需要创建用户地址相关的即可。
用户地址信息查询 在 changgou-service-user-api 中创建 AddressFeign
package com.changgou.user.feign; import com.changgou.entity.Result; import com.changgou.user.pojo.Address; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import java.util.List; @FeignClient(name = "user") public interface AddressFeign { @GetMapping("/address/list") public Result<List<Address>> list(); }1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16查询购物车和用户收件地址信息
修改 changgou-web-order 中的
com.changgou.order.controller.OrderController的 readyOrder 方法,在该方法中,使用 feign 调用查询收件地址信息和用户购物车信息@Autowired private AddressFeign addressFeign; @Autowired private CartFeign cartFeign; @RequestMapping("/ready/order") public String readyOrder(Model model) { //收件人地址信息 List<Address> addressList = addressFeign.list().getData(); model.addAttribute("address", addressList); //购物车信息 Map map = cartFeign.list(); List<OrderItem> orderItemList = (List<OrderItem>) 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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# 页面回显
order 第 84 行
<div class="step-cont">
<div class="addressInfo">
<ul class="addr-detail">
<li class="addr-item">
<div class="choose-address" th:each="addr:${address}">
<div class="con name" th:classappend="${addr.isDefault}==1?'selected':''"><a
href="javascript:;"><em th:text="${addr.contact}"></em><span
title="点击取消选择"></span></a></div>
<div class="con address">
<span class="place"><em th:text="${addr.address}"></em></span>
<span class="phone"><em th:text="${addr.phone}"></em></span>
<span class="base" th:if="${addr.isDefault}==1">默认地址</span>
</div>
<div class="clearfix"></div>
</div>
</li>
</ul>
<!--确认地址-->
</div>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
第 132 行代码
<div class="sendGoods">
<span>商品清单:</span>
<ul class="yui3-g" th:each="cart:${carts}">
<li class="yui3-u-1-6">
<span><img th:src="${cart.image}" style="max-height: 80px"/></span>
</li>
<li class="yui3-u-7-12">
<div class="desc" th:text="${cart.name}"></div>
<div class="seven">7天无理由退货</div>
</li>
<li class="yui3-u-1-12">
<div class="price">¥<em th:text="${cart.price}"></em></div>
</li>
<li class="yui3-u-1-12">
<div class="num"><em th:text="${cart.num}"></em></div>
</li>
<li class="yui3-u-1-12">
<div class="exit">有货</div>
</li>
</ul>
</div>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
第 188 行
<div class="list">
<span><i class="number"><em th:text="${totalNum}"></em></i>件商品,总商品金额</span>
<em class="allprice" >¥<em th:text="${totalMoney}"></em></em>
</div>
2
3
4
第 203 行
<div class="fc-price">应付金额: <span class="price">¥<em th:text="${totalMoney}"></em></span></div>
# 记录选中收件人
用户每次点击收件人的时候,我们需要记录收件人信息。我们可以使用 Vue,定义一个订单变量,并且每次点击的时候,将该收件人信息传给 Vue 的一个方法在订单变量中记录选中的用户信息即可。
我们要先引入 Vue, 在 order.html 中引入 vue
<script type="text/javascript" src="/js/vue.js"></script>
<script type="text/javascript" src="/js/axios.js"></script>
2
同时在 72 行左右添加一个 id="app" 作为 Vue 入口标签。

定义记录用户信息方法

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

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

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

我们可以在后台加载找出默认的收件人信息,前台通过 Vue 直接绑定给变量即可。
修改 com.changgou.order.controller.OrderController , 添加默认收件人信息判断

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

此时页面可以正常显示用户信息了。
# 支付方式选中
在 data 中的 order 添加 payType 对象

order 页面第 115 行
<ul class="payType">
<li class="selected" th:@click="|order.payType=1|">在线支付<span title="点击取消选择"></span></li>
<li th:@click="|order.payType=0|">货到付款<span title="点击取消选择"></span></li>
</ul>
2
3
4
更改之前购物车点击结算后跳转到 order 页面
在 cart.html 第 189 行
<a class="sum-btn" href="/api/worder/ready/order" target="_blank">结算</a>
# 下单
点击提交订单的时候,会立即创建订单数据,创建订单数据会将数据存入到 2 张表中,分别是订单表和订单明细表,此处还需要修改商品对应的库存数量。

# 下单实现
下单的时候,先往 tb_order 表中增加数据,再往 tb_order_item 表中增加数据。
这里先修改 changgou-service-order 微服务,实现下单操作,这里会生成订单号,我们首先需要在启动类中创建一个 IdWorker 对象。
@Bean
public IdWorker idWorker(){
return new IdWorker(1,1);
}
2
3
4
控制修改层 changgou-service-order 微服务,修改 com.changgou.order.controller.OrderController 类
/***
* 新增数据
* @param order
* @return
*/
@PostMapping
public Result add(@RequestBody Order order){
//获取登陆人名称
String username = tokenDecode.getUserInfo().get("username");
order.setUsername(username);
orderService.add(order);
return new Result(true,StatusCode.OK,"添加成功");
}
2
3
4
5
6
7
8
9
10
11
12
13
业务层 修改订单微服务添加 com.changgou.order.service.impl.OrderServiceImpl
@Autowired
private CartService cartService;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private IdWorker idWorker;
/**
* 增加
*
* @param order
*/
@Override
public void add(Order order) {
//1.获取购物车的数据 从redis中获取
Map cartMap = cartService.list(order.getUsername()); //调用cartService的方法
List<OrderItem> orderItemList = (List<OrderItem>) cartMap.get("orderItemList");
//2.统计计算总金额 和总数量
//3.填充订单数据并保存到tb_order中
order.setTotalNum((Integer) cartMap.get("totalNum")); //总数量
order.setTotalMoney((Integer) cartMap.get("totalMoney")); //总金额
order.setPayMoney((Integer) cartMap.get("totalMoney")); //总支付金额
order.setCreateTime(new Date()); //创建时间
order.setUpdateTime(new Date()); //更新时间
order.setBuyerRate("0"); //是否评价 0未评价 1已评价
order.setSourceType("1"); //来源端 1 web端
order.setOrderStatus("0"); //订单状态 0 未完成 1已完成 2已退货
order.setPayStatus("0"); //支付状态 0未支付 1已支付
order.setConsignStatus("0"); //发货状态 0未发货 1已发货
String orderId = String.valueOf(idWorker.nextId());
order.setId(orderId); //订单id
orderMapper.insert(order);
//4.填充订单项数据并保存到tb_order_item
for (OrderItem orderItem : orderItemList) {
orderItem.setId(String.valueOf(idWorker.nextId()));
orderItem.setIsReturn("0"); //是否退货 0未退货 1已退货
orderItem.setOrderId(orderId); //该商品是依赖订单id
orderItemMapper.insertSelective(orderItem); //向tb_order_item表 插入orderItem
}
//5.删除购物车在redis中的数据
redisTemplate.delete("cart_" + order.getUsername());
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# 渲染服务对接

我们需要在模板渲染端调用订单微服务实现下单操作,下单操作需要调用订单微服务,所以需要创建对应的 Feign。
Feign 创建 修改 changgou-service-order-api,添加 OrderFeign
package com.changgou.order.feign;
import com.changgou.entity.Result;
import com.changgou.order.pojo.Order;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
@FeignClient(name = "order")
public interface OrderFeign {
@PostMapping("/order")
public Result add(@RequestBody Order order);
}
2
3
4
5
6
7
8
9
10
11
12
13
下单调用 修改 changgou-web-order 的 com.changgou.order.controller.OrderController 添加下单方法
@Autowired
private OrderFeign orderFeign;
@PostMapping("/add")
@ResponseBody
public Result add(@RequestBody Order order) {
Result result = orderFeign.add(order);
return result;
}
2
3
4
5
6
7
8
9
页面调用 修改 order.html,增加下单 js 方法,并且在页面点击下单调用
add: function () {
axios.post('/api/worder/add', this.order).then( function (res){
if (res.data.flag){
//添加订单成功
alert("添加订单成功")
}else {
alert("添加订单失败")
}
})
}
2
3
4
5
6
7
8
9
10

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

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

如何控制库存数量 >= 购买数量呢?其实可以通过 SQL 语句实现,每次减少数量之前,加个条件判断。
where num>=#{num} 即可。
商品服务需要查询购物车数据,所以需要引入订单的 api 在 changgou_service_goods 模块导入 changgou_service_order_api
<!--order api 依赖-->
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou_service_order_api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2
3
4
5
6
Dao 层 修改 changgou-service-goods 微服务的 com.changgou.goods.dao.SkuMapper 接口,增加库存递减方法
package com.changgou.goods.dao;
import com.changgou.goods.pojo.Sku;
import com.changgou.order.pojo.OrderItem;
import org.apache.ibatis.annotations.Update;
import tk.mybatis.mapper.common.Mapper;
public interface SkuMapper extends Mapper<Sku> {
//扣减库存并增加销量
@Update("update tb_sku set num=num-#{num},sale_num=sale_num+#{num} where id=#{skuId} and num >=#{num}")
int decrCount(OrderItem orderItem);
}
2
3
4
5
6
7
8
9
10
11
12
业务层 在 changgou-service-order 微服务的 com.changgou.goods.service.SkuService 接口添加 decrCount 方法
//扣减库存并增加销量
void decrCount(String username);
2
修改 changgou-service-order 微服务的 com.changgou.goods.service.impl.SkuServiceImpl 实现类,添加一个实现方法
//扣减库存并增加销量
@Override
public void decrCount (String username){
//1.获取购物车中的数据
List<OrderItem> orderItemList = redisTemplate.boundHashOps("cart_" + username).values();
//循环扣减库存并增加销量
for (OrderItem orderItem : orderItemList) {
int count = skuMapper.decrCount(orderItem); //影响行数
if (count <= 0 ){
throw new RuntimeException("库存不足,请重试");
}
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
控制层 修改 changgou-service-goods 的 com.changgou.goods.controller.SkuController 类,添加库存递减方法
@PostMapping("/decr/count")
public Result decrCount(@RequestParam("usernaem") String username) {
skuService.decrCount(username);
return new Result(true, StatusCode.OK, "库存扣减成功");
}
2
3
4
5
创建 feign 同时在 changgou-service-goods-api 工程添加 com.changgou.goods.feign.SkuFeign 的实现
@PostMapping("/sku/decr/count")
public Result decrCount(@RequestParam("usernaem") String username);
2
# 调用库存递减
修改 changgou-service-order 微服务的启动类 添加 feign 的加载
@Bean
public FeignInterceptor feignInterceptor() {
return new FeignInterceptor();
}
2
3
4
修改 changgou-service-order 微服务的 com.changgou.order.service.impl.OrderServiceImpl 类的 add 方法,增加库存递减的调用。
先注入 SkuFeign
@Autowired
private SkuFeign skuFeign;
2
再调用库存递减方法
//扣减库存并增加销量
skuFeign.decrCount(order.getUsername());
2

修改 changgou-service-goods 微服务的 application 配置 添加 redis 的 host
spring:
redis:
host: 192.168.130.128
2
3
# 增加积分
比如每次下单完成之后,给用户增加 10 个积分,支付完成后赠送优惠券,优惠券可用于支付时再次抵扣。我们先完成增加积分功能。如下表:points 表示用户积分
CREATE TABLE `tb_user` (
`username` varchar(50) NOT NULL COMMENT '用户名',
`password` varchar(100) NOT NULL COMMENT '密码,加密存储',
`phone` varchar(20) DEFAULT NULL COMMENT '注册手机号',
`email` varchar(50) DEFAULT NULL COMMENT '注册邮箱',
`created` datetime NOT NULL COMMENT '创建时间',
`updated` datetime NOT NULL COMMENT '修改时间',
`source_type` varchar(1) DEFAULT NULL COMMENT '会员来源:1:PC,2:H5,3:Android,4:IOS',
`nick_name` varchar(50) DEFAULT NULL COMMENT '昵称',
`name` varchar(50) DEFAULT NULL COMMENT '真实姓名',
`status` varchar(1) DEFAULT NULL COMMENT '使用状态(1正常 0非正常)',
`head_pic` varchar(150) DEFAULT NULL COMMENT '头像地址',
`qq` varchar(20) DEFAULT NULL COMMENT 'QQ号码',
`is_mobile_check` varchar(1) DEFAULT '0' COMMENT '手机是否验证 (0否 1是)',
`is_email_check` varchar(1) DEFAULT '0' COMMENT '邮箱是否检测(0否 1是)',
`sex` varchar(1) DEFAULT '1' COMMENT '性别,1男,0女',
`user_level` int(11) DEFAULT NULL COMMENT '会员等级',
`points` int(11) DEFAULT NULL COMMENT '积分',
`experience_value` int(11) DEFAULT NULL COMMENT '经验值',
`birthday` datetime DEFAULT NULL COMMENT '出生年月日',
`last_login_time` datetime DEFAULT NULL COMMENT '最后登录时间',
PRIMARY KEY (`username`),
UNIQUE KEY `username` (`username`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='用户表';
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
dao 层 修改 changgou-service-user 微服务的 com.changgou.user.dao.UserMapper 接口,增加用户积分方法
/***
* 增加用户积分
* @param username
* @param pint
* @return
*/
@Update("UPDATE tb_user SET points=points+#{point} WHERE username=#{username}")
int addUserPoints(@Param("username") String username, @Param("point") Integer pint);
2
3
4
5
6
7
8
业务层 修改 changgou-service-user 微服务的 com.changgou.user.service.UserService 接口
/***
* 添加用户积分
* @param username
* @param pint
* @return
*/
int addUserPoints(String username,Integer pint);
2
3
4
5
6
7
修改 changgou-service-user 微服务的 com.changgou.user.service.impl.UserServiceImpl ,增加添加积分方法实现
/***
* 修改用户积分
* @param username
* @param pint
* @return
*/
@Override
public int addUserPoints(String username, Integer pint) {
return userMapper.addUserPoints(username,pint);
}
2
3
4
5
6
7
8
9
10
控制层 修改 changgou-service-user 微服务的 com.changgou.user.controller.UserController ,添加增加用户积分方法
@Autowired
private TokenDecode tokenDecode;
/***
* 增加用户积分
* @param points:要添加的积分
*/
@GetMapping(value = "/points/add")
public Result addPoints(Integer points){
//获取用户名
Map<String, String> userMap = tokenDecode.getUserInfo();
String username = userMap.get("username");
//添加积分
userService.addUserPoints(username,points);
return new Result(true,StatusCode.OK,"添加积分成功!");
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Feign 添加 修改 changgou-service-user-api 工程,修改 com.changgou.user.feign.UserFeign ,添加增加用户积分方法
/***
* 添加用户积分
* @param points
* @return
*/
@GetMapping(value = "/points/add")
Result addPoints(@RequestParam(value = "points")Integer points);
2
3
4
5
6
7
# 增加积分调用
修改 changgou-service-order,添加 changgou-service-user-api 的依赖
<!--user api 依赖-->
<dependency>
<groupId>com.changgou</groupId>
<artifactId>changgou-service-user-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
2
3
4
5
6
在增加订单的时候,同时添加用户积分,修改 changgou-service-order 微服务的 com.changgou.order.service.impl.OrderServiceImpl 下单方法,增加调用添加积分方法

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