17、构建销售模块:从服务重写到结账流程的实现

构建销售模块:从服务重写到结账流程的实现

1. 服务重写

1.1 重写客户订单服务

src/Foggyline/SalesBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php 文件中,将 // Override 'foggyline_customer.customer_orders' 注释替换为以下代码:

$container->removeDefinition 
  ('foggyline_customer.customer_orders');
$container->setDefinition('foggyline_customer.customer_orders',  
  $container->getDefinition('foggyline_sales.customer_orders'));

这样可以使服务重写生效,并引入我们刚刚所做的所有更改。

1.2 重写畅销书服务

1.2.1 定义服务

src/Foggyline/SalesBundle/Resources/config/services.xml 文件的 service 元素下添加以下定义:

<service id="foggyline_sales.bestsellers"  
  class="Foggyline\SalesBundle\Service\BestSellers">
  <argument type="service" id="doctrine.orm.entity_manager"/>
  <argument type="service" id="router"/>
</service>
1.2.2 创建服务类

创建 src/Foggyline/SalesBundle/Service/BestSellers.php 文件,内容如下:

namespace Foggyline\SalesBundle\Service;

class BestSellers
{
  private $em;
  private $router;

  public function __construct(
    \Doctrine\ORM\EntityManager $entityManager,
    \Symfony\Bundle\FrameworkBundle\Routing\Router $router
  )
  {
    $this->em = $entityManager;
    $this->router = $router;
  }

  public function getItems()
  {
    $products = array();
    $salesOrderItem = $this->em->getRepository 
      ('FoggylineSalesBundle:SalesOrderItem');
    $_products = $salesOrderItem->getBestsellers();
    foreach ($_products as $_product) {
      $products[] = array(
        'path' => $this->router->generate('product_show',  
          array('id' => $_product->getId())),
        'name' => $_product->getTitle(),
        'img' => $_product->getImage(),
        'price' => $_product->getPrice(),
        'id' => $_product->getId(),
      );
    }
    return $products;
  }
}
1.2.3 定义 getBestsellers 方法

src/Foggyline/SalesBundle/Repository/SalesOrderItemRepository.php 文件中添加 getBestsellers 方法:

public function getBestsellers()
{
  $products = array();
  $query = $this->_em->createQuery('SELECT IDENTITY(t.product),  
    SUM(t.qty) AS HIDDEN q
  FROM Foggyline\SalesBundle\Entity 
    \SalesOrderItem t
  GROUP BY t.product ORDER BY q DESC')
  ->setMaxResults(5);
  $_products = $query->getResult();
  foreach ($_products as $_product) {
    $products[] = $this->_em->getRepository 
      ('FoggylineCatalogBundle:Product')
    ->find(current($_product));
  }
  return $products;
}
1.2.4 完成服务重写

src/Foggyline/SalesBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php 文件中,将 // Override 'bestsellers' service 注释替换为以下代码:

$container->removeDefinition('bestsellers');
$container->setDefinition('bestsellers', $container-> 
  getDefinition('foggyline_sales.bestsellers'));

通过重写畅销书服务,我们为其他模块提供了基于实际销售的畅销书列表。

2. 创建购物车页面

2.1 编辑 indexAction 方法

CartController 中编辑 indexAction 方法:

public function indexAction()
{
  if ($customer = $this->getUser()) {
    $em = $this->getDoctrine()->getManager();
    $cart = $em->getRepository('FoggylineSalesBundle:Cart')-> 
      findOneBy(array('customer' => $customer));
    $items = $cart->getItems();
    $total = null;
    foreach ($items as $item) {
      $total += floatval($item->getQty() * $item-> 
        getUnitPrice());
    }
    return $this->render('FoggylineSalesBundle:default: 
      cart/index.html.twig', array(
        'customer' => $customer,
        'items' => $items,
        'total' => $total,
      ));
  } else {
    $this->addFlash('warning', 'Only logged in customers can  
      access cart page.');
    return $this->redirectToRoute('foggyline_customer_login');
  }
}

此方法检查用户是否登录,若登录则显示购物车中的所有商品,未登录则重定向到客户登录页面。

2.2 创建 index.html.twig 模板

创建 src/Foggyline/SalesBundle/Resources/views/Default/cart/index.html.twig 文件,内容如下:

{% extends 'base.html.twig' %}
{% block body %}
<h1>Shopping Cart</h1>
<div class="row">
  <div class="large-8 columns">
    <form action="{{ path('foggyline_sales_cart_update') }}" 
      method="post">
    <table>
      <thead>
        <tr>
          <th>Item</th>
          <th>Price</th>
          <th>Qty</th>
          <th>Subtotal</th>
        </tr>
      </thead>
      <tbody>
        {% for item in items %}
        <tr>
          <td>{{ item.product.title }}</td>
          <td>{{ item.unitPrice }}</td>
          <td><input name="item[{{ item.id }}]" value="{{ item.qty  
            }}"/></td>
          <td>{{ item.qty * item.unitPrice }}</td>
        </tr>
        {% endfor %}
      </tbody>
    </table>
    <button type="submit" class="button">Update Cart</button>
  </form>
