42、订单处理管道的实现与优化

订单处理管道的实现与优化

1. 第三阶段流程概述

订单处理的第三阶段流程如下:
1. 当供应商确认商品已发货时, presentation/admin_order_details.php 调用 OrderProcessor 继续处理订单。
2. OrderProcessor 检测到新的订单状态并调用 PsShipOk
3. PsShipOk 将发货日期录入数据库,更新订单状态,并通知 OrderProcessor 继续处理。
4. OrderProcessor 检测到新的订单状态并调用 PsFinalNotification
5. PsFinalNotification 向客户发送电子邮件,确认订单已发货,并推进订单阶段。
6. OrderProcessor 终止。

若在管道处理的任何环节出现问题,如信用卡被拒,系统会向管理员发送电子邮件。管理员可根据邮件信息检查情况,与相关客户联系,必要时取消或更换订单。

graph LR
    A[供应商确认发货] --> B[调用OrderProcessor]
    B --> C[OrderProcessor检测状态]
    C --> D[调用PsShipOk]
    D --> E[录入发货日期, 更新状态]
    E --> F[OrderProcessor检测新状态]
    F --> G[调用PsFinalNotification]
    G --> H[发送确认邮件, 推进阶段]
    H --> I[OrderProcessor终止]
    J[处理出错] --> K[发送邮件给管理员]

2. 前期准备工作

在构建上述组件之前,需要对数据库和 Web 应用进行一些修改。管道在订单处理中的一个重要功能是维护最新的审计跟踪,这需要向名为 audit 的新数据库表添加记录。同时,还需在数据库中添加名为 orders_create_audit 的新函数,并创建 OrderProcessor 类。该类的初始版本应具备以下功能:
- 动态选择支持 IPipelineSection 接口的管道部分
- 添加基本审计数据
- 访问当前订单详情
- 访问当前订单的客户信息
- 访问管理员邮件信息
- 出现错误时向管理员发送邮件

2.1 接口在 PHP 中的应用

接口是现代面向对象语言的常见特性,代表一组类在实现接口时必须定义的方法。当一个类实现接口时,必须实现该接口定义的所有方法,从而保证实现该接口的类包含特定的方法集。例如,创建的 IPipelineSection 接口包含一个名为 Process() 的方法:

interface IPipelineSection
{
    public function Process($processor);
}

所有代表管道部分的类都将实现此接口,确保每个类都包含 Process() 方法。接口不能像普通类一样实例化,因为它只包含方法签名,不包含方法实现。类使用 implements 运算符实现接口,一个类可以实现多个接口,但这些接口不能包含相同的方法以避免歧义。

2.2 实现订单处理功能框架的步骤

  1. 使用 phpMyAdmin 选择数据库,打开新的 SQL 查询页面。
  2. 执行以下代码创建 audit 表:
-- Create audit table
CREATE TABLE `audit` (
    `audit_id`       INT      NOT NULL AUTO_INCREMENT,
    `order_id`       INT      NOT NULL,
    `created_on`     DATETIME NOT NULL,
    `message`        TEXT     NOT NULL,
    `code`           INT      NOT NULL,
    PRIMARY KEY (`audit_id`),
    KEY `idx_audit_order_id` (`order_id`)
);
  1. 执行以下代码创建 orders_create_audit 存储过程(注意设置分隔符为 $$ ):
-- Create orders_create_audit stored procedure
CREATE PROCEDURE orders_create_audit(IN inOrderId INT,
    IN inMessage TEXT, IN inCode INT)
BEGIN
    INSERT INTO audit (order_id, created_on, message, code)
    VALUES (inOrderId, NOW(), inMessage, inCode);
END$$
  1. business/orders.php Orders 类中添加以下方法:
// Creates audit record
public static function CreateAudit($orderId, $message, $code)
{
    // Build the SQL query
    $sql = 'CALL orders_create_audit(:order_id, :message, :code)';
    // Build the parameters array
    $params = array (':order_id' => $orderId,
        ':message' => $message,
        ':code' => $code);
    // Execute the query
    DatabaseHandler::Execute($sql, $params);
}
  1. business 目录下添加 order_processor.php 文件,代码如下:
