目录
1.功能展示
用户在购物车点击去结算
点击提交订单
会创建一个订单
2.数据库表分析
在数据库中涉及到三张表
tb_order、tb_order_item、tb_order_shipping.
在tb_order表中
CREATE TABLE `tb_order` (
`order_id` varchar(50) COLLATE utf8_bin NOT NULL DEFAULT '' COMMENT '订单id',
`payment` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '实付金额。精确到2位小数;单位:元。如:200.07,表示:200元7分',
`payment_type` int(2) DEFAULT NULL COMMENT '支付类型,1、在线支付,2、货到付款',
`post_fee` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '邮费。精确到2位小数;单位:元。如:200.07,表示:200元7分',
`status` int(10) DEFAULT NULL COMMENT '状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭',
`create_time` datetime DEFAULT NULL COMMENT '订单创建时间',
`update_time` datetime DEFAULT NULL COMMENT '订单更新时间',
`payment_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 '物流单号',
`user_id` bigint(20) DEFAULT NULL COMMENT '用户id',
`buyer_message` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '买家留言',
`buyer_nick` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '买家昵称',
`buyer_rate` int(2) DEFAULT NULL COMMENT '买家是否已经评价',
PRIMARY KEY (`order_id`),
KEY `create_time` (`create_time`),
KEY `buyer_nick` (`buyer_nick`),
KEY `status` (`status`),
KEY `payment_type` (`payment_type`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
可以看到:
- 主键order_id是字符串类型,不是自增长的,因此我们需要自己生成订单编号,我们平时使用京东、天猫等购物网站,发现人家的订单号都是用数字组成的,我们也使用数字作为订单号,但是怎样才能使订单号不重复呢?用时间加随机数的方案生成的订单其实还是可能会重复的,当同一时刻生成的订单越多越有可能出现订单号一样的情况,因此我们不能使用这种方案。比较好的方案是什么呢?是用redis的incr方法,由于redis中每一个操作都是单线程的,所以每一个操作都是具有原子性的,因此不会出现编号重复的问题
- payment字段是实付金额,需要从前台传过来,保留小数点后2位。
- payment_type是支付类型,分为在线支付和货到付款,也需要从前台页面传过来。
- post_free字段是邮费,邮费得由前台传过来,因为很多电商都搞活动,买够多少钱的东西就免邮费,因此邮费是动态变化的。
- status字段是订单状态,订单状态我们暂且定义了6种状态,未付款、已付款、未发货、已发货、交易成功、交易关闭。
- create_time字段是订单创建时间,这没什么可说的。
- update_time字段是订单更新时间,这个通常是订单状态发生了变化。
- payment_time字段是付款时间。
- consign_time字段是发货时间。
- end_time字段是交易完成时间,这个通常是用户点确认收货的时间。
- close_time字段是交易关闭时间,交易关闭时间则是该订单的所有流程都走完后的时间。
- shipping_name字段是物流名称,即用的谁家的快递。
- shipping_code字段是物流单号,这个不用废话。
- user_id字段当然是指购买者ID。
- buyer_message字段是指买家留言。
- buyer_nick字段指买家昵称。
- buyer_rate字段记录买家是否已经评价。
表中还可以看到create_time、buyer_nick、status、payment_type这四个字段由key修饰,说明为这四个字段建立了索引。
在tb_order_item表中
CREATE TABLE `tb_order_item` (
`id` varchar(20) COLLATE utf8_bin NOT NULL,
`item_id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '商品id',
`order_id` varchar(50) COLLATE utf8_bin NOT NULL COMMENT '订单id',
`num` int(10) DEFAULT NULL COMMENT '商品购买数量',
`title` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品标题',
`price` bigint(50) DEFAULT NULL COMMENT '商品单价',
`total_fee` bigint(50) DEFAULT NULL COMMENT '商品总金额',
`pic_path` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品图片地址',
PRIMARY KEY (`id`),
KEY `item_id` (`item_id`),
KEY `order_id` (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
从订单表中可以看到订单表中并没有购买商品详情信息,那么商品详情信息在哪儿存放呢?它被存放到了tb_order_item表中,主键id字段也是个字符串,我们也需要为其生成主键,同样使用redis的incr。
在tb_order_shipping表中
CREATE TABLE `tb_order_shipping` (
`order_id` varchar(50) NOT NULL COMMENT '订单ID',
`receiver_name` varchar(20) DEFAULT NULL COMMENT '收货人全名',
`receiver_phone` varchar(20) DEFAULT NULL COMMENT '固定电话',
`receiver_mobile` varchar(30) DEFAULT NULL COMMENT '移动电话',
`receiver_state` varchar(10) DEFAULT NULL COMMENT '省份',
`receiver_city` varchar(10) DEFAULT NULL COMMENT '城市',
`receiver_district` varchar(20) DEFAULT NULL COMMENT '区/县',
`receiver_address` varchar(200) DEFAULT NULL COMMENT '收货地址,如:xx路xx号',
`receiver_zip` varchar(6) DEFAULT NULL COMMENT '邮政编码,如:310001',
`created` datetime DEFAULT NULL,
`updated` datetime DEFAULT NULL,
PRIMARY KEY (`order_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
这张表存放的是订单物流信息,包括收货人姓名、固定电话、移动电话、省、市、区/县、街道门牌号、邮政编码,而且收货人信息与订单是一对一的关系,因此收货地址表的主键是order_id。
3.前端如何传递三张表的数据
点击提交订单时,会触发$('#orderForm').submit()函数,使用id选择器来得到表单,并且将该表单提交。
那么,表单在哪儿呢?我们搜索"orderForm",如下图所示,可以看到这个表单所有的标签都是隐藏的,是不会被用户看到的,用户看到的只是表单下面展示的信息(这些信息只是做展示用,不会被提交,真正提交的是被隐藏的表单)。表单要提交的话,我们一般用pojo来接收比较合适,那么这个表单我们应该用什么样的pojo来接收呢?先来看看这个表单都有那些数据。
在这个表单中包含了tb_order、tb_order_item、tb_order_shipping三张表的数据,其中红色线框起来的支付方式paymentType、支付金额payment属于tb_order,黑色线框起来的商品信息属于tb_order_item,黄色线框起来的订单物流信息属于tb_order_shipping表。这里暂时将tb_order_shipping表的数据写死,支付方式也默认使用“1”。
综合以上情况,我们来写个pojo类包含这些表单信息,那么我们这个pojo应该放到哪儿比较合适呢?我们不能把它放到taotao-common当中,因为我们的taotao-order工程已经依赖了taotao-common工程了,如果taotao-common工程现在再依赖taotao-order,那么便成了相互依赖了,这是断不可行的。我们还想让它尽可能的共用,把它放到taotao-order-interface工程比较合适,因为taotao-order工程及taotao-order-web工程都依赖taotao-order-interface,因此把pojo写到taotao-order-interface工程比较合适。
pojo类如下图所示,这里用到了一个技巧,那就是继承了TbOrder类,这样OrderInfo便直接拥有了TbOrder的属性。为了让该pojo在网络中传输,我们需要让它实现序列化接口。
package com.taotao.order.pojo;
import java.io.Serializable;
import java.util.List;
import com.taotao.pojo.TbOrder;
import com.taotao.pojo.TbOrderItem;
import com.taotao.pojo.TbOrderShipping;
public class OrderInfo extends TbOrder implements Serializable{
/**
*
*/
private static final long serialVersionUID = 1L;
//订单商品表实体的集合
private List<TbOrderItem> orderItems;
//订单物流
private TbOrderShipping orderShipping;
public List<TbOrderItem> getOrderItems() {
return orderItems;
}
public void setOrderItems(List<TbOrderItem> orderItems) {
this.orderItems = orderItems;
}
public TbOrderShipping getOrderShipping() {
return orderShipping;
}
public void setOrderShipping(TbOrderShipping orderShipping) {
this.orderShipping = orderShipping;
}
}
4.生成订单实现
既然表和接收表单的pojo都有了,代码就好写了。
4.1服务层
4.1.1dao层
直接使用逆向工程。
4.1.2service层
在taotao-order-interface创建接口
package com.taotao.order.service;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.order.pojo.OrderInfo;
public interface OrderService {
//生成订单,OrderInfo当中包含了表单提交的所有数据。
TaotaoResult createOrder(OrderInfo orderInfo);
}
在taotao-order-service中
由于要操作redis所以需要jedis的依赖,在pom.xml中添加依赖
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
代码中还用到了常量,我们把常量放到配置文件中,如下图所示。
#订单生成key
ORDER_ID_GEN_KEY=ORDER_ID_GEN
#订单号初始值
ORDER_ID_INIT_VALUE=100888
#订单明细表主键生成key
ORDER_ITEM_ID_GEN_KEY=ORDER_ITEM_ID_GEN
还需要添加properties文件扫描
创建com.taotao.order.service.impl包存放实现类
package com.taotao.order.service.impl;
import java.util.Date;
import java.util.List;
import javax.annotation.Resource;
import javax.jms.Destination;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Session;
import javax.jms.TextMessage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.MessageCreator;
import org.springframework.stereotype.Service;
import com.taotao.common.pojo.TaotaoResult;
import com.taotao.mapper.TbOrderMapper;
import com.taotao.mapper.TbOrderItemMapper;
import com.taotao.mapper.TbOrderShippingMapper;
import com.taotao.order.jedis.JedisClient;
import com.taotao.order.pojo.OrderInfo;
import com.taotao.order.service.OrderService;
import com.taotao.pojo.TbOrderItem;
import com.taotao.pojo.TbOrderShipping;
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private TbOrderMapper tbOrderMapper;
@Autowired
private TbOrderItemMapper tbOrderItemMapper;
@Autowired
private TbOrderShippingMapper tbOrderShippingMapper;
@Autowired
private JedisClient jedisClient;
/** 订单表生成id的key */
@Value("${ORDER_ID_GEN_KEY}")
private String ORDER_ID_GEN_KEY;
/** 订单表生成id的初始值 */
@Value("${ORDER_ID_INIT_VALUE}")
private String ORDER_ID_INIT_VALUE;
/** 订单项表生成id的key */
@Value("${ORDER_ITEM_ID_GEN_KEY}")
private String ORDER_ITEM_ID_GEN_KEY;
/** 发消息 */
@Autowired
private JmsTemplate jmsTemplate;
@Resource(name = "topicDestination")
private Destination topicDestination;
/**
* 创建订单
*/
@Override
public TaotaoResult createOrder(OrderInfo orderInfo) {
// 1.插入订单表tb_order
if (!jedisClient.exists(ORDER_ID_GEN_KEY)) {
jedisClient.set(ORDER_ID_GEN_KEY, ORDER_ID_INIT_VALUE);
}
String orderId = jedisClient.incr(ORDER_ID_GEN_KEY).toString();
// 补全orderInfo属性
// 订单创建日期
orderInfo.setCreateTime(new Date());
// 订单编号
orderInfo.setOrderId(orderId);
// 订单邮费
orderInfo.setPostFee("0");
// 订单状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭
orderInfo.setStatus(1);
// 订单更新时间
orderInfo.setUpdateTime(orderInfo.getCreateTime());
// 在controller设置
// info.setBuyerNick(buyerNick);买家昵称
// info.setUserId(userId);用户id
tbOrderMapper.insert(orderInfo);
// 2.插入订单项表tb_order_item
List<TbOrderItem> orderItems = orderInfo.getOrderItems();
for (TbOrderItem tbOrderItem : orderItems) {
// 补全属性
String orderItemId = jedisClient.incr(ORDER_ITEM_ID_GEN_KEY).toString();
// 设置订单项id
tbOrderItem.setId(orderItemId);
// 设置订单项所属订单id
tbOrderItem.setOrderId(orderId);
tbOrderItemMapper.insert(tbOrderItem);
}
// 3.插入订单物流表tb_order_shipping
TbOrderShipping orderShipping = orderInfo.getOrderShipping();
// 补全属性
// 设置订单物流表所属订单id
orderShipping.setOrderId(orderId);
orderShipping.setCreated(orderInfo.getCreateTime());
orderShipping.setUpdated(orderInfo.getUpdateTime());
tbOrderShippingMapper.insert(orderShipping);
// 4.返回需要包含订单的ID
return TaotaoResult.ok(orderId);
}
}
在applicationContext-service.xml发布服务
<!-- 声明需要暴露的服务接口 -->
<dubbo:service interface="com.taotao.order.service.OrderService" ref="orderServiceImpl" timeout="300000"/>
4.2表现层
4.2.1引入服务
在springmvc.xml中引入服务
<dubbo:reference interface="com.taotao.order.service.OrderService" id="orderService" timeout="300000"/>
4.2.2controller
请求的url:/order/create
参数:使用OrderInfo接收
返回值:逻辑视图。(页面应该显示订单号)
业务逻辑:
- 接收表单提交的数据OrderInfo。
- 补全用户信息。
- 调用Service创建订单。
- 返回逻辑视图展示成功页面
- 需要Service返回订单号
- 当前日期加三天。
/**
* 创建订单
* @param orderInfo
* @param request
* @return
*/
@RequestMapping(value="/order/create",method=RequestMethod.POST)
public String createOrder(OrderInfo orderInfo,HttpServletRequest request) {
//1.接收表单提交的OrderInfo
//2.补全OrderInfo信息
TbUser tbUser = (TbUser)request.getAttribute("USER_INFO");
orderInfo.setUserId(tbUser.getId());
orderInfo.setBuyerNick(tbUser.getUsername());
//3.调用service创建订单
TaotaoResult result = orderService.createOrder(orderInfo);
//4.设置回显属性:orderId、payment、date
request.setAttribute("orderId", result.getData());
request.setAttribute("payment", orderInfo.getPayment());
DateTime dateTime = new DateTime();
DateTime plusDays = dateTime.plusDays(3);//加3天
request.setAttribute("date", plusDays.toString("yyyy-MM-dd"));
//5.返回逻辑视图展示成功页面
return "success";
}
4.3测试访问
安装taotao-order
启动
用户在购物车点击去结算
点击提交订单
会创建一个订单
数据库中已存在数据
本文参考:学习淘淘商城第一百零四课(生成订单)