</div>
<div class="large-4 columns">
  <div>Order Total: {{ total }}</div>
  <div><a href="{{ path('foggyline_sales_checkout') }}" 
    class="button">Go to Checkout</a></div>
  </div>
</div>
{% endblock %}

模板会显示每个添加商品下的数量输入元素和“更新购物车”按钮,点击该按钮会提交表单到 foggyline_sales_cart_update 路由。

2.3 创建 foggyline_sales_cart_update 路由

src/Foggyline/SalesBundle/Resources/config/routing.xml 文件的 route 元素下添加以下条目:

<route id="foggyline_sales_cart_update" path="/cart/update">
  <default key="_controller">FoggylineSalesBundle:Cart:update 
    </default>
</route>

2.4 实现 updateAction 方法

CartController 中添加 updateAction 方法:

public function updateAction(Request $request)
{
  $items = $request->get('item');
  $em = $this->getDoctrine()->getManager();
  foreach ($items as $_id => $_qty) {
    $cartItem = $em->getRepository 
      ('FoggylineSalesBundle:CartItem')->find($_id);
    if (intval($_qty) > 0) {
      $cartItem->setQty($_qty);
      $em->persist($cartItem);
    } else {
      $em->remove($cartItem);
    }
  }
  // Persist to database
  $em->flush();
  $this->addFlash('success', 'Cart updated.');
  return $this->redirectToRoute('foggyline_sales_cart');
}

若要从购物车中移除商品,只需将数量值设为 0 并点击“更新购物车”按钮。

3. 创建支付服务

3.1 替换注释代码

src/Foggyline/SalesBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php 文件中,将 // Pickup/parse 'payment_method' services 注释替换为以下代码:

$container->getDefinition('foggyline_sales.payment')
  ->addArgument(
  array_keys($container->findTaggedServiceIds 
    ('payment_method'))
);

findTaggedServiceIds 方法返回所有标记为 payment_method 的服务的键值列表,并将其作为参数传递给 foggyline_sales.payment 服务。

3.2 编辑 services.xml 文件

src/Foggyline/SalesBundle/Resources/config/services.xml 文件的 service 元素下添加以下内容:

<service id="foggyline_sales.payment"  
  class="Foggyline\SalesBundle\Service\Payment">
  <argument type="service" id="service_container"/>
</service>

3.3 创建 Payment

创建 src/Foggyline/SalesBundle/Service/Payment.php 文件,内容如下:

namespace Foggyline\SalesBundle\Service;

class Payment
{
  private $container;
  private $methods;

  public function __construct($container, $methods)
  {
    $this->container = $container;
    $this->methods = $methods;
  }

  public function getAvailableMethods()
  {
    $methods = array();
    foreach ($this->methods as $_method) {
      $methods[] = $this->container->get($_method);
    }
    return $methods;
  }
}