<?php
/* Main class, used to obtain order information,
run pipeline sections, audit orders, etc. */
class OrderProcessor
{
    public  $mOrderInfo;
    public  $mOrderDetailsInfo;
    public  $mCustomerInfo;
    public  $mContinueNow;
    private $_mCurrentPipelineSection;
    private $_mOrderProcessStage;
    // Class constructor
    public function __construct($orderId)
    {
        // Get order
        $this->mOrderInfo = Orders::GetOrderInfo($orderId);
        if (empty ($this->mOrderInfo['shipping_id']))
            $this->mOrderInfo['shipping_id'] = -1;
        if (empty ($this->mOrderInfo['tax_id']))
            $this->mOrderInfo['tax_id'] = -1;
        // Get order details
        $this->mOrderDetailsInfo = Orders::GetOrderDetails($orderId);
        // Get customer associated with the processed order
        $this->mCustomerInfo = Customer::Get($this->mOrderInfo['customer_id']);
        $credit_card = new SecureCard();
        $credit_card->LoadEncryptedDataAndDecrypt(
            $this->mCustomerInfo['credit_card']);
        $this->mCustomerInfo['credit_card'] = $credit_card;
    }
    /* Process is called from presentation/checkout_info.php and
    presentation/admin_orders.php to process an order */
    public function Process()
    {
        // Configure processor
        $this->mContinueNow = true;
        // Log start of execution
        $this->CreateAudit('Order Processor started.', 10000);
        // Process pipeline section
        try
        {
            while ($this->mContinueNow)
            {
                $this->mContinueNow = false;
                $this->_GetCurrentPipelineSection();
                $this->_mCurrentPipelineSection->Process($this);
            }
        }
        catch(Exception $e)
        {
            $this->MailAdmin('Order Processing error occurred.',
                'Exception: "' . $e->getMessage() . '" on ' .
                $e->getFile() . ' line ' . $e->getLine(),
                $this->_mOrderProcessStage);
            $this->CreateAudit('Order Processing error occurred.', 10002);
            throw new Exception('Error occurred, order aborted. ' .
                'Details mailed to administrator.');
        }
        $this->CreateAudit('Order Processor finished.', 10001);
    }
    // Adds audit message
    public function CreateAudit($message, $code)
    {
        Orders::CreateAudit($this->mOrderInfo['order_id'], $message, $code);
    }
    // Builds e-mail message
    public function MailAdmin($subject, $message, $sourceStage)
    {
        $to = ADMIN_EMAIL;
        $headers = 'From: ' . ORDER_PROCESSOR_EMAIL . "\r\n";
        $body = 'Message: ' . $message . "\n" .
            'Source: ' . $sourceStage . "\n" .
            'Order ID: ' . $this->mOrderInfo['order_id'];
        $result = mail($to, $subject, $body, $headers);
        if ($result === false)
        {
            throw new Exception ('Failed sending this mail to administrator:' .
                "\n" . $body);
        }
    }
    // Gets current pipeline section
    private function _GetCurrentPipelineSection()
    {
        $this->_mOrderProcessStage = 100;
        $this->_mCurrentPipelineSection = new PsDummy();
    }
}
?>
  1. business/i_pipeline_section.php 文件中创建 IPipelineSection 接口:
<?php
interface IPipelineSection
{
    public function Process($processor);
}
?>
  1. business 目录下添加 ps_dummy.php 文件,代码如下:
<?php
class PsDummy implements IPipelineSection
{
    public function Process($processor)
    {
        $processor->CreateAudit('PsDoNothing started.', 99999);
        $processor->CreateAudit('Customer: ' .
            $processor->mCustomerInfo['name'], 99999);
        $processor->CreateAudit('Order subtotal: ' .
            $processor->mOrderInfo['total_amount'], 99999);
        $processor->MailAdmin('Test.', 'Test mail from PsDummy.', 99999);
        $processor->CreateAudit('PsDoNothing finished', 99999);
    }
}
?>
  1. include/config.php 中添加以下代码,并根据实际情况修改电子邮件地址:
