订单功能模块设计与实现
在商城项⽬中,之前我们介绍了购物车功能模块的实现,商品加⼊到购物车之后,就是到购物车结算,然后显⽰购物车的商品列表,点击去结算,然后到了未提交前的订单列表,
点击提交订单后,⽣成此订单,返回订单的订单号,付款⾦额,订单预计到达时间。订单系统是⼀个⾮常重要的系统,我们的移动端、PC 端都需要订单系统,所以这⾥我们将订单系统单独作为⼀个服务来,留出接⼝供客户单来调⽤
今天我们来看下这个订单系统到底是如何实现的:
⼀、订单系统功能
订单系统主要包含哪些功能模块呢?
创建订单功能、查看订单列表、根据订单id查询订单的详细信息、订单修改、订单取消、订单状态、订单评价等功能的实现。
今天我们来看下创建订单的流程:
⼆、订单系统的数据库表的设计
创建订单说到底就是向订单表中添加数据,即insert这些信息。
下单功能⼀定要使⽤关系型数据库表,保证数据的⼀致性,因为创建订单要保证在⼀个事务(⼀个事务就是指向数据库中进⾏的⼀种操作:⽐如插⼊,删除等等)⾥⾯,nosql数据库不⽀持事务,可能会丢失数据。
我们在⽹上购物的时候通常这个订单包含的信息⽐较多,所以对于订单系统如何创建它的数据库也是⾮常重要的。创建数据库遵循数据库设计的三⼤范式原则来设计。
我们创建了三个表:tb_order(订单信息表),tb_order_item(订单详情表),tb_order_shipping(订单配送表).
tb_order:这⾥包含了订单的基本信息
tb_order_item:订单详情表:订单的详情主要就是购买商品的信息,通过订单的id来实现关联
tb_order_shipping:订单配送表:
这是三个基本的表,其实还可以有物流信息表,订单交易信息表。这⾥我们采⽤这三张表⾜够。
三、订单系统接⼝⽂档,⼀般我们开发的时候会收到已经写好的接⼝⽂档,⽐如创建订单的接⼝⽂档。
从上⾯这个表中,我们可以看到该接⼝的url,接⼝的传⼊参数和返回值。
接下来我们针对这三个来进⾏代码的编写:
url属于controller层,
传⼊参数这⾥我们可以看到是数据库建⽴的三张表信息:第⼀个是tb_order,第⼆个是⼀个集合式的订单明细List,第三个是订单的配送信息表。
所以传⼊参数就是这三个对象。这⾥我们是编写接⼝,供客户端调⽤,⾄于客户端怎么将这些参数传递过来,那是客户端团队考虑的事情。
返回值这⾥使⽤了taotaoresult来包装了下,因为我们提交订单成功后,返回的是订单号,即订单的id所以,我们需要向客户端传递订单id过去,并显⽰在订单创建成功的页⾯。
下⾯看下订单服务接⼝的service层的实现:
service层的主要实现是将订单信息添加到数据库中,即接收controller传递过来的对象,然后补全页⾯没有的字段,insert数据库,这⾥可以使⽤逆向⼯程⽣成的dao。
另外还有个问题:
订单编号:订单编号⽤什么形式⽐较好呢?
解决⽅案⼀(不能使⽤):
使⽤mysql的⾃增长。
优点:不需要我们⾃⼰⽣成订单号,mysql会⾃动⽣成。
缺点:如果订单表数量太⼤时需要分库分表,此时订单号会重复。如果数据备份后再恢复,订单号会变。
⽅案⼆:⽇期+随机数
采⽤毫秒+随机数。
缺点:仍然有重复的可能。不建议采⽤此⽅案。在没有更好的解决⽅案之前可以使⽤。
⽅案三:使⽤UUID
优点:不会重复。
缺点:长。可读性查。不建议使⽤。
⽅案四:可读性好,不能太长。⼀般订单都是全数字的。可以使⽤redis的incr命令⽣成订单号。优点:可读性好,不会重复
缺点:需要搭建redis服务器。
所以我们选取⽅案四作为⽣成订单号的⽅案。
那么service层的编码如下:
1
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 36package der.service.impl;
import java.util.Date;
import java.util.List;
import org.apachemons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service;
import com.taotaomon.utils.TaotaoResult;
import com.taotao.mapper.TbOrderItemMapper;
import com.taotao.mapper.TbOrderMapper;
import com.taotao.mapper.TbOrderShippingMapper;
import der.dao.JedisClient;
import der.service.OrderService;
import com.taotao.pojo.TbOrder;
import com.taotao.pojo.TbOrderItem;
import com.taotao.pojo.TbOrderShipping;
//订单模块实现
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private TbOrderMapper orderMapper;
@Autowired
private TbOrderItemMapper orderItemMapper;
@Autowired
private TbOrderShippingMapper shippingMapper;
@Autowired
private JedisClient jedisClient;
@Value("${ORDER_GEN_KEY}")
private String ORDER_GEN_KEY;
@Value("${ORDER_DEFAULT_KEY}")
private String ORDER_DEFAULT_KEY;
@Value("${ORDER_ITEM_KEY}")
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80    private String ORDER_ITEM_KEY;
//创建订单功能实现
@Override
public TaotaoResult createOrder(TbOrder order,List<TbOrderItem> orderItem, TbOrderShipping orderShipping) {
//逻辑步骤:创建订单即向数据库中三个表中插⼊数据,所以讲数据库中的字段补全即可。页⾯传递过来的就不需要补全了,直接可以写⼊数据库
/*第⼀步:订单号的⽣成⽅式:因为订单号的特殊性,订单后最好采⽤数字组成,且不能重复,可以采⽤redis中⾃增长的⽅式来实现,
* 在redis中,如果⽆key,则redis⾃动创建你写的key的名字。采⽤incr的命令来实现订单号的⾃增。默认⾃增是从1开始的,因为考虑到⽤户的原因,在redis中设置⼀个默认的key值
* 这样⽤户体验会好些,不会因为看到简单的1,2,3,所以设置⼀个默认的key值
*/
String string = (ORDER_GEN_KEY);
if(StringUtils.isBlank(string)) {
//如果redis中的key为空,则利⽤默认值
String defaultKey = jedisClient.set(ORDER_GEN_KEY,ORDER_DEFAULT_KEY);
}
//如果存在此key则,采⽤incr命令⾃增
long orderId= jedisClient.incr(ORDER_GEN_KEY);
//然后向订单表中插⼊数据
order.setOrderId(orderId+"");
//订单状态:状态:1、未付款,2、已付款,3、未发货,4、已发货
order.setStatus(1);
order.setCreateTime(new Date());
order.setUpdateTime(new Date());
order.setBuyerRate(0);
//补全完信息后,插⼊数据库表
orderMapper.insert(order);
//补全完订单表后,将订单明细表补全,因为之前订单写⼊redis,订单明细的id也写⼊redis吧,⾃动创建这个id的key。这个不需要默认编号了。对⽐页⾯传递的参数,将剩下的补全
String string2 = (ORDER_ITEM_KEY);
long orderItemId = jedisClient.incr(string2);
//因为传递过来的是⼀个集合列表,所以遍历列表
for(TbOrderItem tbOrderItem : orderItem) {
tbOrderItem.setId(orderItemId+"");
tbOrderItem.setOrderId(orderId+"");
orderItemMapper.insert( tbOrderItem);
}
//接下来补全订单配送表
orderShipping.setOrderId(orderId+"");
orderShipping.setCreated(new Date());
orderShipping.setUpdated(new Date());
shippingMapper.insert(orderShipping);
return TaotaoResult.ok(orderId);
}
}
 controller:层实现
controller需要将对象传递给service层:(客户端向服务器端传⼊的参数格式,详见后⾯博⽂)
spring framework是什么系统我们看到接⼝⽂档中,controller接收的参数是⼀个json格式的字符串,也就是说客户端传递过来的是json格式的字符串。
这就涉及到springMVC是如何接收json字符串的,需要⽤到@RequestBody注解。这⾥多说⼏句:
@ResponseBody注解的原理是response只能响应⼀个字符串,当我们的返回值是java对象的时候,它有⼀个默认⾏为,即利⽤jackson包将java对象转为字符串响应。这是⼀个默认⾃动的⾏为,不需要我们设置,只要这个注解即可。
@RequestBody注解同理:利⽤这个注解告诉springMVC我们现在接收的是⼀个json字符串,需要采取默认⾏为利⽤jackson包将json字符串转换为java对象,所以controller层我们需要⼀个java对象的pojo。
1 2 3 4 5 6 7 8 9 10package der.pojo;
import java.util.List;
import com.taotao.pojo.TbOrder; import com.taotao.pojo.TbOrderItem; import com.taotao.pojo.TbOrderShipping;
public class Order extends TbOrder {
private List<TbOrderItem> orderItems;

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。