该服务接受两个参数 $container $methods getAvailableMethods 方法能够返回所有标记为 payment_method 的服务。

4. 创建运输服务

4.1 替换注释代码

src/Foggyline/SalesBundle/DependencyInjection/Compiler/OverrideServiceCompilerPass.php 文件中,将 // Pickup/parse shipment_method' services 注释替换为以下代码:

$container->getDefinition('foggyline_sales.shipment')
  ->addArgument(
  array_keys($container->findTaggedServiceIds 
    ('shipment_method'))
);

4.2 编辑 services.xml 文件

src/Foggyline/SalesBundle/Resources/config/services.xml 文件的 service 元素下添加以下内容:

<service id="foggyline_sales.shipment" 
  class="Foggyline\SalesBundle\Service\Payment">
  <argument type="service" id="service_container"/>
</service>

4.3 创建 Shipment

创建 src/Foggyline/SalesBundle/Service/Shipment.php 文件,内容如下:

namespace Foggyline\SalesBundle\Service;

class Shipment
{
  private $container;
  private $methods;

  public function __construct($container, $methods)
  {
    $this->container = $container;
    $this->methods = $methods;
  }

  public function getAvailableMethods()
  {
    $methods = array();
    foreach ($this->methods as $_method) {
      $methods[] = $this->container->get($_method);
    }
    return $methods;
  }
}

通过统一的支付和运输服务,我们能够获取所有支付和运输服务,使结账过程更加便捷。

5. 创建结账页面

5.1 编辑 indexAction 方法

CheckoutController 中编辑 indexAction 方法:

public function indexAction()
{
  if ($customer = $this->getUser()) {
    $form = $this->getAddressForm();
    $em = $this->getDoctrine()->getManager();
    $cart = $em->getRepository('FoggylineSalesBundle:Cart') 
      ->findOneBy(array('customer' => $customer));
    $items = $cart->getItems();
    $total = null;
    foreach ($items as $item) {
      $total += floatval($item->getQty() * $item->getUnitPrice());
    }
    return $this->render 
      ('FoggylineSalesBundle:default:checkout/index.html.twig',  
      array(
      'customer' => $customer,
      'items' => $items,
      'cart_subtotal' => $total,
      'shipping_address_form' => $form->createView(),
      'shipping_methods' => $this->get 
        ('foggyline_sales.shipment')->getAvailableMethods()
    ));
  } else {
    $this->addFlash('warning', 'Only logged in customers can  
      access checkout page.');
    return $this->redirectToRoute('foggyline_customer_login');
  }
}

private function getAddressForm()
{
  return $this->createFormBuilder()
  ->add('address_first_name', TextType::class)
  ->add('address_last_name', TextType::class)
  ->add('company', TextType::class)
  ->add('address_telephone', TextType::class)
  ->add('address_country', CountryType::class)
  ->add('address_state', TextType::class)
  ->add('address_city', TextType::class)
  ->add('address_postcode', TextType::class)
  ->add('address_street', TextType::class)
  ->getForm();
}

此方法获取当前登录客户的购物车,并将其传递到 checkout/index.html.twig 模板,同时还会传递运输步骤所需的其他变量。

5.2 创建 index.html.twig 模板

创建 src/Foggyline/SalesBundle/Resources/views/Default/checkout/index.html.twig 文件,内容如下:

{% extends 'base.html.twig' %}
{% block body %}
<h1>Checkout</h1>
<div class="row">
  <div class="large-8 columns">
    <form action="{{ path('foggyline_sales_checkout_payment') }}"  
      method="post" id="shipping_form">
      <fieldset>
        <legend>Shipping Address</legend>
        {{ form_widget(shipping_address_form) }}
      </fieldset>
      <fieldset>
        <legend>Shipping Methods</legend>
        <ul>
          {% for method in shipping_methods %}
          {% set shipment = method.getInfo('street', 'city',  
            'country', 'postcode', 'amount', 'qty')['shipment'] %}
          <li>
            <label>{{ shipment.title }}</label>
            <ul>
              {% for delivery_option in shipment.delivery_options %}
              <li>
                <input type="radio" name="shipment_method"
                  value="{{ shipment.code }}____ 
                  {{ delivery_option.code }}____ 
                  {{ delivery_option.price }}">  
                  {{ delivery_option.title }}
                  ({{ delivery_option.price }})
                <br>
              </li>
              {% endfor %}
            </ul>
          </li>
          {% endfor %}
        </ul>
      </fieldset>
    </form>
  </div>
  <div class="large-4 columns">
    {% include  
      'FoggylineSalesBundle:default:checkout/order_sumarry.html.twig' 
    %}
    <div>Cart Subtotal: {{ cart_subtotal }}</div>
    <div><a id="shipping_form_submit" href="#"  
      class="button">Next</a>
    </div>
  </div>
</div>
<script type="text/javascript">
  var form = document.getElementById('shipping_form');
  document.getElementById('shipping_form_submit') 
    .addEventListener('click', function () {
    form.submit();
  });
</script>
{% endblock %}

模板会列出所有与地址相关的表单字段和可用的运输方法,点击“下一步”按钮会将表单提交到 foggyline_sales_checkout_payment 路由。

5.3 定义 foggyline_sales_checkout_payment 路由

src/Foggyline/SalesBundle/Resources/config/routing.xml 文件的 routes 元素下添加以下条目:

<route id="foggyline_sales_checkout_payment"  
  path="/checkout/payment">
  <default  
    key="_controller">FoggylineSalesBundle:Checkout:payment</default>
</route>

5.4 实现 paymentAction 方法

CheckoutController 中添加 paymentAction 方法:

public function paymentAction(Request $request)
{
  $addressForm = $this->getAddressForm();
  $addressForm->handleRequest($request);
  if ($addressForm->isSubmitted() && $addressForm->isValid() &&  
    $customer = $this->getUser()) {
    $em = $this->getDoctrine()->getManager();
    $cart = $em->getRepository('FoggylineSalesBundle:Cart')-> 
      findOneBy(array('customer' => $customer));
    $items = $cart->getItems();
    $cartSubtotal = null;
    foreach ($items as $item) {
      $cartSubtotal += floatval($item->getQty() * $item-> 
        getUnitPrice());
    }
    $shipmentMethod = $_POST['shipment_method'];
    $shipmentMethod = explode('____', $shipmentMethod);
    $shipmentMethodCode = $shipmentMethod[0];
    $shipmentMethodDeliveryCode = $shipmentMethod[1];
    $shipmentMethodDeliveryPrice = $shipmentMethod[2];
    // Store relevant info into session
    $checkoutInfo = $addressForm->getData();
    $checkoutInfo['shipment_method'] = $shipmentMethodCode .  
      '____' . $shipmentMethodDeliveryCode;
    $checkoutInfo['shipment_price'] =  
      $shipmentMethodDeliveryPrice;
    $checkoutInfo['items_price'] = $cartSubtotal;
    $checkoutInfo['total_price'] = $cartSubtotal +  
      $shipmentMethodDeliveryPrice;
    $this->get('session')->set('checkoutInfo', $checkoutInfo);
    return $this->render('FoggylineSalesBundle:default: 
      checkout/payment.html.twig', array(
      'customer' => $customer,
      'items' => $items,
      'cart_subtotal' => $cartSubtotal,
      'delivery_subtotal' => $shipmentMethodDeliveryPrice,
      'delivery_label' =>'Delivery Label Here',
      'order_total' => $cartSubtotal +  
        $shipmentMethodDeliveryPrice,
      'payment_methods' => $this->get 
        ('foggyline_sales.payment')->getAvailableMethods()
    ));
  } else {
    $this->addFlash('warning', 'Only logged in customers can  
      access checkout page.');
    return $this->redirectToRoute('foggyline_customer_login');
  }
}

该方法获取结账过程运输步骤的提交内容,将相关值存储到会话中,获取支付步骤所需的变量,并渲染 checkout/payment.html.twig 模板。

5.5 创建 payment.html.twig 模板

创建 src/Foggyline/SalesBundle/Resources/views/Default/checkout/payment.html.twig 文件,内容如下:

{% extends 'base.html.twig' %}
{% block body %}
<h1>Checkout</h1>
<div class="row">
  <div class="large-8 columns">
    <form action="{{ path('foggyline_sales_checkout_process') }}" 
      method="post" id="payment_form">
      <fieldset>
        <legend>Payment Methods</legend>
        <ul>
          {% for method in payment_methods %}
          {% set payment = method.getInfo()['payment'] %}
          <li>
            <input type="radio" name="payment_method"
              value="{{ payment.code }}"> {{ payment.title }}
            {% if payment['form'] is defined %}
            <div id="{{ payment.code }}_form">
              {{ form_widget(payment['form']) }}
            </div>
            {% endif %}
          </li>
          {% endfor %}
        </ul>
      </fieldset>
    </form>
  </div>
  <div class="large-4 columns">
    {% include 'FoggylineSalesBundle:default:checkout/ 
      order_sumarry.html.twig' %}
    <div>Cart Subtotal: {{ cart_subtotal }}</div>
    <div>{{ delivery_label }}: {{ delivery_subtotal }}</div>
    <div>Order Total: {{ order_total }}</div>
    <div><a id="payment_form_submit" href="#" class="button">Place  
      Order</a>
    </div>
  </div>
</div>
<script type="text/javascript">
  var form = document.getElementById('payment_form');
  document.getElementById('payment_form_submit'). 
    addEventListener('click', function () {
    form.submit();
  });
</script>
{% endblock %}

该模板会渲染可用的支付方法和“下单”按钮,点击该按钮会将 POST 请求提交到 foggyline_sales_checkout_process 路由。

5.6 定义 foggyline_sales_checkout_process 路由

src/Foggyline/SalesBundle/Resources/config/routing.xml 文件的 routes 元素下添加以下条目:

<route id="foggyline_sales_checkout_process" 
  path="/checkout/process">
  <default  
    key="_controller">FoggylineSalesBundle:Checkout:process</default>
</route>

5.7 实现 processAction 方法

CheckoutController 中添加 processAction 方法:

public function processAction()
{
  if ($customer = $this->getUser()) {
    $em = $this->getDoctrine()->getManager();
    // Merge all the checkout info, for SalesOrder
    $checkoutInfo = $this->get('session')->get 
      ('checkoutInfo');
    $now = new \DateTime();
    // Create Sales Order
    $salesOrder = new \Foggyline\SalesBundle\Entity 
      \SalesOrder();
    $salesOrder->setCustomer($customer);
    $salesOrder->setItemsPrice($checkoutInfo['items_price']);
    $salesOrder->setShipmentPrice
      ($checkoutInfo['shipment_price']);
    $salesOrder->setTotalPrice($checkoutInfo['total_price']);
    $salesOrder->setPaymentMethod($_POST['payment_method']);
    $salesOrder->setShipmentMethod 
      ($checkoutInfo['shipment_method']);
    $salesOrder->setCreatedAt($now);
    $salesOrder->setModifiedAt($now);
    $salesOrder->setCustomerEmail($customer->getEmail());
    $salesOrder->setCustomerFirstName 
      ($customer->getFirstName());
    $salesOrder->setCustomerLastName 
      ($customer->getLastName());
    $salesOrder->setAddressFirstName 
      ($checkoutInfo['address_first_name']);
    $salesOrder->setAddressLastName 
      ($checkoutInfo['address_last_name']);
    $salesOrder->setAddressCountry 
      ($checkoutInfo['address_country']);
    $salesOrder->setAddressState 
      ($checkoutInfo['address_state']);
    $salesOrder->setAddressCity 
      ($checkoutInfo['address_city']);
    $salesOrder->setAddressPostcode 
      ($checkoutInfo['address_postcode']);
    $salesOrder->setAddressStreet 
      ($checkoutInfo['address_street']);
    $salesOrder->setAddressTelephone 
      ($checkoutInfo['address_telephone']);
    $salesOrder->setStatus(\Foggyline\SalesBundle\Entity\ 
      SalesOrder::STATUS_PROCESSING);
    $em->persist($salesOrder);
    $em->flush();
    // Foreach cart item, create order item, and delete cart  
      item
    $cart = $em->getRepository('FoggylineSalesBundle:Cart')-> 
      findOneBy(array('customer' => $customer));
    $items = $cart->getItems();
    foreach ($items as $item) {
      $orderItem = new \Foggyline\SalesBundle\Entity 
        \SalesOrderItem();
      $orderItem->setSalesOrder($salesOrder);
      $orderItem->setTitle($item->getProduct()->getTitle());
      $orderItem->setQty($item->getQty());
      $orderItem->setUnitPrice($item->getUnitPrice());
      $orderItem->setTotalPrice($item->getQty() * $item-
>getUnitPrice());
      $orderItem->setModifiedAt($now);
      $orderItem->setCreatedAt($now);
      $orderItem->setProduct($item->getProduct());
      $em->persist($orderItem);
      $em->remove($item);
    }
    $em->remove($cart);
    $em->flush();
    $this->get('session')->set('last_order', $salesOrder-> 
getId());
    return $this->redirectToRoute 
      ('foggyline_sales_checkout_success');
  } else {
    $this->addFlash('warning', 'Only logged in customers can  
      access checkout page.');
    return $this->redirectToRoute('foggyline_customer_login');
  }
}

当 POST 请求到达控制器时,会创建一个新订单及所有相关商品,同时清空购物车和购物车商品,最后将客户重定向到订单成功页面。

6. 创建订单成功页面

6.1 定义路由

src/Foggyline/SalesBundle/Resources/config/routing.xml 文件的 routes 元素下添加以下条目:

<route id="foggyline_sales_checkout_success"  
  path="/checkout/success">
  <default  
    key="_controller">FoggylineSalesBundle:Checkout:success</default>
</route>

6.2 实现 successAction 方法

CheckoutController 中添加 successAction 方法:

public function successAction()
{
  return $this->render('FoggylineSalesBundle:default: 
    checkout/success.html.twig', array(
    'last_order' => $this->get('session')->get('last_order')
  ));
}

6.3 创建 success.html.twig 模板

创建 src/Foggyline/SalesBundle/Resources/views/Default/checkout/success.html.twig 文件,内容如下:

{% extends 'base.html.twig' %}
{% block body %}
<h1>Checkout Success</h1>
<div class="row">
  <p>Thank you for placing your order #{{ last_order }}.</p>
  <p>You can see order details <a href="{{  
    path('customer_account') }}">here</a>.</p>
</div>
{% endblock %}

至此,我们完成了网店的整个结账流程,为更强大的实现奠定了基础。

7. 创建商店经理仪表盘

7.1 创建控制器

创建 src/AppBundle/Controller/StoreManagerController.php 文件,内容如下:

namespace AppBundle\Controller;

use Sensio\Bundle\FrameworkExtraBundle\Configuration\Route;
use Symfony\Bundle\FrameworkBundle\Controller\Controller;

class StoreManagerController extends Controller
{
  /**
  * @Route("/store_manager", name="store_manager")
  */
  public function indexAction()
  {
    return $this->render 
      ('AppBundle:default:store_manager.html.twig');
  }
}

7.2 创建模板

创建 src/AppBundle/Resources/views/default/store_manager.html.twig 文件,内容如下:

{% extends 'base.html.twig' %}
{% block body %}
<h1>Store Manager</h1>
<div class="row">
  <div class="large-6 columns">
    <div class="stacked button-group">
      <a href="{{ path('category_new') }}" class="button">Add new  
        Category</a>
      <a href="{{ path('product_new') }}" class="button">Add new  
        Product</a>
      <a href="{{ path('customer_new') }}" class="button">Add new  
        Customer</a>
    </div>
  </div>
  <div class="large-6 columns">
    <div class="stacked button-group">
      <a href="{{ path('category_index') }}" class="button">List &  
        Manage Categories</a>
      <a href="{{ path('product_index') }}" class="button">List &  
        Manage Products</a>
      <a href="{{ path('customer_index') }}" class="button">List &  
        Manage Customers</a>
    </div>
  </div>
</div>
{% endblock %}

这个仪表盘为商店经理提供了添加和管理类别、产品和客户的功能。

总结

通过以上步骤,我们完成了销售模块的构建,包括服务重写、购物车页面、支付和运输服务、结账页面、订单成功页面以及商店经理仪表盘的创建。这个简单的实现为后续更复杂的功能扩展奠定了基础。

流程图

graph LR
    A[开始] --> B[服务重写]
    B --> C[创建购物车页面]
    C --> D[创建支付服务]
    D --> E[创建运输服务]
    E --> F[创建结账页面]
    F --> G[创建订单成功页面]
    G --> H[创建商店经理仪表盘]
    H --> I[结束]

表格

功能模块 相关文件 主要方法
服务重写 OverrideServiceCompilerPass.php removeDefinition setDefinition
购物车页面 CartController.php index.html.twig indexAction updateAction
支付服务 Payment.php services.xml getAvailableMethods
运输服务 Shipment.php services.xml getAvailableMethods
结账页面 CheckoutController.php index.html.twig payment.html.twig indexAction paymentAction processAction
订单成功页面 CheckoutController.php success.html.twig successAction
商店经理仪表盘 StoreManagerController.php store_manager.html.twig indexAction

构建销售模块:从服务重写到结账流程的实现