// Constant definitions for order handling related messages
define('ADMIN_EMAIL', 'Admin@example.com');
define('CUSTOMER_SERVICE_EMAIL', 'CustomerService@example.com');
define('ORDER_PROCESSOR_EMAIL', 'OrderProcessor@example.com');
define('SUPPLIER_EMAIL', 'Supplier@example.com');
  1. admin.php 中添加相关文件的引入:
// Load Business Tier
...
require_once BUSINESS_DIR . 'customer.php';
require_once BUSINESS_DIR . 'i_pipeline_section.php';
require_once BUSINESS_DIR . 'ps_dummy.php';
require_once BUSINESS_DIR . 'order_processor.php';
  1. 修改 presentation/templates/admin_order_details.tpl ,添加处理订单按钮:
<input type="submit" name="submitCancel" value="Cancel"
{if ! $obj->mEditEnabled} disabled="disabled" {/if} />
<input type="submit" name="submitProcessOrder" value="Process Order" />
</p>
<h3>Order contains these products:</h3>
  1. 修改 presentation/admin_order_details.php ,添加订单处理逻辑:
// Initializes class members
public function init()
{
    if (isset ($_GET['submitUpdate']))
    {
        Orders::UpdateOrder($this->mOrderId, $_GET['status'],
            $_GET['comments'], $_GET['authCode'], $_GET['reference']);
    }
    if (isset ($_GET['submitProcessOrder']))
    {
        $processor = new OrderProcessor($this->mOrderId);
        $processor->Process();
    }
    $this->mOrderInfo = Orders::GetOrderInfo($this->mOrderId);
    $this->mOrderDetails = Orders::GetOrderDetails($this->mOrderId);
}
  1. 在浏览器中加载订单管理页面,选择一个订单查看详情,点击“Process Order”按钮。若要向本地邮件账户发送错误邮件,需启动 SMTP 服务器(Red Hat 或 Fedora Linux 系统使用 service sendmail start 命令,Windows 系统需确保 IIS 中的默认 SMTP 虚拟服务器已启动)。
  2. 检查收件箱,应收到内容为“Test mail from PsDummy.”的电子邮件。
  3. 检查数据库中的 audit 表,查看新添加的记录。

3. 订单处理功能框架的工作原理

OrderProcessor 类和各个管道阶段会添加记录以指示处理的成功或失败,这些记录可用于检查订单处理情况,对错误检查至关重要。审计表中的 code 列允许将特定消息与标识号关联,虽然可以创建另一个数据库表将代码号与描述匹配,但编号方案本身已具有较强的描述性。

OrderProcessor 类的主要方法是 Process() ,该方法从 presentation/admin_order_details.php 调用以处理订单。处理过程中,通过 CreateAudit() 方法添加审计记录,使用 _GetCurrentPipelineSection() 方法选择要处理的管道部分,并调用其 Process() 方法。若处理过程中出现异常,会使用 MailAdmin() 方法向管理员发送邮件,添加错误审计记录,并抛出新的异常。

审计代码采用五位数方案,第一位数字表示审计记录由 OrderProcessor (1)还是管道部分(2)添加,接下来的两位数字表示添加审计的管道阶段,最后两位数字在该范围内唯一标识消息。例如:
| 代码 | 描述 |
| ---- | ---- |
| 10000 | 订单处理器启动 |
| 10001 | 订单处理器完成 |
| 10002 | 订单处理出错 |

PsDummy 类用于测试,通过调用 OrderProcessor CreateAudit() MailAdmin() 方法生成测试信息,以验证代码是否正确执行。虽然实现这些功能需要大量代码,但 OrderProcessor 承担了大部分工作,使客户端代码变得简单。这充分体现了强大的设计能够带来强大而健壮的代码。

4. 订单处理代码的更新

为了完善订单处理功能,需要对 OrderProcessor 类进行一些补充,通过以下几个短练习来实现本章开头列出的功能,包括更新订单状态、设置信用卡认证详情、设置订单发货日期、向客户和供应商发送电子邮件以及检索订单详情和客户地址。

4.1 更新订单状态

每个管道部分都需要具备更改订单状态的能力,这需要在数据库中创建名为 orders_update_status 的新存储过程,并在 Orders 类中添加 UpdateOrderStatus() 方法。

步骤如下
  1. 在数据库中创建 orders_update_status 存储过程:
-- Create orders_update_status stored procedure
CREATE PROCEDURE orders_update_status(IN inOrderId INT, IN inStatus INT)
BEGIN
    UPDATE orders SET status = inStatus WHERE order_id = inOrderId;
END$$
  1. business/orders.php Orders 类中添加 UpdateOrderStatus() 方法:
// Updates the order pipeline status of an order
public static function UpdateOrderStatus($orderId, $status)
{
    // Build the SQL query
    $sql = 'CALL orders_update_status(:order_id, :status)';
    // Build the parameters array
    $params = array (':order_id' => $orderId, ':status' => $status);
    // Execute the query
    DatabaseHandler::Execute($sql, $params);
}
  1. business/order_processor.php OrderProcessor 类中添加 UpdateOrderStatus() 方法:
// Set order status
public function UpdateOrderStatus($status)
{
    Orders::UpdateOrderStatus($this->mOrderInfo['order_id'], $status);
    $this->mOrderInfo['status'] = $status;
}

4.2 设置信用卡认证详情

在处理信用卡使用时,需要设置订单的授权码和参考码,这需要在数据库中创建 orders_set_auth_code 存储过程,并在 Orders 类和 OrderProcessor 类中添加相应方法。

步骤如下
  1. 在数据库中添加 orders_set_auth_code 存储过程:
-- Create orders_set_auth_code stored procedure
CREATE PROCEDURE orders_set_auth_code(IN inOrderId INT,
    IN inAuthCode VARCHAR(50), IN inReference VARCHAR(50))
BEGIN
    UPDATE orders
    SET    auth_code = inAuthCode, reference = inReference
    WHERE  order_id = inOrderId;
END$$
  1. business/orders.php Orders 类中添加 SetOrderAuthCodeAndReference() 方法:
// Sets order's authorization code
public static function SetOrderAuthCodeAndReference ($orderId, $authCode,
    $reference)
{
    // Build the SQL query
    $sql = 'CALL orders_set_auth_code(:order_id, :auth_code, :reference)';
    // Build the parameters array
    $params = array (':order_id' => $orderId,
        ':auth_code' => $authCode,
        ':reference' => $reference);
    // Execute the query
    DatabaseHandler::Execute($sql, $params);
}
  1. business/order_processor.php OrderProcessor 类中添加 SetAuthCodeAndReference() 方法:
// Set order's authorization code and reference code
public function SetAuthCodeAndReference($authCode, $reference)
{
    Orders::SetOrderAuthCodeAndReference($this->mOrderInfo['order_id'],
        $authCode, $reference);
    $this->mOrderInfo['auth_code'] = $authCode;
    $this->mOrderInfo['reference'] = $reference;
}

4.3 设置订单发货日期

当订单发货时,需要更新数据库中的发货日期,这需要在数据库中创建 orders_set_date_shipped 存储过程,并在 Orders 类和 OrderProcessor 类中添加相应方法。

步骤如下
  1. 在数据库中添加 orders_set_date_shipped 存储过程:
-- Create orders_set_date_shipped stored procedure
CREATE PROCEDURE orders_set_date_shipped(IN inOrderId INT)
BEGIN
    UPDATE orders SET shipped_on = NOW() WHERE order_id = inOrderId;
END$$
  1. business/orders.php Orders 类中添加 SetDateShipped() 方法:
// Set order's ship date
public static function SetDateShipped($orderId)
{
    // Build the SQL query
    $sql = 'CALL orders_set_date_shipped(:order_id)';
    // Build the parameters array
    $params = array (':order_id' => $orderId);
    // Execute the query
    DatabaseHandler::Execute($sql, $params);
}
  1. business/order_processor.php OrderProcessor 类中添加 SetDateShipped() 方法:
// Set order's ship date
public function SetDateShipped()
{
    Orders::SetDateShipped($this->mOrderInfo['order_id']);
    $this->mOrderInfo['shipped_on'] = date('Y - m - d');
}

4.4 向客户和供应商发送电子邮件

为了实现向客户和供应商发送电子邮件的功能,需要在 OrderProcessor 类中添加 MailCustomer() MailSupplier() 方法。

步骤如下
  1. OrderProcessor 类中添加 MailCustomer() 方法:
// Send e-mail to the customer
public function MailCustomer($subject, $body)
{
    $to = $this->mCustomerInfo['email'];
    $headers = 'From: ' . CUSTOMER_SERVICE_EMAIL . "\r\n";
    $result = mail($to, $subject, $body, $headers);
    if ($result === false)
    {
        throw new Exception ('Unable to send e-mail to customer.');
    }
}
  1. OrderProcessor 类中添加 MailSupplier() 方法:
// Send e-mail to the supplier
public function MailSupplier($subject, $body)
{
    $to = SUPPLIER_EMAIL;
    $headers = 'From: ' . ORDER_PROCESSOR_EMAIL . "\r\n";
    $result = mail($to, $subject, $body, $headers);
    if ($result === false)
    {
        throw new Exception ('Unable to send e-mail to supplier.');
    }
}

4.5 检索订单详情和客户地址

为了方便获取订单详情和客户地址,需要在 OrderProcessor 类中添加 GetCustomerAddressAsString() GetOrderAsString() 方法。

步骤如下
  1. business/order_processor.php OrderProcessor 类中添加 GetCustomerAddressAsString() 方法:
// Returns a string that contains the customer's address 
public function GetCustomerAddressAsString()
{
    $new_line = "\n";
    $address_details = $this->mCustomerInfo['name'] . $new_line .
        $this->mCustomerInfo['address_1'] . $new_line;
    if (!empty ($this->mOrderInfo['address_2']))
        $address_details .= $this->mCustomerInfo['address_2'] . $new_line;
    $address_details .= $this->mCustomerInfo['city'] . $new_line .
        $this->mCustomerInfo['region'] . $new_line .
        $this->mCustomerInfo['postal_code'] . $new_line .
        $this->mCustomerInfo['country'];
    return $address_details;
}
  1. business/order_processor.php OrderProcessor 类中添加 GetOrderAsString() 方法:
// Returns a string that contains the order details
public function GetOrderAsString($withCustomerDetails = true)
{
    $total_cost = 0.00;
    $order_details = '';
    $new_line = "\n";
    if ($withCustomerDetails)
    {
        $order_details = 'Customer address:' . $new_line .
            $this->GetCustomerAddressAsString() .
            $new_line . $new_line;
        $order_details .= 'Customer credit card:' . $new_line .
            $this->mCustomerInfo['credit_card']->CardNumberX .
            $new_line . $new_line;
    }
    foreach ($this->mOrderDetailsInfo as $order_detail)
    {
        $order_details .= $order_detail['quantity'] . ' ' .
            $order_detail['product_name'] . '(' .
            $order_detail['attributes'] . ') $' .
            $order_detail['unit_cost'] . ' each, total cost $' .
            number_format($order_detail['subtotal'],
                2, '.', '') . $new_line;
        $total_cost += $order_detail['subtotal'];
    }
    // Add shipping cost
    if ($this->mOrderInfo['shipping_id'] != -1)
    {
        $order_details .= 'Shipping: ' . $this->mOrderInfo['shipping_type'] .
            $new_line;
        $total_cost += $this->mOrderInfo['shipping_cost'];
    }
    // Add tax
    if ($this->mOrderInfo['tax_id'] != -1 &&
        $this->mOrderInfo['tax_percentage'] != 0.00)
    {
        $tax_amount = round((float)$total_cost *
            (float)$this->mOrderInfo['tax_percentage'], 2)
            / 100.00;
        $order_details .= 'Tax: ' . $this->mOrderInfo['tax_type'] . ', $' .
            number_format($tax_amount, 2, '.', '') .
            $new_line;
        $total_cost += $tax_amount;
    }
    $order_details .= $new_line . 'Total order cost: $' .
        number_format($total_cost, 2, '.', '');
    return $order_details;
}

