下单
业务分析
点击提交订单的时候,会立即创建订单数据,创建订单数据会将数据存入到2张表中,分别是订单表和订单明细表,此处还需要修改商品对应的库存数量。
订单表结构如下:
CREATE TABLE `tb_order` ( `id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '订单id', `total_num` int(11) DEFAULT NULL COMMENT '数量合计', `total_money` int(11) DEFAULT NULL COMMENT '金额合计', `pre_money` int(11) DEFAULT NULL COMMENT '优惠金额', `post_fee` int(11) DEFAULT NULL COMMENT '邮费', `pay_money` int(11) DEFAULT NULL COMMENT '实付金额', `pay_type` varchar(1) COLLATE utf8_bin DEFAULT NULL COMMENT '支付类型,1、在线支付、0 货到付款', `create_time` datetime DEFAULT NULL COMMENT '订单创建时间', `update_time` datetime DEFAULT NULL COMMENT '订单更新时间', `pay_time` datetime DEFAULT NULL COMMENT '付款时间', `consign_time` datetime DEFAULT NULL COMMENT '发货时间', `end_time` datetime DEFAULT NULL COMMENT '交易完成时间', `close_time` datetime DEFAULT NULL COMMENT '交易关闭时间', `shipping_name` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流名称', `shipping_code` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流单号', `username` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '用户名称', `buyer_message` varchar(1000) COLLATE utf8_bin DEFAULT NULL COMMENT '买家留言', `buyer_rate` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否评价', `receiver_contact` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人', `receiver_mobile` varchar(12) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人手机', `receiver_address` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '收货人地址', `source_type` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '订单来源:1:web,2:app,3:微信公众号,4:微信小程序 5 H5手机页面', `transaction_id` varchar(30) COLLATE utf8_bin DEFAULT NULL COMMENT '交易流水号', `order_status` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '订单状态,0:未完成,1:已完成,2:已退货', `pay_status` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '支付状态,0:未支付,1:已支付,2:支付失败', `consign_status` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '发货状态,0:未发货,1:已发货,2:已收货', `is_delete` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否删除', PRIMARY KEY (`id`), KEY `create_time` (`create_time`), KEY `status` (`order_status`), KEY `payment_type` (`pay_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
订单明细表结构如下:
CREATE TABLE `tb_order_item` ( `id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT 'ID', `category_id1` int(11) DEFAULT NULL COMMENT '1级分类', `category_id2` int(11) DEFAULT NULL COMMENT '2级分类', `category_id3` int(11) DEFAULT NULL COMMENT '3级分类', `spu_id` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT 'SPU_ID', `sku_id` bigint(20) NOT NULL COMMENT 'SKU_ID', `order_id` bigint(20) NOT NULL COMMENT '订单ID', `name` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品名称', `price` int(20) DEFAULT NULL COMMENT '单价', `num` int(10) DEFAULT NULL COMMENT '数量', `money` int(20) DEFAULT NULL COMMENT '总金额', `pay_money` int(11) DEFAULT NULL COMMENT '实付金额', `image` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '图片地址', `weight` int(11) DEFAULT NULL COMMENT '重量', `post_fee` int(11) DEFAULT NULL COMMENT '运费', `is_return` char(1) COLLATE utf8_bin DEFAULT NULL COMMENT '是否退货,0:未退货,1:已退货', PRIMARY KEY (`id`), KEY `item_id` (`sku_id`), KEY `order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
下单实现
下单的时候,先往tb_order表中增加数据,再往tb_order_item表中增加数据。
代码实现
这里先修改changgou-service-order微服务,实现下单操作,这里会生成订单号,我们首先需要在启动类中创建一个IdWorker对象。
在com.changgou.OrderApplication
中创建IdWorker,代码如下:
@Bean public IdWorker idWorker(){ return new IdWorker(1,1); }
(1)控制层
修改changgou-service-order微服务,修改com.changgou.order.controller.OrderController类,代码如下:
@Autowired private TokenDecode tokenDecode; /*** * 新增Order数据 * @param order * @return */ @PostMapping public Result add(@RequestBody Order order){ //获取用户名 Map<String, String> userMap = tokenDecode.getUserInfo(); String username = userMap.get("username"); //设置购买用户 order.setUsername(username); orderService.add(order); return new Result(true,StatusCode.OK,"添加成功"); }
(2)业务层
实现逻辑:
1)获取所有购物项
2)统计计算:总金额,总数量
3)填充订单数据并保存
4)获取每一个购物项保存到orderItem
5)删除购物车中数据
修改订单微服务添加com.changgou.order.service.impl.OrderServiceImpl,代码如下:
/** * 增加 * @param order */ @Override public void add(Order order){ //1)获取所有购物项 Map cartMap = cartService.list(order.getUsername()); List<OrderItem> orderItemList = (List<OrderItem>) cartMap.get("orderItemList"); //3)填充订单数据并保存 order.setTotalNum((Integer) cartMap.get("totalNum")); order.setTotalMoney((Integer) cartMap.get("totalMoney")); order.setPayMoney((Integer) cartMap.get("totalMoney")); order.setCreateTime(new Date()); order.setUpdateTime(order.getCreateTime()); order.setBuyerRate("0"); //0:未评价,1:已评价 order.setSourceType("1"); //来源,1:WEB order.setOrderStatus("0"); //0:未完成,1:已完成,2:已退货 order.setPayStatus("0"); //0:未支付,1:已支付,2:支付失败 order.setConsignStatus("0"); //0:未发货,1:已发货,2:已收货 order.setId(idWorker.nextId()+""); int count = orderMapper.insertSelective(order); //添加订单明细 for (OrderItem orderItem : orderItemList) { orderItem.setId(idWorker.nextId()+""); orderItem.setIsReturn("0"); orderItem.setOrderId(order.getId()); orderItemMapper.insertSelective(orderItem); } //清除Redis缓存购物车数据 redisTemplate.delete("Cart_"+order.getUsername()); }
渲染服务对接
我们需要在模板渲染端调用订单微服务实现下单操作,下单操作需要调用订单微服务,所以需要创建对应的Feign。
(1)Feign创建
修改changgou-service-order-api,添加OrderFeign,代码如下:
@FeignClient(name="order") public interface OrderFeign { /*** * 提交订单数据 * @param order * @return */ @PostMapping("/order") Result add(@RequestBody Order order); }
(2)下单调用
修改changgou-web-order的com.changgou.order.controller.OrderController
添加下单方法,代码如下:
@Autowired private OrderFeign orderFeign; /*** * 添加订单数据到购物车中 * @param order * @return */ @PostMapping(value = "/add") @ResponseBody public Result add(@RequestBody Order order){ Result result = orderFeign.add(order); return result; }
(3)页面调用
修改order.html,增加下单js方法,并且在页面点击下单调用,代码如下:
点击提交订单调用
保存订单测试,表数据变化如下:
tb_order表数据:
tb_order_item表数据:
库存变更
业务分析
上面操作只实现了下单操作,但对应的库存还没跟着一起减少,我们在下单之后,应该调用商品微服务,将下单的商品库存减少,销量增加。每次订单微服务只需要将用户名传到商品微服务,商品微服务通过用户名到Redis中查询对应的购物车数据,然后执行库存减少,库存减少需要控制当前商品库存>=销售数量。
如何控制库存数量>=购买数量呢?其实可以通过SQL语句实现,每次减少数量之前,加个条件判断。
where num>=#{num}
即可。
商品服务需要查询购物车数据,所以需要引入订单的api,在pom.xml中添加如下依赖:
<!--order api 依赖--> <dependency> <groupId>com.changgou</groupId> <artifactId>changgou_service_order_api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
代码实现
要调用其他微服务,需要将头文件中的令牌数据携带到其他微服务中取,所以我们不能使用hystrix的多线程模式,修改changgou-service-order的applicatin.yml配置,代码如下:
#hystrix 配置 hystrix: command: default: execution: isolation: thread: timeoutInMilliseconds: 10000 strategy: SEMAPHORE
每次还需要使用拦截器添加头文件信息,修改配置类com.changgou.OrderApplication添加拦截器,代码如下:
@Bean public FeignInterceptor feignInterceptor(){ return new FeignInterceptor(); }
(1)Dao层
修改changgou-service-goods微服务的com.changgou.goods.dao.SkuMapper
接口,增加库存递减方法,代码如下:
/** * 递减库存 * @param orderItem * @return */ @Update("UPDATE tb_sku SET num=num-#{num},sale_num=sale_num+#{num} WHERE id=#{skuId} AND num>=#{num}") int decrCount(OrderItem orderItem);
(2)业务层
修改changgou-service-order微服务的com.changgou.goods.service.SkuService
接口,添加如下方法:
/*** * 库存递减 * @param username */ void decrCount(String username);
修改changgou-service-order微服务的com.changgou.goods.service.impl.SkuServiceImpl
实现类,添加一个实现方法,代码如下:
@Autowired private RedisTemplate redisTemplate; /*** * 库存递减 * @param username */ @Override public void decrCount(String username) { //获取购物车数据 List<OrderItem> orderItems = redisTemplate.boundHashOps("Cart_" + username).values(); //循环递减 for (OrderItem orderItem : orderItems) { //递减库存 int count = skuMapper.decrCount(orderItem); if(count<=0){ throw new RuntimeException("库存不足,递减失败!"); } } }
(3)控制层
修改changgou-service-goods的com.changgou.goods.controller.SkuController
类,添加库存递减方法,代码如下:
/*** * 库存递减 * @param username * @return */ @PostMapping(value = "/decr/count") public Result decrCount(@RequestParam("username") String username){ //库存递减 skuService.decrCount(username); return new Result(true,StatusCode.OK,"库存递减成功!"); }
(4)创建feign
同时在changgou-service-goods-api工程添加com.changgou.goods.feign.SkuFeign
的实现,代码如下:
/*** * 库存递减 * @param username * @return */ @PostMapping(value = "/decr/count") Result decrCount(@RequestParam(value = "username") String username);
调用库存递减
修改changgou-service-order微服务的com.changgou.order.service.impl.OrderServiceImpl类的add方法,增加库存递减的调用。
先注入SkuFeign
@Autowired private SkuFeign skuFeign;
再调用库存递减方法
//库存减库存 skuFeign.decrCount(order.getUsername());
完整代码如下:
测试
库存减少前,查询数据库Sku数据如下:个数98,销量0
使用Postman执行 http://localhost:18081/api/order/add
执行测试后,剩余库存97,销量1
增加积分(学员练习)
比如每次下单完成之后,给用户增加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='用户表';
代码实现
(1)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)业务层
修改changgou-service-user微服务的com.changgou.user.service.UserService
接口,代码如下:
/*** * 添加用户积分 * @param username * @param pint * @return */ int addUserPoints(String username,Integer pint);
修改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); }
(3)控制层
修改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,"添加积分成功!"); }
(4)Feign添加
修改changgou-service-user-api工程,修改com.changgou.user.feign.UserFeign
,添加增加用户积分方法,代码如下:
/*** * 添加用户积分 * @param points * @return */ @GetMapping(value = "/points/add") Result addPoints(@RequestParam(value = "points")Integer points);
增加积分调用
修改changgou-service-order,添加changgou-service-user-api的依赖,修改pom.xml,添加如下依赖:
<!--user api 依赖--> <dependency> <groupId>com.changgou</groupId> <artifactId>changgou-service-user-api</artifactId> <version>1.0-SNAPSHOT</version> </dependency>
在增加订单的时候,同时添加用户积分,修改changgou-service-order微服务的com.changgou.order.service.impl.OrderServiceImpl
下单方法,增加调用添加积分方法,代码如下:
修改changgou-service-order的启动类com.changgou.OrderApplication
,添加feign的包路径: