20191128 oomall提交订单部分代码解读

20191128读邱明老师代码后的解读

前言

本文章的代码基础:点我下载

本文章按照自底向上的顺序描述提交订单部分的代码逻辑

util

工具类

Common

  • 1
    public static String getRandomNum(int length)

    传入想要的数字长度,生成唯一随机数

Config

  • 1
    public Integer getMaxPayTime()

    获得最长付款时间

    实际上就是对于订单付款期限的全局设定

JacksonUtil

  • 传入json字符串和file,在json字符串中,用field作为key,返回value
  • 传入json字符串,返回json对象
  • 传入json字符串,返回map
  • 传入对象,返回json字符串

ResponseUtil

描述了对于一个操作的响应

每个响应都是一个Map<String, Object>,包括3个字段:

  1. errno : 错误码
  2. errmsg : 错误消息
  3. data : 响应数据

该工具类存在的主要原因,是为了统一返回格式

domain

负责描述所有类

所有实体类都包含通用的1-5,以下在每个实体类内均只描述其特性

  1. 包含表示一个类的若干字段
  2. 有非功能方法toString()
  3. 有重载方法equals,用于判定两个对象是否是同一对象
  4. 有重载方法hashCode,返回对该对象的id的哈希结果
  5. 各字段均包含public权限的get和set方法

cart

CartItem

此处与商品的关系表现在,一个CartItem内保存的是Product的id

coupon

AbstractCouponStrategy

一个抽象类,描述了计算一个订单优惠后的价格计算方法

只具体写了cacuDiscount方法,用于计算适用折扣后的OrderItem列表:

  1. 传入OrderItems和couponSn(优惠券序号)

  2. 判断优惠门槛

  3. 若满足门槛,计算优惠后的价格

    1. 将优惠减免金额平均分配到各商品

    2. 若理论总价与实际总价不符

      • 寻找数量为1的Item,将差价补偿在该Item上

      • 无数量为1的Item

        1. 将第一个Item拆开,变成两个Item,其中一个数量为n-1,另一个为1
        2. 将差价补偿在数量为1的Item上
  4. 返回该列表

要求子类实现:

  1. boolean isEnough方法,判断是否已经满足优惠门槛
  2. BigDecimal getDealPrice方法,计算折扣后价格
  3. BigDecimal getError方法,获得理论总价与实际总价的误差

CashOffStrategy

策略为满X减Y

继承了AbstractCouponStrategy类,实现了其中要求的方法,成为实体类,负责计算优惠后的OrderItem列表

PercentageStrategy

策略为满X减Y%

继承了AbstractCouponStrategy类,实现了其中要求的方法,成为实体类,负责计算优惠后的OrderItem列表

Coupon

包含类内enum类型子类Status。该子类包含2个参数

  1. String name,表示当前优惠券所处状态
  2. Integer value,表示该状态的状态码