4.6 订单处理器修改的工作原理

通过对 Orders OrderProcessor 类的修改以及创建多个数据库存储过程,构建了支持订单处理管道的基础设施代码,这对于专业的电子商务网站至关重要。这些功能的实现使得订单处理更加灵活和完善,能够满足不同场景下的订单处理需求。

以下是订单处理更新功能的流程图:

graph LR
    A[开始] --> B[更新订单状态]
    B --> C[设置信用卡认证详情]
    C --> D[设置订单发货日期]
    D --> E[发送电子邮件]
    E --> F[检索订单详情和客户地址]
    F --> G[结束]

综上所述,通过上述步骤和代码实现了一个完整的订单处理系统,包括订单处理的各个阶段、审计记录的添加、错误处理以及与数据库和电子邮件的交互。这些功能的实现为电子商务网站的订单处理提供了强大而可靠的支持。

**项目名称:** 基于Vue.jsSpring Cloud架构的博客系统设计开发——微服务分布式应用实践 **项目概述:** 本项目为计算机科学技术专业本科毕业设计成果,旨在设计并实现一个采用前后端分离架构的现代化博客平台。系统前端基于Vue.js框架构建,提供响应式用户界面;后端采用Spring Cloud微服务架构,通过服务拆分、注册发现、配置中心及网关路由等技术,构建高可用、易扩展的分布式应用体系。项目重点探讨微服务模式下的系统设计、服务治理、数据一致性及部署运维等关键问题,体现了分布式系统在Web应用中的实践价值。 **技术架构:** 1. **前端技术栈:** Vue.js 2.x、Vue Router、Vuex、Element UI、Axios 2. **后端技术栈:** Spring Boot 2.x、Spring Cloud (Eureka/Nacos、Feign/OpenFeign、Ribbon、Hystrix、Zuul/Gateway、Config) 3. **数据存储:** MySQL 8.0(主数据存储)、Redis(缓存会话管理) 4. **服务通信:** RESTful API、消息队列(可选RabbitMQ/Kafka) 5. **部署运维:** Docker容器化、Jenkins持续集成、Nginx负载均衡 **核心功能模块:** - 用户管理:注册登录、权限控制、个人中心 - 文章管理:富文本编辑、分类标签、发布审核、评论互动 - 内容展示:首页推荐、分类检索、全文搜索、热门排行 - 系统管理:后台仪表盘、用户内容监控、日志审计 - 微服务治理:服务健康检测、动态配置更新、熔断降级策略 **设计特点:** 1. **架构解耦:** 前后端完全分离,通过API网关统一接入,支持独立开发部署。 2. **服务拆分:** 按业务域划分为用户服务、文章服务、评论服务、文件服务等独立微服务。 3. **高可用设计:** 采用服务注册发现机制,配合负载均衡熔断器,提升系统容错能力。 4. **可扩展性:** 模块化设计支持横向扩展,配置中心实现运行时动态调整。 **项目成果:** 完成了一个具备完整博客功能、具备微服务典型特征的分布式系统原型,通过容器化部署验证了多服务协同运行的可行性,为云原生应用开发提供了实践参考。 资源来源于网络分享,仅用于学习交流使用,请勿用于商业,如有侵权请联系我删除!
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符  | 博主筛选后可见
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值