订单处理管道的实现与优化
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 实现订单处理功能框架的步骤
-
使用
phpMyAdmin选择数据库,打开新的 SQL 查询页面。 -
执行以下代码创建
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`)
);
-
执行以下代码创建
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$$
-
在
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);
}
-
在
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();
}
}
?>
-
在
business/i_pipeline_section.php文件中创建IPipelineSection接口:
<?php
interface IPipelineSection
{
public function Process($processor);
}
?>
-
在
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);
}
}
?>
-
在
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');
-
在
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';
-
修改
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>
-
修改
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);
}
-
在浏览器中加载订单管理页面,选择一个订单查看详情,点击“Process Order”按钮。若要向本地邮件账户发送错误邮件,需启动 SMTP 服务器(Red Hat 或 Fedora Linux 系统使用
service sendmail start命令,Windows 系统需确保 IIS 中的默认 SMTP 虚拟服务器已启动)。 - 检查收件箱,应收到内容为“Test mail from PsDummy.”的电子邮件。
-
检查数据库中的
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()
方法。
步骤如下
-
在数据库中创建
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$$
-
在
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);
}
-
在
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
类中添加相应方法。
步骤如下
-
在数据库中添加
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$$
-
在
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);
}
-
在
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
类中添加相应方法。
步骤如下
-
在数据库中添加
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$$
-
在
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);
}
-
在
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()
方法。
步骤如下
-
在
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.');
}
}
-
在
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()
方法。
步骤如下
-
在
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;
}
-
在
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[结束]
综上所述,通过上述步骤和代码实现了一个完整的订单处理系统,包括订单处理的各个阶段、审计记录的添加、错误处理以及与数据库和电子邮件的交互。这些功能的实现为电子商务网站的订单处理提供了强大而可靠的支持。
超级会员免费看
170万+

被折叠的 条评论
为什么被折叠?



