一、引言

  上一专题已经为网上书店实现了购物车的功能了,在这一专题中,将继续对网上书店案例进行完善,本专题将对网上书店订单功能的实现进行介绍,现在废话不多说了,让我们来一起看看订单功能是如何实现的吧。

二、订单功能的实现思路

  在网上购过物的朋友,对于订单功能的流程自然不陌生,这里我还是先来梳理下下订单的一个流程:

  • 用户点击我的购物车,可以勾选对应的商品进行结算
  • 在结算页面可以提交订单来创建一个订单
  • 创建订单成功之后就是进行付款了。

  一般购物网站下订单的流程分上面3步,由于在本案例中并没有对接第三方的支付平台,所以这里就没有上面第三步的过程了。即在网上书店案例中订单提交成功之后就是已付款状态。

  从上面下订单流程我们就可以知道订单功能的实现思路:

  • 用户点击购物车中购买商品按钮来进行下订单,此时触发控制器中的结算方法进行调用
  • 结算方法通过调用OrderService服务中的结算方法
  • 由于订单的创建涉及了3个实体操作,包括购物车实体,购物车项实体和订单实体。所以这里需要引入领域服务。因为创建订单这个操作涉及了多个实体,则这个业务逻辑放在每个实体中都不合适,因为并属于单独一个实体的逻辑。所以这里引入领域服务来实现这种涉及多个实体的操作。
  • 则OrderService服务中的结算方法通过调用领域服务中的CreateOrder方法来完成订单创建的功能。
  • 领域服务中可以调用对应的实体仓储来完成实体的持久化。这里需要注意的一点:因为领域服务涉及多个实体的持久化,则需要引入工作单元模式将这些实体的操作进行统一提交,要不都成功,要不都不成功。这也就是引入工作单元初衷。

  上面的思路就是订单功能的实现思路。有了上面的思路之后,实现订单功能也一目了然了。下面让我们一起在网上书店案例中实现下看看。

三、网上书店订单功能的实现

  这里我们按照上面的实现思路由下至上地去实现订单功能。

  1. 首先我们需要订单仓储来完成订单实体的持久化。具体订单仓储接口和实现如下代码所示:
// 订单仓储接口
public interface IOrderRepository : IRepository<Order>
{
} // 订单仓储的实现类
public class OrderRepository : EntityFrameworkRepository<Order>, IOrderRepository
{
public OrderRepository(IRepositoryContext context) : base(context)
{
}
}

  2. 领域服务的实现。具体的领域服务接口和实现代码如下所示:

// 领域服务接口
public interface IDomainService
{
Order CreateOrder(User user, ShoppingCart shoppingCart);
} // 领域服务类型
// 牵涉到多个实体的操作可以把这些操作封装到领域服务里
public class DomainService : IDomainService
{
private readonly IRepositoryContext _repositoryContext;
private readonly IShoppingCartItemRepository _shoppingCartItemRepository;
private readonly IOrderRepository _orderRepository; /// <summary>
/// 创建订单,涉及到的操作有2个:1. 把购物车中的项中购物车移除; 2.创建一个订单。
/// 这两个操作必须同时完成或失败。
/// </summary>
/// <param name="user"></param>
/// <param name="shoppingCart"></param>
/// <returns></returns>
public Order CreateOrder(User user, ShoppingCart shoppingCart)
{
var order = new Order();
var shoppingCartItems =
_shoppingCartItemRepository.GetAll(
new ExpressionSpecification<ShoppingCartItem>(s => s.ShoopingCart.Id == shoppingCart.Id));
if (shoppingCartItems == null || !shoppingCartItems.Any())
throw new InvalidOperationException("购物篮中没有任何物品"); order.OrderItems = new List<OrderItem>();
foreach (var shoppingCartItem in shoppingCartItems)
{
var orderItem = shoppingCartItem.ConvertToOrderItem();
orderItem.Order = order;
order.OrderItems.Add(orderItem);
_shoppingCartItemRepository.Remove(shoppingCartItem);
}
order.User = user;
order.Status = OrderStatus.Paid;
_orderRepository.Add(order);
_repositoryContext.Commit();
return order;
}
}

  3. 订单服务的实现。具体订单服务实现代码如下所示:

public class OrderServiceImp : ApplicationService, IOrderService
{
#region Private Fileds
private readonly IShoppingCartRepository _shoppingCartRepository;
private readonly IShoppingCartItemRepository _shoppingCartItemRepository;
private readonly IUserRepository _userRepository;
private readonly IOrderRepository _orderRepository;
private readonly IProductRepository _productRepository;
private readonly IDomainService _domainService;
private readonly IEventBus _eventBus;
#endregion #region Ctor
public OrderServiceImp(IRepositoryContext context,
IUserRepository userRepository,
IShoppingCartRepository shoppingCartRepository,
IProductRepository productRepository,
IShoppingCartItemRepository shoppingCartItemRepository,
IDomainService domainService,
IOrderRepository orderRepository,
IEventBus eventBus) : base(context)
{
_userRepository = userRepository;
_shoppingCartRepository = shoppingCartRepository;
_productRepository = productRepository;
_shoppingCartItemRepository = shoppingCartItemRepository;
_domainService = domainService;
_orderRepository = orderRepository;
_eventBus = eventBus;
} #endregion public OrderDto Checkout(Guid customerId)
{
var user = _userRepository.GetByKey(customerId);
var shoppingCart = _shoppingCartRepository.GetByExpression(s => s.User.Id == user.Id);
var order = _domainService.CreateOrder(user, shoppingCart); return Mapper.Map<Order, OrderDto>(order);
}
     
public OrderDto GetOrder(Guid orderId)
{
var order = _orderRepository.GetBySpecification(new ExpressionSpecification<Order>(o=>o.Id.Equals(orderId)), elp=>elp.OrderItems);
return Mapper.Map<Order, OrderDto>(order);
} // 获得指定用户的所有订单
public IList<OrderDto> GetOrdersForUser(Guid userId)
{
var user = _userRepository.GetByKey(userId);
var orders = _orderRepository.GetAll(new ExpressionSpecification<Order>(o => o.User.Id == userId), sp => sp.CreatedDate, SortOrder.Descending, elp=>elp.OrderItems);
var orderDtos = new List<OrderDto>();
orders
.ToList()
.ForEach(o=>orderDtos.Add(Mapper.Map<Order, OrderDto>(o)));
return orderDtos;
}

  4. HomeController控制器中Checkout操作的实现。具体实现代码如下所示:

public class HomeController : ControllerBase
{
/// <summary>
/// 结算操作
/// </summary>
/// <returns></returns>
[Authorize]
public ActionResult Checkout()
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.Checkout(this.UserId);
return View(model);
}
} [Authorize]
public ActionResult Orders()
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.GetOrdersForUser(this.UserId);
return View(model);
}
} [Authorize]
public ActionResult Order(string id)
{
using (var proxy = new OrderServiceClient())
{
var model = proxy.GetOrder(new Guid(id));
return View(model);
}
}
}

  这样我们就在网上书店中实现了订单功能了。具体的视图界面也就是上一专题中实现的购物车页面。下面具体看看订单的具体实现效果:

  结算页面:

  点击确认购买按钮,在弹出框中点击确认来完成订单的创建:

  通过我的订单来查看所有订单页面:

四、总结

  到此,网上书店案例的订单功能的实现就完成了,在接下来的专题将继续对该案例进行完善,在下一专题中将为该案例引入后台管理操作。商家或管理员可以进入后台管理来对用户订单进行确认发货,以及添加商品,分类等操作。具体实现请见下一专题。

  本专题中所有实现源码下载:https://github.com/lizhi5753186/OnlineStore_Second/

[.NET领域驱动设计实战系列]专题六:DDD实践案例:网上书店订单功能的实现的更多相关文章

  1. [.NET领域驱动设计实战系列]专题十一:.NET 领域驱动设计实战系列总结

    一.引用 其实在去年本人已经看过很多关于领域驱动设计的书籍了,包括Microsoft .NET企业级应用框架设计.领域驱动设计C# 2008实现.领域驱动设计:软件核心复杂性应对之道.实现领域驱动设计 ...

  2. [.NET领域驱动设计实战系列]专题二:结合领域驱动设计的面向服务架构来搭建网上书店

    一.前言 在前面专题一中,我已经介绍了我写这系列文章的初衷了.由于dax.net中的DDD框架和Byteart Retail案例并没有对其形成过程做一步步分析,而是把整个DDD的实现案例展现给我们,这 ...

  3. [.NET领域驱动设计实战系列]专题一:前期准备之EF CodeFirst

    一.前言 从去年已经接触领域驱动设计(Domain-Driven Design)了,当时就想自己搭建一个DDD框架,所以当时看了很多DDD方面的书,例如领域驱动模式与实战,领域驱动设计:软件核心复杂性 ...

  4. [.NET领域驱动设计实战系列]专题十:DDD扩展内容:全面剖析CQRS模式实现

    一.引言 前面介绍的所有专题都是基于经典的领域驱动实现的,然而,领域驱动除了经典的实现外,还可以基于CQRS模式来进行实现.本专题将全面剖析如何基于CQRS模式(Command Query Respo ...

  5. [.NET领域驱动设计实战系列]专题四:前期准备之工作单元模式(Unit Of Work)

    一.前言 在前一专题中介绍了规约模式的实现,然后在仓储实现中,经常会涉及工作单元模式的实现.然而,在我的网上书店案例中也将引入工作单元模式,所以本专题将详细介绍下该模式,为后面案例的实现做一个铺垫. ...

  6. [.NET领域驱动设计实战系列]专题八:DDD案例:网上书店分布式消息队列和分布式缓存的实现

    一.引言 在上一专题中,商家发货和用户确认收货功能引入了消息队列来实现的,引入消息队列的好处可以保证消息的顺序处理,并且具有良好的可扩展性.但是上一专题消息队列是基于内存中队列对象来实现,这样实现有一 ...

  7. [.NET领域驱动设计实战系列]专题七:DDD实践案例:引入事件驱动与中间件机制来实现后台管理功能

    一.引言 在当前的电子商务平台中,用户下完订单之后,然后店家会在后台看到客户下的订单,然后店家可以对客户的订单进行发货操作.此时客户会在自己的订单状态看到店家已经发货.从上面的业务逻辑可以看出,当用户 ...

  8. [.NET领域驱动设计实战系列]专题五:网上书店规约模式、工作单元模式的引入以及购物车的实现

    一.前言 在前面2篇博文中,我分别介绍了规约模式和工作单元模式,有了前面2篇博文的铺垫之后,下面就具体看看如何把这两种模式引入到之前的网上书店案例里. 二.规约模式的引入 在第三专题我们已经详细介绍了 ...

  9. [.NET领域驱动设计实战系列]专题三:前期准备之规约模式(Specification Pattern)

    一.前言 在专题二中已经应用DDD和SOA的思想简单构建了一个网上书店的网站,接下来的专题中将会对该网站补充更多的DDD的内容.本专题作为一个准备专题,因为在后面一个专题中将会网上书店中的仓储实现引入 ...

随机推荐

  1. JS验证字符长度

    function getStrLength(str) { var cArr = str.match(/[^\x00-\xff]/ig); return str.length + (cArr == nu ...

  2. java基于socket公共聊天室的实现

    项目:一个公共聊天室功能的实现,实现了登录聊天,保存聊天记录等功能. 一.实现代码 1.客户端 ChatClient.java import java.io.BufferedReader; impor ...

  3. 数据库执行sql报错Got a packet bigger than 'max_allowed_packet' bytes及重启mysql

    准备在mysql上使用数据库A,但mysql5经过重装后,上面的数据库已丢失,只得通过之前备份的A.sql重新生成数据库A. 1.执行sql报错 在执行A.sql的过程中,出现如下错误:Got a p ...

  4. I/O知识

    1.jdk1.4之前(jdk1.4开始提供了nio)的早起版本,java对I/O的支持并不完善,开发人员开发高性能I/O程序时,面临的问题主要有:     没有缓冲区,I/O性能存在问题     没有 ...

  5. 关于MYSQL四种引擎

    你能用的数据库引擎取决于mysql在安装的时候是如何被编译的.要添加一个新的引擎,就必须重新编译MYSQL.在缺省情况下,MYSQL支持三个引擎:ISAM.MYISAM和HEAP.另外两种类型INNO ...

  6. SQL Server 大数据量分页建议方案

    简单的说就是这个 select top(20) * from( select *, rowid = row_number() over(order by xxx) from tb with(noloc ...

  7. ASP.NET操作ORACLE数据库之模糊查询

    ASP.NET操作ORACLE数据库之模糊查询 一.ASP.NET MVC利用OracleHelper辅助类操作ORACLE数据库 //连接Oracle数据库的连接字符串 string connect ...

  8. python ImportError: DLL load failed: %1 不是有效的 Win32 应用程序

    导入的时候报出了 ImportError 在windows上安装python 的模块后,导入模块时报 python ImportError: DLL load failed: %1 不是有效的 Win ...

  9. 【转】linux yum命令详解

    yum(全称为 Yellow dog Updater, Modified)是一个在Fedora和RedHat以及SUSE中的Shell前端软件包管理器.基於RPM包管理,能够从指定的服务器自动下载RP ...

  10. 1813. M进制数问题

    1813. M进制数问题 Constraints Time Limit: 1 secs, Memory Limit: 32 MB Description 试用 C++的类来表示一般进制数. 给定 2 ...