8. 功能模块总结与分析

8.1 服务重写

服务重写是整个销售模块的基础,它允许我们根据实际需求对现有的服务进行定制。通过重写客户订单服务和畅销书服务,我们能够确保系统使用最新的业务逻辑。例如,在重写畅销书服务时,我们从数据库中获取实际的销售数据,而不是使用虚拟数据,这使得显示的畅销书列表更加真实和有价值。

8.2 购物车页面

购物车页面是用户与系统交互的重要环节。它提供了一个直观的界面,让用户可以查看和管理自己的购物车内容。通过 indexAction 方法,我们可以根据用户的登录状态显示不同的页面。如果用户已登录,将显示购物车中的商品和总价;如果用户未登录,则会重定向到登录页面。 updateAction 方法则允许用户更新购物车中的商品数量,甚至移除商品。

8.3 支付和运输服务

支付和运输服务的创建使得结账过程更加灵活和可扩展。通过统一的服务,我们可以轻松地集成不同的支付和运输方式。例如,在 Payment 类和 Shipment 类中, getAvailableMethods 方法可以返回所有可用的支付和运输服务,这使得用户可以根据自己的需求进行选择。

8.4 结账页面

结账页面是整个销售流程的核心。它分为两个步骤:运输信息收集和支付信息收集。在运输信息收集步骤中,用户需要填写收货地址和选择运输方式;在支付信息收集步骤中,用户需要选择支付方式并提交订单。通过 indexAction paymentAction processAction 方法,我们可以确保整个结账过程的顺利进行。

8.5 订单成功页面

订单成功页面是用户完成购物后的反馈页面。它不仅可以感谢用户的购买,还可以提供订单详情和相关的购物建议。通过 successAction 方法,我们可以将最后创建的订单 ID 传递给模板,让用户可以查看订单详情。

8.6 商店经理仪表盘

商店经理仪表盘为商店管理人员提供了一个便捷的管理界面。通过该界面,管理人员可以添加和管理类别、产品和客户。 indexAction 方法返回的模板中包含了各种管理按钮,方便管理人员进行操作。

9. 代码优化建议

9.1 错误处理

在现有的代码中,错误处理相对较少。例如,在 processAction 方法中,如果数据库操作失败,没有相应的错误处理机制。建议在关键的数据库操作和表单处理中添加错误处理代码,以提高系统的稳定性。