同时,有方法:

  1. 1
    public void cacuCouponPrice(Order order)
    1
    2
    3
    4
      借助[CouponRule](#domain-coupon-CouponRule)来计算CouponPrice

    2. ```java
    public boolean isReadyToUse()

    判断该优惠券当前是否可用

CouponRulePo

描述了作为一个优惠券所需的全部静态信息

但由于优惠券适用的方法较多,所以方法拆开到另一个类中实现

CouponRule

在类中声明一个realObj来与CouponRulePo建立联系

实现了能用于优惠券的各种方法:

  1. 1
    private List<OrderItem> getValidItems(List<OrderItem> items)

    传入订单的子订单列表,返回可以适用所选当前CouponRule的子订单列表

  2. 1
    public AbstractCouponStrategy getStrategy()

    获得当前CouponRule所属的折扣策略

  3. 1
    public void setStrategy(AbstractCouponStrategy strategy)

    传入新的折扣策略方案,将当前CouponRule所属折扣测了更新

  4. 1
    public boolean isUsedOnGoods(Integer goodsId)

    传入商品id,判断该商品是否能够使用当前CouponRule

  5. 1
    public List<Integer> getGoodsIds()

    获得当前CouponRule所适用的商品id列表

  6. 1
    public void setGoodsIds(List<Integer> goodsIds)

    传入商品id列表,更新当前CouponRule所适用的商品id列表

  7. 1
    public void cacuCouponPrice(Order order, String couponSn)

    传入订单和使用的优惠券序号,借助CashOffStrategy,更新订单Items为适用了优惠券策略后的订单Items

goods

AbstractPaymentStrategy

仅定义了 活动付款策略 的接口

Goods

含有字段products,以此与Product建立联系

Product

无特性

PromotionPo

描述一个商品的优惠活动策略

无方法

Promotion

实现了PromotionPo需要的方法

对于PromotionPo和Promotion,看代码完成度,认为应该是未实现

参考Coupon内的策略写法,应该还有

  1. 填充完整的抽象付款策略类
  2. 未给出的策略类,继承了抽象付款策略类

order

Order

包含类内enum类型子类Status。该子类包含2个参数

  1. String name,表示当前订单所处状态
  2. Integer value,表示该状态的状态码

含有4种方法:

  1. 构造函数

    有2种方案

    1. 不传参数
    2. 传入user和address
  2. 计算价格

    1. 先执行

      1
      public void cacuDealPrice()

      借助Coupon算出使用的优惠券情况

    2. 再执行

      1
      private void cacuTotal()

      算出订单总价

  3. 反向绑定。

    在OrderItem中绑定Order的id

  4. 借助Payment计算付款方式

OrderItem

继承自Cloneable类,这意味着该类实例可以被克隆

冗余存储productName

含有2种方法:

  1. 构造函数
    1. 不传参数
    2. 传入cartItem
  2. 重载的clone方法,指示了如何克隆该类的实例

user

Address

无特性

User

无特性

GrouponOnRule

无特性

GrouponOnStrategy

无特性(该模块代码未完成)

Payment

包含类内enum类型子类Status。该子类包含2个参数

  1. String name,表示当前payment所处状态
  2. Integer value,表示该状态的状态码

含有一个不传参数的构造函数

mapper

负责直接和数据库交互

以下各个Mapper均由java文件和xml文件构成,其中:

  1. java文件定义为接口,提供若干方法
  2. xml文件用xml语言描述了java文件中的方法的sql实现

每个类都要加上 @Mapper 注解

CartItemMapper

提供了2个方法

  1. 传入购物车对象id,以id获得购物车明细
  2. 传入购物车对象列表,删除购物车中指定的项目

CouponMapper

提供了2个方法

  1. 传入优惠券对象id,以id获得一张优惠券的明细
  2. 传入优惠券对象id,以id获得该优惠券所属规则的明细

GoodsMapper

提供了2个方法

  1. 传入product对象id,以id获得product明细
  2. 传入商品对象id,以id获得商品明细

OrderMapper

提供了3个方法

  1. 传入订单id,以id获得订单明细
  2. 传入订单对象,新增一个订单
  3. 传入子订单列表,新增订单中的所有明细

dao

负责数据的交流

对service层表现为数据来源,对mapper层表现为数据调用者

每个类都要加上 @Repository 注解

以下均只描述提供的方法

CartItemDao

借助CartItemMapper读取数据

  • 用id返回购物车明细

CouponDao

借助CouponMapper读取数据

  • 用id返回优惠券明细

GoodsDao

借助GoodsMapper读取数据

  • 用id返回product明细

OrderDao

借助OrderMapper读取数据

  • 用id返回order明细

service

负责业务的具体实现,需要数据的时候均访问dao层

每个Service都是先提供一个接口,然后写具体的实现(impl)

每个类都要加上 @Service 注解

以下省略对接口的描述,直接描述实现

CartItemServiceImpl

  • 1
    public CartItem findCartItemById(Integer id)

    根据cartItem id,借助CartItemDao,返回该cartItem的明细

  • 1
    public void clearCartItem(List<CartItem> cartItems)

    根据传入的cartItem列表,借助CartItemMapper,清除原购物车中的cartItems

CouponServiceImpl

  • 1
    public Coupon findCouponById(Integer id)

    根据coupon id,借助CouponDao,返回该coupon明细

GoodsServiceImpl

  • 1
    public Product findProductById(Integer id)

    根据product id,借助GoodsDao,返回该product明细

OrderServiceImpl

  • 1
    public Order submit(Order order, List<CartItem> cartItems)
    1. 传入空订单和cartItems
    2. 组装订单
    3. 借助CartItemServiceImpl,清除原购物车中在该订单内选中的商品
    4. 使用Order的cacuDealPrice方法计算优惠价
    5. 使用Order的cacuPayment方法计算订单总价
    6. 返回组装完成的订单

controller

负责描述用例

OrderSubmitVO

  1. 该VO类内有4个字段

    1. cartItems,是Integer类型的List,保存订单内所选中的商品的id
    2. address,是Address类型,保存订单内选中的地址id
    3. couponId,是Integer类型,保存订单内选中的优惠券id
      • 这也意味着一个订单只能使用一张优惠券
    4. message,是String类型,保存别的消息信息,起到一个备用的作用

    同时,有一个非功能方法:toString()

  2. 4个字段各自有public权限的get和set方法,用于给Controller使用

  3. 该VO只用于承接前端提交的订单信息,无其它功能

OrderController

  1. 声明Controller为RestController,设置RMap

  2. 定义需要使用的服务类的实体,自动装配(Controller内公用)

    1. OrderService
    2. CartService
    3. CouponService
  3. 进入具体行为(Post)及实现方法submit

    1. submit方法在此处定义为Object类型,表示要返回一个对象。可以看到在该方法结尾有一个return ResponseUtil.ok(retOrder)语句,说明返回到前端的对象就是ResponseUtil.ok(retOrder)的返回值

      ResponseUtil类

    2. 回到submit方法,参数列表有1个参数,带有@RequestBody注解,表明参数来自前端传值;同时参数声明为OrderSubmitVo类型,表明前端传值到达后端后先拼装为一个OrderSubmitVo的实例

    3. 进入方法,可以看到在方法刚开始和即将结束之处都有logger.debug语句,生成调试日志。此处与主要功能无关

    4. 获得生成一个订单的各种主要信息,包括:

      1. user
      2. address
      3. coupon
      4. cartitems

      其中coupon需要判空,cartItems则因为前端只传回了cartItems的id,所以需要CartItemServiceImpl来获得每个cartitem的具体信息

    5. 新建一个空订单,保存除了商品之外的信息(第58行)

      1. 新建一个用商品装填好的订单。此处使用OrderServiceImpl的submit方法装填,传入参数为空订单和cartitem列表
      2. 构造返回对象,返回到前端,订单提交结束
  4. Controller完毕

--It's the end.Thanks for your read.--