9.2 代码复用

部分代码存在重复的情况,例如在计算购物车总价的代码在多个方法中出现。建议将这些重复的代码提取到一个公共的方法中,以提高代码的复用性。

9.3 性能优化

在查询畅销书列表时,使用了 DQL 查询。可以考虑对数据库进行优化,例如添加索引,以提高查询性能。同时,在处理大量数据时,可以考虑使用分页技术,以减少内存占用。

10. 未来功能扩展

10.1 优惠券系统

可以添加优惠券系统,让用户在结账时可以使用优惠券享受折扣。这需要在数据库中添加优惠券表,并在结账过程中进行相应的处理。

10.2 订单跟踪系统

为用户提供订单跟踪功能,让用户可以实时了解订单的状态。这需要在数据库中添加订单状态表,并在相关的控制器和模板中进行相应的处理。

10.3 多语言支持

为了满足不同地区用户的需求,可以添加多语言支持。这需要在模板和控制器中进行相应的国际化处理。

11. 总结

通过以上步骤,我们完成了一个简单的销售模块的构建。从服务重写、购物车页面的创建到结账流程的实现,再到商店经理仪表盘的搭建,每个环节都紧密相连,构成了一个完整的销售系统。虽然目前的系统还比较简单,但它为后续的功能扩展提供了良好的基础。在未来的开发中,我们可以根据实际需求添加更多的功能,如优惠券系统、订单跟踪系统等,以提高系统的实用性和用户体验。

流程图

graph LR
    A[服务重写] --> B[购物车页面]
    B --> C[支付和运输服务]
    C --> D[结账页面]
    D --> E[订单成功页面]
    E --> F[商店经理仪表盘]
    F --> G[代码优化]
    G --> H[未来功能扩展]

表格

优化建议 具体内容 涉及文件
错误处理 在关键数据库操作和表单处理中添加错误处理代码 多个控制器文件
代码复用 提取重复代码到公共方法 多个控制器文件
性能优化 对数据库添加索引,使用分页技术 数据库相关文件

通过以上的总结和分析,我们可以更好地理解整个销售模块的构建过程,并为后续的开发和优化提供参考。希望这些内容对大家有所帮助。

复杂几何的多球近似MATLAB类及多球模型的比较 MATLAB类Approxi提供了一个框架,用于使用具有迭代缩放的聚集球体模型来近似解剖体积模型,以适应目标体积和模型比较。专为骨科、生物力学和计算几何应用而开发。 MATLAB class for multi-sphere approximation of complex geometries and comparison of multi-sphere models 主要特点: 球体模型生成 1.多球体模型生成:与Sihaeri的聚集球体算法的接口 2.音量缩放 基于体素的球体模型和参考几何体的交集。 迭代缩放球体模型以匹配目标体积。 3.模型比较:不同模型体素占用率的频率分析(多个评分指标) 4.几何分析:原始曲面模型和球体模型之间的顶点到最近邻距离映射(带颜色编码结果)。 如何使用: 1.代码结构:Approxi类可以集成到相应的主脚本中。代码的关键部分被提取到单独的函数中以供重用。 2.导入:将STL(或网格)导入MATLAB,并确保所需的函数,如DEM clusteredSphere(populateSpheres)和inpolyhedron,已添加到MATLAB路径中 3.生成多球体模型:使用DEM clusteredSphere方法从输入网格创建多球体模型 4.运行体积交点:计算多球体模型和参考几何体之间的基于体素的交点,并调整多球体模型以匹配目标体积 5.比较和可视化模型:比较多个多球体模型的体素频率,并计算多球体模型与原始表面模型之间的距离,以进行2D/3D可视化 使用案例: 骨科和生物力学体积建模 复杂结构的多球模型形状近似 基于体素拟合度量的模型选择 基于距离的患者特定几何形状和近似值分析 优点: 复杂几何的多球体模型 可扩展模型(基于体素)-自动调整到目标体积 可视化就绪输出(距离图)
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值