第 1 章:业务需求和设计模型

摘要:本章讨论了指导如何设计 ConsolidatedRetail.com(它属于企业对消费者 (B2C) 网站,采用 Microsoft 商务参考体系结构)的业务需求,并概括了在此应用程序设计期间确定的实际业务需求。本章还概要介绍了 Microsoft 解决方案框架 (Microsoft Solutions Framework,MSF) 三层应用程序模型和阶段式设计过程。

请注意:尽管此处提到的业务需求仅仅局限于一个可轻松安装的参考示例所具有的能力,但是本“开发人员指南”提供的信息线索非常有用,通过这些线索,您可以对该应用程序进行升级,使其满足生产环境的需求。

简介

请 Web 用户给电子商务站点定义时,一般用户可能会回答电子商务站点就是可以用信用卡购买商品的在线商店。尽管这个定义相当正确,却没有充分说明目前为 Internet 开发的各种电子商务站点的特点。在迅猛发展的 Internet 商务时代,一个高效率的电子商务网站绝不仅仅是基于 Web 的商店。

用户对电子商务站点的要求越来越高,如果某个站点无法满足他们的要求,他们就将弃之而去。那么,用户对电子商务有哪些要求呢?下表列出了一些影响应用程序设计的主要问题。

  • 易于使用/导航
  • 性能高
  • 匿名购物
  • 维护用户配置文件
  • 安全性好
  • 能够通过多种设备访问站点
  • 通过可管理性提高竞争优势

粗略一看,在上述问题中,有些应由应用程序设计人员负责解决,有些似乎应由企业决策者或基本结构专家负责解决。不过,如果您仔细思考这些问题,就会明白这些问题为什么都与应用程序的设计有关。

易于使用/导航

网站理所当然地应该易于使用和导航。毕竟,企业不希望消费者在购买自己的产品时遇到困难,而消费者也更愿意在自己能轻松找到结帐页的站点消费。

使站点易于使用的一种方法是确保在常见任务上使用大家熟悉的类似方法。这意味着在消费者完成购买(或“结帐”)之前,可将其选购的商品存储在购物篮或筐中。这种比喻可便于不熟悉计算机的人理解站点是如何工作的,从而开展购买活动。

使站点易于导航比您最初想象的要困难得多。Web 完全是以一种非线性方式工作的,用户单击链接的顺序经常无法预料。因此,您应该确保无论用户目前在查看哪一页,站点向用户展示的始终是完全一致的界面,并确保只需单击一个链接即可访问重要网页(如主页、购物篮所在页以及用户帐户信息所在页等)。在 ConsolidatedRetail.com 站点上,顶部的标帜始终包含到购物篮所在页、消费者帐户所在页和主页的链接,而左侧的面板上始终包含搜索和目录链接。

还有一种方法可以确保用户能在站点中找到所需内容,这就是要以逻辑方式编排产品清单或目录。如果将目录分成几个类别和许多可能的子类别,即可让消费者轻而易举地找到他们感兴趣的产品。此外,还应给用户提供搜索功能,以便他们在不太清楚某种产品的陈列位置时可以进行搜索。

如果您的站点易于使用和导航,消费者将乐意使用。相反,如果使用起来比较困难,消费者可能就会弃之而去,另择站点。

性能高

在网站的设计当中,影响其性能的因素很多。由于不同的人对性能的要求各不相同,因而,对于什么才是可接受的性能水平也将因人而异。

尽量减少响应时间

大多数人认为:提供可接受的响应时间的站点才是性能良好的站点。响应时间是指用户在请求了某个操作之后、能够看到结果之前需要等待的时间量。在理想情况下,我们都希望站点上的操作瞬时就能得到执行;但在实际生活中,我们需要接受这样一个事实:有限的带宽、数据库并发性和业务处理任务通常都会导致轻微的延迟。因此,设计电子商务站点时,应尽量减少那些对响应时间有负面影响的因素(尽管不能完全排除它们)。

电子商务优化的关键在于减少执行诸如结帐之类的操作所耗费的时间,这样,消费者就不会因排队等待而放弃自己选购的商品,您也就不会因此而失去订单。

尽量增强可扩展性

性能的另一个重要方面就是“可扩展性”。这是指添加资源时站点容量增加的能力。从用户角度来看,这意味着当大量用户同时访问站点时,站点仍能提供可接受的响应时间。许多开发人员经常会得到这样令人沮丧的消息:当访问的用户达到一定数量(这个数量是实际生活要求达到的数量)后,在开发机上性能卓越的测试站点就无法应付。

那么,如何才能最大限度地增强站点的可扩展性呢?两种典型的方法就是“向上扩展”和“向外扩展”。

向上扩展

第一种方法(“向上扩展”)就是通过采用更好和/或更快的 CPU、更大的 RAM、更快的磁盘等等来增强服务器的处理能力。这种方法非常有效,尤其是在数据层上,该层上的一些大型数据库需要相对较强的处理能力。不过,由于硬件成本随处理能力的加强而按指数增长,因此,服务器越接近顶端,这种方法就愈加不合算。

向外扩展

“向外扩展”则从另一个方面来解决问题,即由“群集”(或服务器集合,也称为“Web 领域”)中的多个服务器来分担处理工作量。Web 领域在硬件方面的花费更为合算,而且提供了更为灵活、可扩展的解决方案。当站点上的负载增加时,可以很轻松地将服务器添加到 Web 领域中。

Microsoft® Windows® 2000 Advanced Server 和 Windows 2000 Datacenter Server 以及 Windows 网络负载平衡 (Windows Network Load Balancing,NLB) 服务一起,将整个 Web 领域作为一个具有单一 IP 的逻辑服务器显示在 Internet 上。收到请求之后,会根据负载情况将请求分发给领域中的服务器,这些服务器可使用主干网络进行通信,也可以与数据库服务器进行通信。图 1-1 显示 Web 领域的基本体系结构。

图 1-1:Web 领域

管理 Web 领域中的状态

对于商务站点设计人员而言,最重要的问题之一就是 Web 领域中的应用程序状态问题。状态就是在两个用户请求之间必须保留的会话数据;例如,在用户继续浏览站点期间,必须一直维护该用户购物篮中的物品原状。即使每个用户请求可能是由 Web 领域中不同的服务器处理的,也须如此。

许多 ASP 开发人员使用“会话”对象来存放状态数据。不过,通常应避免使用此方法。为了优化站点的软件体系结构以便在服务器领域中加以实现,Web 前端禁止维护内存中的用户状态。如果前端服务器维护用户状态,将出现以下问题:

  • 用户会话将依附于特定服务器(会话相关性),这会破坏动态地将请求分配给服务器的网络负载平衡策略。此外,还会破坏服务器领域的可靠性,因为当原服务器发生故障(并丢失了其内存中的会话状态信息)时,就无法将用户会话转移到其他服务器。
  • 内存资源被前端服务器耗费在存放用户会话状态的细节上,从而减少了可用于处理请求和高速缓存内容的内存。如果一个受欢迎的站点能够在短时间内吸引大量的用户,则状态维护方面的内存需求可能非常大。为了部分解决内存需求问题,Commerce Server 大量使用了高速缓存。对配置文件架构、折扣和商业活动都将进行高速缓存。
  • 除了避免会话相关性之外,还应避免使前端操作与长时间运行的操作发生关联,以便将前端操作设计为快速执行的操作。由于 IIS 是用一个缓冲池来处理请求而缓冲池包含的工作器线程数量是有限的,因而当这些线程都已被占用且在等待长时间运行的操作完成时,传入请求等待处理的平均时间就会增加。

匿名购物(浏览)

通常,用户都不愿意仅仅为了了解站点在销售哪些商品而被迫登录到站点。因此,站点应在不需要身份验证的情况下,允许用户以匿名方式浏览商品,甚至允许他们将一部分商品放入购物篮中。

维护用户配置文件

当用户再次访问站点时,他们不希望重新输入上次访问时输入过的相同资料。一旦向站点提供了自己的购物和联系信息后,用户就希望站点能够记住这些数据。

为了实现此目的,许多站点会为每个已注册的用户维护其用户“配置文件”信息。在大多数情况下,用户都需要注册,以便提供最少量的配置文件信息,如用户名和口令。然后,用户会分配到一个唯一标识符,该标识符可用作其配置文件数据的主密钥。

用户在站点上注册之后,其配置文件信息就可以保存在数据库中,以便在以后需要时调用。通常,用户可以添加一些必备信息,指定一些细节,如电子邮件地址、电话号码、发货地址或任何其他允许用户添加的个人信息。

保留用户配置文件信息相当有用,其原因如下:

  • 使用户在以后访问时不必重新输入数据。
  • 可用于分析用户在站点上的活动。
  • 可作为个性化的基础,允许您根据特定的用户群发布标帜广告或开展打折活动。
  • 可用于商业分析,如根据特定的配置文件值跟踪购买趋势。

通过可管理性提高竞争优势

尽管应用程序设计人员不负责业务决策(如定价、广告活动等等),电子商务解决方案的设计对企业如何应对市场趋势和竞争对手活动却有着巨大影响。业务经理开展的管理活动要受电子商务站点管理功能的制约。要取得成功,电子商务解决方案必须易于使用,还必须具备全面的管理基础结构。

为电子商务站点设计管理界面时有两个基本选择。您可以创建自己自定义的界面,也可以使用一种“现成的”解决方案,如 Microsoft Commerce Server 2000 Business Desk。

如果构建自己的管理界面,您将能完全按照自己的愿望来设计站点的管理功能。不过,这样会给一个已经很大的软件项目增加大量开发工作量,其工作量几乎等于或大于软件项目本身的工作量。默认情况下,Commerce Server Business Desk 可以满足电子商务站点的大多数管理要求,如果需要还可以通过创建自定义模块来添加其他功能。

本章的其余部分将说明在该项目的规划阶段确认的实际业务需求,以及在 ConsolidatedRetail.com 应用程序的设计中所使用的应用程序模型和设计过程。

参考应用程序业务需求

在设计应用程序之前,应该明确该应用程序必须执行哪些任务。分析业务需求是应用程序开发中最重要的步骤之一。确认业务需求的目的在于创建一个能同时满足零售商和消费者需要的解决方案。这样,需求就转换成了业务需求文档,这种文档可作为开发整个项目的指南。

本节概括了为参考体系结构应用程序 ConsolidatedRetail.com 确定的实际需求。请注意:此处所用的业务需求被有意限制为一个可轻松安装的参考示例具有的能力。

功能需求

ConsolidatedRetail.com 旨在满足以下功能需求:

易于导航

站点应易于导航。链接应该清晰、易于理解而且实用。用户应能够在页和屏幕之间随意移动。

易于使用

应用程序应易于使用。应该易于购买产品和访问“结帐”页。

站点应使用易于理解的比喻,例如:将选购的物品存储在“购物篮”中,直到购物者准备结帐

站点上的每一页都应显示完全一致的界面。重要页或常用页应只需单击一次即可访问。

可用性测试

站点应使不熟悉计算机的人易于理解。

站点访问

用户能通过以下方法访问站点:

  • 在浏览器中输入 URL
  • 从其他站点或电子邮件的链接访问

维护用户注册/配置文件

无论从站点上的任何页,用户都必须能够注册,这样,用户就不必在每次下订单时都重新输入相同的信息。用户无需注册即可浏览站点;但结帐时必须注册。另外,申请电子邮件时事通讯、特价通知等服务时要求注册。

注册涉及:

  • 配置文件信息:用户名、付款地址、主要发货地址、电话号码和电子邮件地址。
  • 身份验证信息:用户身份标识(用户 ID)和口令应保留在应用程序中。
  • 付款信息:用户应可以输入信用卡信息并保存该信息。应用程序应能够保存多个信用卡号。
  • 首选项:用户应能够指定是否想得到有关发货状态的电子邮件通知(默认值为“是”),以及是否想得到有关销售价格和特价的通知(默认值为“否”)。
  • 地址簿:用户应能够存储任意多个附加发货地址。

保留用户配置文件信息相当有用,其原因如下:

  • 使用户在以后访问时不必重新输入数据。
  • 可作为个性化的基础,允许您根据特定的用户群发布标帜广告或开展打折活动。
  • 可用于商业分析,例如,根据特定的配置文件值跟踪购买趋势。

用户注册管理

用户登录并经过身份验证之后,用户应能够修改、添加或删除注册信息。除“用户 ID”字段之外,所有其他字段都应是可编辑字段。

登录/身份验证

用户一经注册之后,如果该用户返回到站点,他或她应能够从该站点上的任何页登录。

浏览

用户应能够浏览目录。在主页上,应向用户显示目录清单。在用户选择了一个目录之后,应向其显示子类别或实际产品。

匿名浏览

用户应能够以匿名方式浏览目录;即:用户应能够在不必登录的情况下即可查看产品。

多目录

应用程序应支持多目录。多目录产品的汇总对用户应是透明的。

产品和类别

应用程序应允许将产品与一个或多个目录关联。

产品页

应用程序应有一产品页,其中包括该产品项目的较大图片和/或该产品项目的详细说明。在此页,应能够将该产品添加到购物篮中。在此页,用户应能够:

  • 将产品项目添加到购物篮中
  • 浏览下一个项目
  • 浏览上一个项目
  • 返回上一页

产品搜索

主页以及所有类别页和子类别页都应能进行搜索。用户应能够输入多个词。如果用户指定多个词,将根据这些词构建使用“and”运算符的布尔查询。

如果用户在主页上,搜索将默认为“搜索所有类别”。在类别和子类别页执行的搜索将默认为“在‘类别名’范围内搜索”。用户可以选择要搜索的特定站点区域或特定类别,以便覆盖这些默认设置。

如果站点使用多目录,将对所有目录执行搜索。如果站点展示了多个目录(并有一个分层产品清单),则不会按此规则进行搜索。在类别/产品分层结构中,每个目录都是第一级。在这种情况下,默认为只搜索用户当前所在的目录。用户可以覆盖默认设置,选择搜索其他目录或整个站点。这类似于先前描述的为“多目录”指定的行为。

默认情况下,将针对关键词和标题进行搜索。

产品搜索结果

“搜索结果”页应显示一系列产品项目及其相应类别(或目录)。项目应按类别或目录分组。每个搜索结果都应提供到相应产品页的超文本链接。

向购物篮中添加项目

无论从任何产品页中,用户都应能够将一个或多个项目添加到购物篮中。这些项目可来自不同的目录。每添加一个项目,篮中的项目数也会相应地增加。该数目显示在该篮子图标旁边。

管理购物篮

用户应随时能够管理购物篮。用户可指定项目是“活动的”(实际购买的标记)还是“保留的”(标识为将来可能购买)。用户查看购物篮时可进行以下选择:

  • 删除单个项目。
  • 更改每种项目的数量。
  • 保留任何项目,以备将来购买。
  • 删除购物篮中的所有项目。
  • 保留购物篮中的所有项目,以备将来购买。
  • 将项目移入购物篮的保留(将来购买)区和从中移出项目。
  • 检索保留的订单。

保留购物篮或项目

用户应能够保留选定的项目或购物篮中的所有物品,以备将来购买。只有已注册并登录的用户可以保留其项目。如果用户尚未登录或注册,将提示他们进行此操作。用户完成此操作之后,将返回到“保留购物篮”操作。

结帐

无论从任何屏幕,用户都应能够结帐。结帐时,将向用户显示所有订购的项目(购物篮)。此时,用户应能够管理购物篮。用户对购物篮中的物品进行确认后,将出现“发货”屏幕。每个项目都将与该用户的主要发货地址关联。用户可以用地址簿中的一个地址或新地址来替换该地址。如果用户添加了一个新地址,他或她可以选择将该新地址保存在地址簿中。

用户为每个项目指派了地址(或接受了默认的地址)之后,他或她可以转至“发货”屏幕,选择每个地址的交货方式。默认方式由站点所有者决定。用户选择交货方式后,他或她可以继续到“订单一览表”屏幕。该屏幕应按发货地址划分。在每个地址下,将列出项目说明、项目价格以及价格合计(若有)。对该项目的价格合计进行小计,将装运费用作为明细项目列出并进行小计,最后将列出该地址下的税金和总金额。

对所有地址下的金额求和之后,会在该页的末尾列出总计。用户可以:

  • 接受订单
  • 修改订单
  • 取消订单
  • 继续购物

如果用户选择修改订单,他或她将返回到“管理购物篮”页。如果用户选择取消订单,将清空购物篮。如果用户选择继续购物,他或她应该返回到主页。

如果用户选择接受订单,他或她将转至“付款”页。如果用户已在“注册”页中存储了信用卡信息,则显示该信息。用户可选择使用保存的信用卡,也可选择忽略保存的信息,提供新的信用卡信息。如果用户添加新的信用卡信息,他或她应可以选择将新信息添加到保存的注册信息中。

用户选择或输入了信用卡信息之后,他或她可以:

  • 取消订单
  • 修改订单
  • 继续购物
  • 提交订单

如果用户提交了订单,将收到确认页和订单号。

发货选择

必须支持以下发货选择:

  • 装运港地面交货
  • 次日交货
  • 隔夜交货
  • 国际交货

订单状态通知

用户可选择接收有关订单状态的电子邮件通知。

装运费用的计算

装运费用的计算基于承运人的类型(如 UPS)或站点所有者规定的其他规则。

税金的计算

税金的计算必须基于站点所有者规定的规则。这些规则应包括:

  • 销售地点
  • 发货地址
  • 货物类型

结帐时,税金信息将显示在“订单一览表”屏幕上。

订单一览表

该屏幕显示每个订单的地址、项目说明、项目价格、装运费用、税金和费用总计(若有)。

地址簿

已注册的用户都会保存在地址簿中。尽管站点所有者可设置一些限制,该地址簿仍可存放无限的发货地址信息。

订单的取消

用户在提交订单之前必须能够随时取消订单。此操作将导致购物篮中的所有项目都被清空。但保留的项目不会受到影响。

系统需求

站点必须满足以下系统范围的需求:

全球化能力

应用程序应能够进行自定义以适应不同的文化环境。即:界面颜色、导航布局、页结构和语言都应可以修改。

性能

用户在每次访问该站点时都应能体验到始终如一的性能。站点的表现应和其他正在使用的企业电子商务应用程序一样好。

可扩展性

站点应既能向上扩展又能向外扩展。如果添加了更快的磁盘和 CPU 或添加了更大的 RAM,响应应更快。如果给 Web 领域添加了更多的服务器,响应也应该有所改进。Web 领域中的服务器应能正确处理请求。

可用性

站点应处于开启和运行状态,且应无任何故障。它应能捕获错误,此功能应不会防止用户访问站点授权的区域。站点应随时能接受用户的访问。

可管理性

站点上应有一个管理界面,用于修改和管理公司报表、目录、订单、装运费用、税率和用户帐户。

安全性

站点应保护机密信息,如信用卡号。站点应显示保密政策和任何相关的版权信息。用户 ID 和口令应防止未经授权的人员访问敏感信息。

允许通过多种设备访问

站点必须能在多种客户端设备上正常运转。站点应能在低版本的浏览器和高版本的浏览器上工作。

以文档形式记录业务需求

确定了基本需求之后,应在“构想/范围”文档中捕捉、传递和批准这些需求,该文档标识了应用程序业务值、需求和限制以及规划、设计和完成项目所需的人员。之后,即可开始设计了。

下一节说明在 ConsolidatedRetail.com 的创建过程中所用的应用程序设计模型和设计过程。

MSF 应用程序模型

ConsolidatedRetail.com 应用程序的设计遵循 Microsoft 解决方案框架 (MSF) 中定义的三层模型。该模型将应用程序提供的服务划分成三个抽象层,以便由此得到的应用程序具有一定的灵活性和可扩展性。任何一层都可以进行更改,且不会对其他两层产生负面影响,这样将能不断改进应用程序以满足用户需求和技术方面的新变化。

这三层是:

  • 表示服务:应用程序的表示服务用于呈现向用户显示的数据和接受用户的输入。
  • 业务服务:有时又称为“应用程序服务”,应用程序的业务服务强制执行业务规则。在典型的电子商务应用程序中,这可能包括:确保用户在下订单之前必须经过身份验证,根据用户的配置文件检索相应的内容,校验在处理订单中涉及的所有步骤都是以正确的顺序执行的。
  • 数据服务:应用程序中的数据服务包括存储、检索和修改数据所需的逻辑,以及应用程序必须强制执行的数据完整性规则。在电子商务应用程序中,这可能包括目录、用户和订单数据的处理。

注意:有关 MSF 的详细信息,请访问站点 http://www.microsoft.com/msf(英文)。

为何使用 MSF 应用程序模型?

除了以上所述的灵活性和可扩展性优点之外,MSF 三层应用程序模型还在减少开发、部署和管理应用程序时间方面具有明显的优势。在应用程序体系结构上采用 MSF 三层方式的主要优点体现在:

  • 分离:由于服务是相互分离的,因此,应用程序的每一层都可独立于其他两层进行开发、维护和增强。这样,三个不同的开发小组可以就同一应用程序项目开展工作。
  • 分布:由于逻辑层是独立的,因此,可将它们以分布式方式部署在多个服务器上。
  • 重新使用:不同的客户端设备可以使用每一层提供的服务。例如,电子商务站点的业务服务可被一组表示服务使用,以便给网站提供 HTML 接口;也可被另一组表示服务用于支持 WAP 的手机。某些业务服务还可被各种行业 (LOB) 应用程序或贸易合作伙伴提供的补充应用程序配置为 Web 服务。

除遵循三层设计模型之外,ConsolidatedRetail.com 的设计人员和开发人员还遵循了 MSF 设计过程。下面讲述此过程。

MSF 应用程序的设计过程

无论构建何种类型的有效应用程序,第一步都是确保在设计上是合理的。软件设计的方法有多种,每一种都有自己的优点和缺点。决定要使用的设计过程时,应确保该过程提供了明确的阶段,这些阶段是按实际软件的实现步骤来划分的;确保该过程能根据各个阶段的需求变化进行调整。

MSF 应用程序设计模型不仅为分布式应用程序定义了基于服务的体系结构,还定义了一种循环的设计过程,在每一轮循环中,软件是按“概念”阶段、“逻辑”阶段、“物理”阶段这样的顺序设计的。

  • 概念阶段:在概念阶段,将从用户和/或企业的角度看待面临的挑战。概念阶段的主要目标在于定义挑战和解决方案的概念。

    “功能说明书”文档是概念阶段的最终成果。

  • 逻辑阶段:在逻辑阶段,将从项目开发小组的角度看待设计目标和挑战。逻辑阶段的主要目标在于将概念设计映射为逻辑组件。

    小组使用概念阶段中标识的用户方案(或用户案例)来构建面向对象的软件解决方案中各组件的逻辑模型。面向对象的解决方案将业务功能装入实际对象的软件表示法中,这由面向对象的设计中的“类”定义。

  • 物理阶段:在物理阶段,将从开发人员的角度看待目标和挑战。物理阶段的主要目标在于将逻辑设计应用到实际需求和约束。

    由于物理阶段是从开发人员的角度考虑设计问题,而且任务就是定义物理实现方案,因此物理设计阶段的成果是技术规范文档。

总结

本章包含电子商务需求的一般性概述,并概括了 ConsolidatedRetail.com 应用程序的特定需求。本章还对 MSF 应用程序模型和设计过程进行了概要说明。下一章将更为详细地讨论“概念”、“逻辑”和“物理”设计阶段以及各个阶段的最终成果。

第 2 章:概念设计阶段

摘要:本章将说明 Microsoft 解决方案框架 (MSF) 设计过程的概念阶段。在这一阶段,设计小组将从潜在用户和业务的角度来确定主要的解决方案概念和目标。该阶段结束时,设计小组将编写功能说明文档,此文档是以后所有应用程序设计工作的基础。

简介

在概念设计阶段中,设计小组将根据以前所确定的业务和用户需求来确定一个完整的项目前景并将其记录下来。基于这些需求,该小组将准备“应用方案”(源自“用例”),然后创建一个功能说明文档,以便从电子商务应用程序的用户及供应方的角度来详细解释该应用程序的工作原理。该功能说明文档是在概念阶段结束时所提交的文档。

应用方案和用例

应用方案通过详细分析“用例”来创建。用例只是一个说明文本,用于描述外部“操作者”(操作者可以是用户或现有系统)与所设计的应用程序(或组件)之间的交互。除了用户之外,操作者还可以是服务、组件等。

创建用例时主要应确定两方面信息:一是操作者的操作,二是预期的结果。用例通常是在白板(也称集体讨论)讨论中草拟出来的,这种讨论将简要地概括用例,并为其提供描述性名称。然后将制定出用例的详细内容,并对其进行某种线性排序。

本章的其余部分将介绍为 ConsolidatedRetail.com 应用程序开发的各种应用方案,并提供各种方案所述内容的操作用例图。

应用方案参考体系结构

在概念设计阶段中,参考体系结构应用的设计人员会确定如下应用方案:

应用方案 1:客户登录到站点

客户导航至登录页并输入其用户名和密码。系统将验证用户名和密码。如果用户输入了有效的用户名和密码,则会显示出一个页面,它所包含的链接将使用户能够更新其用户配置文件信息并查看其订单历史记录(如“应用方案 6”所述)。

如果客户将用户名字段留为空,则提示该用户必须填写用户名字段。

如果客户将密码字段留为空,则提示该用户必须填写密码字段。

如果客户输入的用户名无效,将重新显示登录页,并出现错误消息,指出该用户名无效。

如果客户输入的用户名有效而密码无效,将重新显示登录页,并出现错误消息,指出该密码无效。

如果客户表示已忘记密码,则将向该客户提供一个选项,让其输入用户名,然后在电子邮件消息中接收密码。

图 2-1 是一个用例图,用以说明登录功能。

图 2-1:登录功能

应用方案 2:客户搜索产品

客户可以通过输入产品的标题、文本或说明来搜索产品,系统将显示所有匹配产品,这些产品的说明中都包含用户在搜索框中输入的索引关键字。

客户可以导航至搜索框,通过输入产品名的一部分来搜索产品。系统将显示与搜索标准最匹配的产品。

图 2-2 是一个用例图形,用以显示客户所执行的搜索。

图 2-2: 客户执行搜索

如果客户输入的产品名没有匹配的产品,或者客户输入的产品名有误,搜索结果将显示一则消息,指出没有产品与搜索标准匹配。

如果客户输入的关键字与产品说明不匹配,系统将显示一条消息,指出没有产品与搜索标准匹配。

应用方案 3:客户浏览要购买的产品

在这种情况下,客户选择要浏览的目录。显示所选目录的根中的各类别和各产品。然后,用户可以选择要查看细节的产品,或选择一个类别,查看所选类别中的产品集和子类别集。

图 2-3 是一个用例图形,用以说明浏览方案。

图 2-3:客户浏览

应用方案 4:客户管理购物篮

当客户显示购物篮时,购物篮可能是空的,也可能包含在本次会话或上次访问中未购买或已删除的产品。

如果购物篮为空,应用程序将显示一条消息,向该客户提示购物篮是空的。

如果购物篮包含一种或多种产品,应用程序将显示一个列表,其中包括每种产品的产品名、价格、数量和总计,以及购物篮中所有产品的总计。

如果客户选择删除某种产品,应用程序将从购物篮中删除该产品。

如果客户选择删除购物篮中的所有产品,应用程序将删除购物篮中的所有产品。

如果客户更改了某一产品的数量,应用程序将对购物篮进行更新,以反映该产品的数量。

如果客户更新了购物篮,应用程序将重新计算购物篮中每种产品的产品数量和总计,并重新计算购物篮中所有产品的总计。

图 2-4 是一个用例图形,用以说明购物篮管理方案:

图 2-4:客户管理购物篮

应用方案 5:客户结帐

客户表示愿意结帐,并愿意购买购物篮中产品。

如果客户当前未登录,将要求该客户按照“应用方案 1”所述输入有效的用户名和密码。然后,应用程序将显示与当前用户相关联的送货地址列表。客户可以指定将订单发往其中一个地址,也可以编辑地址或添加新地址。客户还可以指定将购物篮中的不同产品发运到不同的地址。

如果客户指定了单个送货地址,应用程序将提示该客户从可用的送货方式列表中选择一种送货方式。

如果客户指定使用多个地址,应用程序将提示客户为购物篮中的每一产品选择地址和送货方式。

如果客户选择添加或编辑地址,应用程序会将客户重定向到“应用方案 6”中所述的送货信息页之一。

当客户提供了送货地址和送货方式信息后,应用程序将提示该客户确认付款地址并提供信用卡类型、帐户持有人姓名、帐号、到期月和到期年。

如果客户未指定付款地址,应用程序将显示一个页面,让客户按照“应用方案 6”所述指定付款地址。

图 2-5 是一个用例图形,用以说明结帐功能。

图 2-5:客户结帐

下一步,应用程序将显示订单、送货和付款的概要信息页。客户可以确认该订单或修改其中的信息。

如果客户选择修改订单,则将回到“应用方案 4”中所述的购物篮页。

如果客户确认该订单,则将显示感谢消息和唯一的订单号。

最后,应用程序将向用户发送订单确认电子邮件,以确认此次订购。

应用方案 6:客户提供帐户和送货信息

如果客户正在以匿名方式浏览该站点,则必须按照“应用方案 1”所述输入用户名和密码。

“我的帐户”页包含指向多个页面的链接,以便于客户更改帐户信息,更改送货地址信息,更改付费地址信息,更改密码并查看订单历史记录。

如果客户选择更改其帐户信息,应用程序将显示一个页面,让该客户编辑其帐户的用户姓名、电子邮件地址、电话号码和传真号码。

如果客户选择编辑地址信息,应用程序将显示一个页面,列出与该帐户相关联的所有地址。客户可以添加新的地址,也可以编辑或删除现有地址。

如果客户选择添加新的地址,应用程序将显示一个页面,提示用户提供此地址项的名称、收件人姓名、街区地址信息(两行)、城市、省份、邮政编码以及电话号码。

如果客户将“列为”、“收件人姓名”、“地址行 1”、“城市”、“省份”、“邮政编码”字段留为空,应用程序将提示客户填写缺失的数据。如果客户选择编辑地址,应用程序将显示地址字段,该客户即可对其进行修改。如果客户选择删除地址,应用程序将删除帐户的地址记录。

如果客户选择更改其付款地址,应用程序将显示一个页面,提示用户提供此特定地址项的名称、收件人姓名、街区地址信息(两行)、城市、省份、邮政编码以及电话号码。

如果“收件人姓名”、“地址行 1”、“城市”、“省份”、“邮政编码”或“列为”字段留为空,应用程序将提示客户填写缺失的数据。

如果客户选择查看其订单历史记录,应用程序将显示一个页面,其中包含该客户以前订单的详细信息。

如果客户选择更改其密码,应用程序将显示一个页面,提示该客户输入旧密码和新密码,并确认新密码。当用户提交这些数据后,应用程序将检查旧密码是否与帐户中的密码相匹配,新密码是否与确认信息中的密码相匹配。如果密码全部匹配,系统将更改与该客户相关联的密码。如果不匹配,应用程序将显示错误消息,提示客户重新更改密码。

图 2-6 是一个用例图形,用以说明此方案:

图 2-6:客户提供帐户和送货信息

总结

本章介绍了为电子商务应用程序确定应用方案并制定概念设计的过程。图 2-7 是一个用例图形,用以显示已定义的累积应用方案:

图 2-7:累积应用设计图

应用方案及其基础用例都应该说明该应用程序在多种业务过程中的预期行为。应用方案还应从概念上说明应用程序的功能,并为应用程序中组件的逻辑设计提供基础。另外,因为设计过程具有迭代性,所以如果稍后在开发过程中发现了先前未发现的交互,则可以在设计中包括新的用例。

第 3 章:逻辑设计阶段

Microsoft Corporation
2001年5月

摘要:本章描述 Microsoft 解决方案框架 (MSF) 设计流程的逻辑阶段。在此阶段,设计小组要根据概念设计在组件应用和集成方面作出高级的决定。设计小组将使用应用方案(在概念阶段开发)来构建应用程序的逻辑模型。

在此阶段结束时,设计小组应已开发出逻辑设计,此设计是工程实际设计阶段的基础。

简介

逻辑阶段的目标是将概念阶段定义的功能转化为一个抽象模型,以确定将用于支持解决方案的协作逻辑组件。

所生成的逻辑设计并不涉及具体的技术。相反,此阶段的目标是在开展任何技术工作之前对功能性进行分析和了解。例如,当一个小组在逻辑阶段设计电子商务解决方案时,可能认为名为 Users 的组件是必需的服务,用以对访问应用程序的用户组进行跟踪。但在实际设计阶段,设计小组可能会选择使用 Microsoft® Commerce Server 2000。这种情况下,Commerce Server 中的 MSCSProfileService 组件提供了 Users 组件的实际实施方案。

如果最终应用程序设计包括定制组件(即未在现成的解决方案或产品中提供的组件),就可以将逻辑阶段确定的相应组件直接转换到实际阶段中。例如,在逻辑阶段定义了 Users 对象,而设计小组决定使其成为定制对象,那么 Users 对象就将在实际阶段中重复。

本章的其余部分将概述在设计 ConsolidatedRetail.com 时使用的逻辑设计流程,然后详细说明满足应用方案要求所需的逻辑组件。

创建逻辑设计

创建逻辑应用设计的第一步是确定将提供所需功能的业务对象(组件)。除了确定所需对象之外,设计小组还必须确定各个对象具有的行为、属性和关系。设计小组将使用在概念阶段创建的应用方案来确定这些对象及其关系、行为和属性。

例如,以下是应用方案 3:

用户选择要浏览的目录。显示所选目录的根中的各类别和各产品。然后,用户可以选择要查看其细节的产品,或选择一个类别,查看所选类别中的各产品和各子类别。

这样,设计小组将通过分析此方案来确定支持解决方案的各个方面,并执行以下任务:

  1. 确定此方案中的业务对象。
  2. 确定这些对象的行为。
  3. 确定这些对象的属性或特性。
  4. 确定这些对象之间的逻辑关系。

这些任务将在本章的后面小节中进行更为详细的描述。

当完成每个应用方案的这些任务并将其归档后,设计小组就可以结束逻辑设计阶段。有关 ConsolidatedRetail.com 应用的完整设计示例,请参考“完成的逻辑设计”一节。

统一建模语言

统一建模语言 (Unified Modeling Language, UML) 是一种用于描述系统运行方式的工具。在直观地描述系统以对其进行更为全面的分析时,UML 是非常有用的工具。通过使用 UML,可以很方便地用图表说明组件、交互操作、关系等等。

UML 通常用于在逻辑阶段简化对设计的分析。

为了说明创建逻辑设计所涉及的任务,以下各节将提供简单 UML 图表示例。

确定对象

当分析应用方案时,首要任务是确定其中的对象。对象通常是在应用方案中出现的业务实体或过程。例如,在以下段落中,对象将以粗体标识:

user(用户)选择要浏览的 catalog(目录)。显示所选 catalog(目录)的根位置中的 categories(各类别)和 products(各产品)。然后,user(用户)可以选择要查看其细节的 product(产品),或选择一个 category(类别),查看所选 category(类别)中的 products(各产品)和子类别。

上例使用了以下对象:

  • User
  • Catalog
  • Categories
  • Product
  • Products

图 3-1 是描述此示例中所确定的对象的 UML 图:

图 3-1:对象

这五个对象成为此方案的基本对象;不过,在有些情况下,要使方案起作用,还需要添加其他对象,即使这些对象未在方案中明确列出。通过检查没有明显的对象与其相关联的行为,即可确定这些附加对象。要确定这些对象,必须首先确定其行为。

确定行为

当确定明显的对象组后,下一步是确定其各自的行为,这些行为也称作“方法”或“服务”。

要确定对象行为,必须首先确定在方案中执行的操作。例如,在以下段落中,动作将以粗体标识:

用户选择要浏览的目录。显示所选目录的根中的各类别和各产品。然后,用户可以选择查看其细节的产品,或选择一个类别,查看所选类别中的各产品和子类别。

第一项动作是用户选择目录。图 3-2 是一个 UML 图,它将 User(用户)对象描述为具有 Select Catalog(选择目录)行为:

图 3-2 User 对象的行为

如先前所述,必须从方案中派生没有明显的对象与其相关联的行为。我们从而可以想到,由于用户选择了目录,所以必然有某种允许从目录列表中选择目录的机制。这样,您可以从逻辑上假定 Catalogs 对象存在,它管理所有的 Catalog 对象。然后,应该将这一新对象添加到已定义对象的列表中。

当定义“Catalogs”对象后,可以将第一项动作定义为 Select Catalog(选择目录),此行为属于“Catalogs”对象。

您可以继续分析方案中的每个句子,直至确定所有的征询对象及其关联行为。

确定属性

当确定行为后,下一步是确定所定义对象的属性(也称作“特性”)。属性是解决方案需要跟踪的元素。它们是用于保留(或“存留”)数据的占位符。

属性派生的方法是,分析方案中的行为并提取需要存留(或跟踪)的元素。例如,在上一节中,应用方案指定用户将能够查看产品。当查看产品时,显示给用户的元素将成为产品的属性。例如,如果业务要求显示产品规格和价格,这些元素就会成为对象的属性。

图 3-3 是一个 UML 图,显示具有 Name 属性的 User 对象:

图 3-3:User 对象的属性

确定关系

确定对象及其属性后,下一步是确定“关系”。关系是对象之间的逻辑关联。

要确定关系,必须分析对象之间的交互方式。例如,“Categories”对象与“Category”对象具有关系,因为“Categories”对象管理“Category”对象的集合并包含“Category”对象。

务必要注意,另外有一种名为 inheritance(继承)的关系,它专门处理一个对象由另一个对象定义的情况。例如,如果所设计的解决方案要销售食品和书籍,但又需要在逻辑上将这两者区别开,则可以定义这样一种关系:“Book”和“Food”对象都属于一种“Product”对象。这样,它们都可以继承“Product”对象。

在商务参考体系结构解决方案中,未在逻辑阶段定义任何继承关系。但在有些电子商务解决方案中,这些关系可能非常重要。

完成的逻辑设计

在参考体系结构应用 ConsolidatedRetail.com 的逻辑设计阶段,设计小组确定了以下对象,它们组成了支持解决方案所需组件的抽象集合。(这些对象按字母顺序排列,而不是按使用顺序排列。)

Authentication

Authentication 对象处理用户的注册和身份验证。

Catalog

Catalog 对象存留特定目录的信息并管理该目录内产品的集合。

Catalog Manager

Catalog Manager 对象管理目录的集合。

Category

Category 对象存留有关特定类别的信息。

Category Manager

Category Manager 对象管理类别的集合。

Configuration

Configuration 对象存留应用配置信息并处理与配置相关的任务。

Data Functions

Data Functions 对象执行特定于数据的功能,如打开与数据库的连接。

E-mail

E-mail 对象用于向用户发送电子邮件消息,如订单确认消息。

Error Handler

出错时将调用“Error Handler”对象。它处理用户友好的错误转换和日志错误。

Order

Order 对象存留有关特定订单的信息。

Product

Product 对象存留有关特定产品的信息。

Search

Search 对象用于搜索目录并返回产品搜索的结果。

User

User 对象用于存留特定用户的有关信息。此外,它还管理所有的用户订单。

User Manager

User Manager 对象管理用户的集合。

图 3-4 演示了主要对象之间以及主要对象与第二章所述的用例之间的关系

图 3-4:对象关系

总结

本章说明了确定组成电子商务应用的对象(即业务组件)及其属性、行为和相互关系的四步骤流程。此流程的最终结果是创建出将用作技术设计和规范基础的逻辑设计。

要注意,所生成的逻辑设计并不涉及具体的技术。这些技术将在实际设计阶段确定,这是下一章讨论的主题。

第 4 章:物理设计阶段

摘要:本章讨论了和商务参考体系结构应用程序 ConsolidatedRetail.com 有关的 Microsoft 解决方案框架 (MSF) 物理设计阶段。在逻辑设计阶段,项目开发小组将实际的物理设计约束应用到在逻辑设计阶段创建的逻辑设计。这项活动的目标是标识一组组件,然后确定哪些组件已经存在以及哪些组件必须创建。在该阶段的末尾,将结果记录在明确定义的技术规范中,该规范将成为构建应用程序的蓝图。

简介

物理设计阶段是将实际的物理设计约束应用到逻辑设计的过渡阶段。标识了逻辑组件之后,下一个任务就是分析哪些组件已经存在,哪些组件可以重复使用或进行修改,而哪些组件必须创建。

正如前文所述,物理设计过程是从开发人员的角度考虑的。该阶段的成果是一个完整实现方案的设计或蓝图,同时编写出了技术规范文档,开发小组将使用该文档来构建应用程序。

物理阶段可以分为三个更小的任务阶段,如下所示:

  • 研究:在此阶段,开发小组将确定物理基础结构约束和解决方案需求,并处理这两者之间的冲突。此外,开发小组还将确定预期的实现技术。
  • 分析/合理化:开发小组将选择要使用的实现技术,并确定如何满足定义的业务需求。
  • 实现:开发小组将选择编程模型、指定组件接口并选择开发语言。

本章的其余部分将详细讨论这三个任务,并在适当的地方给出示例。

研究工作

物理阶段涉及的第一个任务是研究和收集有关以下主题的信息:

  • 物理解决方案需求
  • 物理约束
  • 可供选择的现有技术

客户需求暗含在需求文档中,在逻辑阶段进一步加以定义;不过开发小组可能需要研究并确定实际的约束和现有的技术。

确定物理解决方案需求

物理解决方案需求是专用于指导基础结构设计的需求。在第 1 章中,我们定义了以下系统需求:

  • 全球化
  • 性能/可靠性
  • 可扩展性
  • 可用性
  • 可管理性
  • 安全性
  • 可访问性

以下章节将对每个主题进行详细说明。

全球化(国际化)

全球化(或国际化)是开发程序核心内容需要经过的一个过程,在这个过程中,不再是基于单种语言或区域进行功能设计和代码设计,同时,编写的源代码更便于创建程序的不同语言版本。

全球化使您能将应用程序移植到不同的文化环境。在早期的编程中,这仅仅意味着支持多种语言(例如,支持 Unicode),但是现在进行全球化时,还要考虑选择什么样的界面,例如确定颜色、导航布局以及页结构等。

进行全球化时,需要仔细审查应用程序或网页中涉及的一些众所周知的地理和文化问题。全球化的步骤包括:研究语言和文化问题,请语言专家校验一些众所周知的问题,如果可能的话,请特定销售区域的公司代表校验一些众所周知的问题。

要支持这些文化差异,可以定义以下物理需求:

  • 在数据库中使用 nVarChar 而不使用 VarChar
  • 提供自定义界面的能力

性能

性能一般用“系统总吞吐量”和“响应时间”来衡量。

系统总吞吐量

系统总吞吐量使用“每秒事务数 (TPS)”来衡量,反映了系统在执行服务请求的特定集合(称为事务)方面的能力。对于电子商务应用程序,事务可能由以下依次执行直到结束的事件组成:

  • 用户来到站点。
  • 用户浏览目录,找到想要的产品。
  • 用户将产品添加到购物车。
  • 用户注册。
  • 用户结帐。

TPS 是系统每秒可以处理这些事务的最大数量。正如在业务需求中所述的,当使用以下开发配置时,将商务参考体系结构应用程序设计为每小时至少处理 4800 个这样的事务:

  • [(4) PIII 500mhz,1GB RAM,服务器运行 IIS 和 Commerce Server]
  • [(1) PIII 500mhz,1GB RAM,服务器运行 SQL Server]

响应时间

响应时间是用户请求和系统响应之间间隔的时间量,是用户最关心的性能指标。响应时间通常用一个百分比和响应秒数来表示。例如,“所有请求中的 90% 应在 5 秒之内响应”意味着在用户认为应用程序的运行出现问题之前,其所发请求中的 90% 必须在 5 秒之内得到服务器的响应。

商务参考体系结构应用程序要求在 5 秒内响应全部请求中的 95%。

可扩展性

可扩展性是指添加资源时站点容量增加的能力。从用户角度来看,这意味着当大量用户同时访问站点时,站点仍能提供可接受的响应时间。

我们在以前的章节中已经提过,提高可扩展性有两个方法:“向上扩展”和“向外扩展”。

向上扩展

“向上扩展”就是通过采用更好和/或更快的 CPU、更大的 RAM、更快的磁盘等等来增强服务器的处理能力。这种方法非常有效,尤其是在数据层上,该层上的一些大型数据库需要相对较强的处理能力。不过,由于硬件成本随处理能力的加强而按指数增长,因此,服务器越接近顶端,这种方法就愈加不合算。

向外扩展

“向外扩展”则是指利用群集(也称为“Web 领域”)中的多个服务器来分担处理工作量。Web 领域在硬件方面的花费更为合算,而且提供了更为灵活、可扩展的解决方案。当站点上的负载增加时,可以很轻松地将服务器添加到 Web 领域中。

要启用向外扩展,您必须避免使用服务器特定的会话内存(例如 ASP 中的 Session 对象)保留信息。其原因如下:

  • 用户会话将依附于特定服务器(会话相关性),这会破坏动态地将请求分配给服务器的网络负载平衡策略。此外,还会破坏服务器领域的可靠性,因为当原服务器发生故障(并丢失了其内存中的会话状态信息)时,就无法将用户会话转移到其他服务器。
  • 内存资源被前端服务器耗费在存放用户会话状态的细节上,从而减少了可用于处理请求和高速缓存内容的内存。如果一个受欢迎的站点能够在短时间内吸引大量的用户,则状态维护方面的内存需求可能非常大。为了部分解决内存需求问题,Commerce Server 大量使用了高速缓存。对配置文件架构、折扣和商业活动都将进行高速缓存。

可用性

可用性是指在任意时候客户机能及时连接和使用资源的能力。

理解高可用性的一种方法是将其与“容错”相对比。这些术语描述了测量可用性的两个不同的基准。“容错”被定义为在 100% 时间内的 100% 可用性(无论处于何种环境)。容错系统的设计目的是“确保”资源的可用性。

高可用性的资源对于客户机来说几乎总是处于运作状态并且是可访问的。因此,它不能出现单点故障。服务器群集和网络负载平衡是使系统资源保持可用的两种方法。

部署站点之前,应将以下方法组合使用,防止服务器出现故障:

  • 地理位置分散的数据中心。
  • 不间断的双重电源。
  • 数据备份。
  • 群集形式的服务器,多个计算机所起的作用相当于单个服务器的作用。
  • 数据复制。
  • 网络负载平衡,即由多个相同的服务器分担负载以确保可用性、可扩展性和完全一致的用户体验。

可管理性

可管理性是指执行站点管理任务的能力。对于电子商务应用程序来说,它包括对产品目录、特价促销、装运费用、税率、用户帐户进行配置,以及为站点使用情况、趋势提供报告机制等等。

如果具有全面的管理基础结构,业务经理就能对站点进行配置,以根据市场趋势和竞争对手的活动采取相应的对策。

安全性

如果能确保最基本的窗体的安全性,也就确保了数据或设备受到保护,防止未经授权的人访问或使用它们。在电子商务应用程序的环境中,应该保护以下信息:

  • 敏感的用户信息
  • 信用卡号
  • 未公开的产品数据

应用程序安全性的设计主要包括三个方面的内容:“身份验证”、“授权”和“加密”。

身份验证

有两种主要的方法可用于分布式解决方案(例如电子商务站点)中的用户身份验证。一般是用“假冒/委托”模型和“受托服务器”模型来描述。

这两种模型都假定使用了 n 层应用程序。在本示例中,用户连接到中间层(具体是指 Web 领域),Web 领域依次访问后端层(具体是指 SQL Server 数据库)的数据或服务。这两种方法的不同之处在于用来访问后端数据的安全帐户不同。

  • “假冒/委托”模型

    在“假冒/委托”模型中,用户向中间层应用程序提供安全凭据,然后,中间层应用程序使用用户的安全凭据访问后端数据库。中间层应用程序实质上在“假冒”用户,代表用户检索数据。

    图 4-1 说明了“假冒/委托”模型:

图 4-1:“假冒/委托”模型

  • “受托服务器”模型

    在“受托服务器”模型中,中间层应用程序对用户进行身份验证,通常是校验用户名和口令的组合。中间层应用程序认为用户的身份正确无误后,它使用“自己的安全帐户”访问后端数据库。除了通过中间层应用程序之外,用户无权访问后端数据。在这种方法中,实际上有两种身份验证操作。首先,Web 应用程序对用户进行身份验证,然后数据库服务器对 Web 应用程序进行身份验证。

    图 4-2 说明了“受托服务器”模型:

图 4-2:“受托服务器”模型

授权

授权是指对特定用户或服务授予访问资源的权限。用户通过了身份验证后,应能从应用程序请求特定的功能。可以向用户分配权限或“授权”,以便执行某些任务而不能执行其他任务。在安全环境中,将访问级别限制为授权的用户是非常重要的。

安全专家经常谈论“最小权限原理”。这是一个经验法则,规定用户应该具有足够的权限来执行所需执行的任务,“但不应该具有更多的权限”。

加密

加密是确保安全的另一种方法,通过对数据进行编码以防止未经授权的访问。

根据加密的位置不同,加密可以在许多级别上进行。通常,加密可以在服务器上、传输时或客户机上进行。

  • 服务器加密

    服务器上的加密是指对在服务器基础结构中存储和传输的数据进行加密的过程。对服务器基础结构中的数据进行加密后,就能确保在出现违反安全性的事件时,访问到的敏感数据由于被加密而毫无使用价值。

    用户的信用卡数据就是应对数据加密的一个示例。当业务层在数据层中存储用户的信用卡信息时,对该数据进行加密是非常重要的。如果某个黑客侵入了系统,并获得了对保存加密的信用卡信息的表的访问权,那么该信息对于黑客没有任何用处。如果信用卡信息未加密,则加重了应用程序对数据安全所负的责任。

  • 传输加密

    传输加密专门用于处理在服务器和客户机之间传送的数据。例如,用户向服务器提交 HTML 窗体时,用户输入到窗体中的数据使用超文本传输协议 (Hypertext Transfer Protocol,HTTP) 通过连接(例如 Internet)进行传输,然后由服务器接收。

    在传输过程中,数据可能会被偷窃和篡改,这可以通过在传输时对数据加密来解决。在 Internet 上传输的数据可以通过以下方式加密:在 Web 服务器上安装安全证书,为站点配置安全套接字层 (Secure Sockets Layer,SSL) 端口,使用 HTTP 的加密形式 HTTPS 作为传输协议。

    服务器证书可以从 http://www.microsoft.com/security/(英文)所列的认证机构之一处购买。您可以使用 Microsoft Certificate Services 发布独立的证书,这将允许您在单个服务器上测试 SSL 安全性。请使用 Web Server Certificate Wizard 安装证书,该向导可以通过 Internet Services Manager 中的站点属性来访问。

    构建实现 SSL 的站点时,应该意识到:将用户从未加密会话定向到加密会话的超级链接或重定向操作必须包含 https:// 前缀。这指定了用户的浏览器将使用 HTTPS 与服务器进行通信。

  • 客户机加密

    客户机加密专门用于处理驻留在客户机上的数据。例如,如果某个文件是公用的,但它被加密了,那么只有具有正确的解密密钥的用户可以使用该文件。

    对于一般的电子商务应用程序,客户机加密不如传输和服务器加密重要,但是某些情况下可能要求使用这种加密方式。

可访问性

可访问性是指从多种设备或浏览器访问站点的能力。Internet 正在以难以置信的速度向前发展,而访问 Internet 的设备也变得五花八门。因此,使电子商务应用程序可被多种设备访问并且在这些设备上正常运行是一个非常艰巨的任务。

支持多个客户机的关键是将表示形式从内容中分离出来。许多方法可以做到这一点,其中包括编写 ASP 页中的逻辑来根据客户机生成不同的响应,或将不同设备重定向到替代站点。不过,将表示逻辑从内容中分离出来的最佳方法之一就是使用 XML。如果数据可以用 XML 来表示,那么可以使用 XSL 样式表为特定类型的客户机呈现数据。通过应用不同的样式表,可以为不同的客户机表示同一内容。图 4-3 说明了这个概念。

图 4-3:将表示形式从内容中分离出来

确定可供选择的现有技术

可供选择的技术是指解决方案中可以使用的技术、产品或服务。利用现有的技术来实现功能在经济上经常是很合算的,这样就不必另外构建这些功能了。例如,构建 Web 应用程序时,您需要一个操作系统作为解决方案的基础,但是没有必要自己构建一个操作系统。

既然在每次构建应用程序时没有必要自己构建一个操作系统,同样也就没有必要自己构建 Web 解决方案本身的所有部分。许多专家都认为:未来的整个应用程序将使用现有的服务来构建,这些服务只需重新加以组合就可构成一个应用程序。

因此,收集有关在解决方案中可以使用哪些技术的信息很重要。在物理设计阶段涉及的下一个任务中,设计小组将分析该信息,并确定哪些技术(如果有的话)可以满足正在讨论的特定应用程序的需求。

操作系统

任何现代的应用程序都是在操作系统之上构建的。操作系统不仅提供了与硬件通信的接口,还提供了构建应用程序的公共框架。选择一个支持面向对象的方法和公共框架(应用程序可以在该框架上运行和通信)的操作系统是很重要的。

Windows 2000 Server 平台

Microsoft® Windows 2000 Server 为应用程序开发人员提供了一套内容丰富的功能。Internet 上许多主要的电子商务站点都是在 Windows 2000 上运行的,其中包括 Buy.com、BarnesAndNoble.com、Dell.comIntel.com

Internet 服务

基于 Web 的应用程序的另一个核心部分是 Internet 服务。基于 Web 的应用程序需要一个 Internet 服务平台,该平台负责基本的 Web 服务,例如响应客户机的 HTTP 请求、HTTPS 请求和其他请求。一个好的 Internet 服务平台还应该提供站点管理能力和动态内容编程模型。

Microsoft Internet Information Services

Microsoft® Internet Information Services (IIS) 是内置于 Windows 2000 中的 Web 服务器。IIS 提供了一个内容丰富的 Internet 服务软件包,包括 ASP、DAV、Web Folders、FrontPage Extensions、FTP、多站点宿主以及其他支持。

表示服务

正如以前在“可访问性”一节中提到的,使内容和表示形式分离是非常重要的,只有这样才能从多个客户机访问内容。进行这种分离对于简化全球化工作也是非常重要的。

正如以前所述,将表示逻辑从内容中分离出来的一种方法是同时使用 XML 和 XSL。除了同时使用 XML 和 XSL 之外,还可以根据诸如 Browscap.ini 文件、USER_AGENT 字符串等等之类的实体,在 ASP 页自身中构建复杂的页逻辑。

Microsoft XSLISAPI 过滤器

可以实现表示服务功能的一个备选技术是 Microsoft® XSLISAPI 过滤器。ISAPI 代表 Internet 服务应用程序编程接口,是 IIS 的基础。筛选器放置在 ISAPI 之上,并在 Web 服务器接收客户机或服务请求时提供相应的功能。例如,ASP 引擎 (ASP.dll) 是在 ISAPI 之上运行的扩展。

XSLISAPI 过滤器用于截获所有对具有 XML 或 PASP 文件名扩展的文档的请求。PASP 文件名扩展专用于与 XSLISAPI 过滤器一起使用的应用程序。具有该扩展的文件被认为是标准的 ASP 文件(具有某些限制),这些文件可以生成有效的 XML(而不是 HTML)输出。然后,根据将 ISAPI 过滤器应用于直接请求的 XML 文件的相同规则,转换 PASP 脚本的输出内容。

检索了 XML 后,表示过程的下一步是将数据转换为某些类型的有效标记(客户机可以显示这些标记)。对于浏览器,这可能是 HTML;对于支持 WAP 的手机,这可能是 WML。可以从以下 Web 位置获取 XSLISAPI 文档:http://msdn.microsoft.com/code/default.asp?URL=/code/sample.asp?url=/MSDN-FILES/026/002/187/msdncompositedoc.xml(英文)。

图 4-4 说明了 XSLISAPI 过滤器的概念:

图 4-4:XSLISAPI 过滤器功能

数据服务

对于电子商务解决方案的构建而言,具备存储、检索和管理数据的功能也很重要。这些服务被封装到一个数据库服务器中。对于企业数据库服务器来说,性能高、并发性好以及具备可扩展性是非常重要的。

SQL Server 2000

Microsoft® SQL Server 2000 是一种 SQL 数据库服务器,它提供了企业级性能、可扩展性和良好的并发性。它还提供了对 XML 的丰富支持、严密的安全性以及功能强大的分析工具。

商务平台

如果从头开始构建一个企业电子商务解决方案,将会耗费大量的时间和人力物力资源。如果充分利用现有产品中的电子商务功能,则会显著缩短整个程序的开发时间,节约大量开发费用。

Microsoft® Commerce Server 2000 就是充分利用现有功能开发的。

Commerce Server 2000

Commerce Server 2000 是一个综合性产品,它将电子商务解决方案的许多功能封装到一个软件包中。Commerce Server 2000 提供了高级的管理功能、较强的可扩展性和良好的性能。有关详细信息,请访问 Microsoft Commerce Server 网站,其 URL 是:http://www.microsoft.com/commerceserver(英文)。

分析/合理化

物理设计阶段的研究工作完成之后,紧接着要进行分析和合理化方面的工作。分析和合理化是指对研究过程中收集的信息进行分析,并基于这些信息作出决策。

使用现有的技术

当设计小组考虑使用现有技术时,必须权衡所有可能影响解决方案的因素。以下列出了可能的因素,但不一定全面:

  • 能力:该技术是否能实现业务功能?
  • 所有权成本:该技术在经济上是否合算?需要考虑产品、开发、升级、许可证、部署和运营方面的费用。
  • 经验:该技术要求开发人员具有哪些经验和专业技能?是否会有培训费用?是否有未知的费用?
  • 成熟和创新:该产品是否是成熟的?它是否已被市场接受?该产品是否有创新性,是否使用了最新的技术?它是否仍是流行的?
  • 部署:该技术是否难以实现?
  • 可支持性:该技术是否可以获得支持?
  • 体系结构:该技术是否实现了一个可接受的体系结构?该体系结构是否满足企业的需求?
  • 可扩展性:该技术是否可以扩展以满足发展要求?
  • 互操作性:该技术是否可以和组织中现有的系统协同工作?
  • 性能:该技术是否可以提供所需的性能?
  • 可靠性:该技术是否可以满足应用程序的可靠性需求?
  • 可用性:该技术是否可以处理应用程序需求,而不会导致解决方案失败?
  • 可管理性:该技术是否易于管理?
  • 安全性:该技术是否符合安全性需求?
  • 标准兼容性:该技术是否与公认的标准兼容?

其他因素,例如项目时间表和预算约束以及可能牵涉到的其他内部项目,都应该考虑在内。

Windows 2000 Server

Windows 2000 Server 是商务参考体系结构的操作系统,因为它提供了一套专门为企业应用程序设计的功能。其中包括以下功能:

  • 服务器类型的选择:Windows 2000 Server 平台可在多种服务器上运行。在商务参考体系结构解决方案的环境中,应用程序既能向上扩展也能向外扩展是非常重要的。根据应用程序负载需求,组织可以选择在两个或多个运行 Windows 2000 Server 或 Windows 2000 Advanced Server 的服务器上部署解决方案。要获取最佳性能并允许将负载分布在多个服务器上,请使用 Microsoft 2000 Datacenter Server 网络和网络负载平衡 (NLB)。
  • 可扩展性:通过使用 Microsoft Windows 2000 群集服务和网络负载平衡,Windows 2000 Advanced Server 和 Datacenter Server 可以进行向外扩展。

通过使两个服务器运行同一应用程序、共享公共的存储机制,群集服务确保了连续的服务。如果这些服务器之一出现故障,另外一个可以接管。由于构建了系统基础结构中的冗余,应用程序就可以使停机时间降为零。

图 4-5 说明了群集的概念。

图 4-5:群集形式的服务器

Windows 2000 Advanced Server 和 Datacenter Server 还可以通过 NLB 进行向外扩展,在这种方法中,将多个服务器作为具有单个 IP 地址的单个单元显示,应用程序负载均匀分布在这些服务器上。当 NLB 设置中的一个服务器出现故障时,NLB 自动检测出现故障的系统,将其负载转移到其他系统,然后重新启动计算机。

图 4-6 说明了 NLB 的概念。

图 4-6:网络负载平衡

  • 可用性:通过使用 Windows 2000 Advanced Server 或 Windows 2000 Datacenter Server 中的群集服务和 NLB 服务,Windows 2000 Server 提供了具有高可用性的解决方案。将 NLB 服务和群集服务一起使用可以消除单点故障。
  • 可靠性:Windows 2000 Server 平台实现了“5 个 9”的可靠性,即确保了正常运行时间高达 99.999%,这相当于每年的停机时间小于 5 分钟。在企业电子商务环境中,停机意味着损失几百万美元的直接收入,还会给客户带来烦恼、招致他们的抱怨。因此,可靠的操作系统是企业解决方案不可分割的一部分。
  • 性能:Windows 2000 Advanced Server 和 Datacenter Server 实现了对称多处理 (Symmetric Multiprocessing,SMP) 支持,它使服务器有效用于 Advanced Server 的处理器高达 8 个,用于 Datacenter Server 的处理器高达 32 个。此外,Advanced Server 还包含增强的内存功能,使服务器可具有高达 8GB 的内存;对于 Datacenter Server,允许服务器的内存达到 64GB。
  • 可管理性:Windows 2000 Server 提供了一套种类繁多的工具,允许您管理网站并连接 Microsoft Management Console (MMC),以便在一个集中的位置管理服务器功能。Windows 2000 中的某些管理功能包括事件记录、性能监视、终端服务以及 Windows 管理规范 (Windows Management Instrumentation,WMI)。
  • 安全性:Windows 2000 提供了一个安全的环境,通过 Active Directory、安全性/身份验证协议以及通信加密,严格控制对文件或服务的访问。
  • 组件服务:Windows 2000 COM+ 服务为开发人员和管理员提供了应用程序功能。这种内置的功能允许开发分布式事务应用程序,而不必开发支持最小单位的事务或异步操作的底层基础结构。您可以在以下位置查找有关 Windows 2000 Server 的详细信息:http://www.microsoft.com/Windows2000(英文)。

Microsoft Internet Information Services (IIS)

Microsoft IIS 随 Microsoft Windows 2000 一起提供,它提供了使用 Internet 提交内容的丰富平台。由于 IIS 完全是在操作系统级别上集成,因此它允许开发和部署直接写入计算基础结构中的解决方案。

IIS 为商务参考体系结构解决方案提供了 Internet 服务,因为它随操作系统一起提供并具有内容丰富的功能集。两个关键的 IIS 功能是 Active Server Pages (ASP) 和 Internet 服务应用程序编程接口 (ISAPI)。

XSLISAPI

XSLISAPI 过滤器为商务参考体系结构解决方案提供了表示服务。使用 XSLISAPI 过滤器后,内容可以和表示形式完全分离,从而允许自定义内容发送而无需修改 ASP 代码。

如果不想同时使用 XML 和 XSL,还可以使用另一方法:在 ASP 页本身中构建复杂的页逻辑。不过,此方法有两个主要的缺点:第一,代码中复杂的显示逻辑可能难以管理;第二,运行这种逻辑需要庞大的开销。

XSLISAPI 截获对具有 XML 或 PASP 文件扩展的文档的所有请求。PASP 文件名扩展专用于与 XSLISAPI 过滤器一起使用的应用程序。具有该扩展的文件被认为是标准的 ASP 文件(具有某些限制),这些文件可以生成有效的 XML(而不是 HTML)输出。

然后,根据发出请求的设备,XSLISAPI 过滤器使用 XSL 样式表转换 PASP 或 XML 页的 XML 输出,将转换后的输出内容发送给客户机。

使用 XSLISAPI 过滤器还有另一个好处,即能按不同的技能组分配相应工作量,从而使开发过程更易于管理。例如,图形设计人员可以按自己的界面规范创建 XSL,而 ASP 开发人员只需要考虑如何将正确的数据传递到界面。

SQL Server 2000

SQL Server 2000 为商务参考体系结构解决方案提供了数据服务。它提供了一个完整的企业数据库解决方案,Commerce Server 2000 将依赖于它的数据服务。对于许多电子商务解决方案,只需要在 Windows 2000 Datacenter Server 上安装一个群集形式的 SQL Server 即可提供所需的可扩展性级别。对于存储数据量极大的站点,可以在多个服务器上创建 SQL Server 数据库,并用“分布式分区视图”来实现跨物理服务器的数据存取与更新。

Commerce Server 2000

开发小组之所以选择 Commerce Server 2000,是因为它是为 Windows 2000 平台构建的,并且为电子商务应用程序提供了一套内容丰富的开发、部署和管理工具。Commerce Server 提供的大约 80% 的对象是在逻辑阶段定义的,开发所需的费用很低。这些对象是作为可用于 ASP 页的 COM 对象实现的。

Commerce Server 2000 提供了以下功能:

  • 与其他服务和软件功能集成:因为 Commerce Server 2000 是专门为 Windows 2000 设计的,因此其体系结构可以和操作系统完全集成。Commerce Server 充分利用了 Windows 2000 Server 中的 COM+ 功能,为企业电子商务应用程序提供了坚实的基础。

    Commerce Server 还可以和 Microsoft® BizTalk™ Server 很好地集成,以便降低某些流程管理功能和外部通信的负担。

  • 管道组件:“管道组件”是一组可配置的自定义 COM 对象,它们被依次调用来执行特定的业务流程。在 Commerce Server 解决方案中,大多数自定义业务类可以作为管道组件实现,以便可以用简单的方法来管理业务流程。(管道组件只不过是实现人们熟知的接口 (IpipelineComponent) 的一些 COM 组件)。这样就允许 Commerce Server 将管道组件标识为适合于 Commerce Server 的组件,然后可以被管道调用。

    在参考体系结构应用程序中,管道组件用于处理诸如“客户订单处理”这样的流程,并确保按顺序执行处理订单所需的任务。

  • 管理基础结构:为电子商务站点设计和构建管理框架不仅要耗费大量资源,而且其工作量一般要大于电子商务应用程序本身的开发工作量。Commerce Server 的管理基础结构是吸引人们最终使用 Commerce Server 的主要因素。

    Commerce Server BizDesk 随 Commerce Server 一起提供。BizDesk 是基于 DHTML 的应用程序,在 IE 5.5 或更高版本上运行,允许管理员远程管理拍卖、促销活动、目录、订单和用户,并提供了一套内容丰富的分析工具。

    因为 BizDesk 是完全基于 DHTML 的,它还可以用于远程管理在 Commerce Server 之上构建的电子商务解决方案。这种方法将 BizDesk 应用程序负载置于客户机上,不会影响电子商务站点的性能。

满足业务需求

确定了可以使用的现有技术之后,开发小组必须确定这些技术如何满足以前提到的业务需求。为了满足这些需求,开发小组必须就工具、过程和方法作出某些关键决策。应该能在必须满足的特定业务需求的环境中查看这些决策。

全球化

正如以前提到的,全球化是指将应用程序移植到不同文化环境中的能力。为了满足该需求,提供给用户的内容和表示形式必须进行“全球化”。在全球化期间,可以将内容分为以下两种类型:

  • 静态内容 — 界面上可以找到的内容以及界面本身。
  • 动态内容 —“动态构建”并显示给用户的内容。

以下各个小节说明了涉及的问题以及对两种内容全球化所作出的关键决策。

静态内容

静态内容包括界面上的文本和界面本身,必须针对指定的文化或语言进行本地化。XSLISAPI 过滤器是这样进行本地化的:允许开发小组对于不同语言使用不同的界面设计,然后将应用程序部署为不同的网站或不同的虚拟目录。通过使用该方法,用户可以选择自己的语言,然后重定向到相应的站点。

注意:参考体系结构应用程序并未进行全球化;不过,该应用程序使用了 XSLISAPI 过滤器,因此具有实现全球化的能力。

动态内容

动态内容由应用程序“动态”生成的内容组成。

如果应用程序将要支持不同语言,它必须支持不同的字符集。Unicode 标准是包含世界上所有语言的字符的字符集。使用 Unicode 标准确保了所有语言可以在动态数据中表示。要支持 Unicode 标准,所有保存字符数据的数据库字段必须使用诸如 nVarChar、nChar 等等的数据类型。同时,业务层必须支持 Unicode 的使用。

因此,商务参考体系结构解决方案在整个应用程序中始终使用 Unicode 标准。

性能

要获得最佳性能,设计小组必须作出多个关键决策。首先,要使系统总吞吐量(即应用程序的总体效率和性能)最大化,为此设计小组应解决以下关键问题:

  • 封送
  • 语言选择
  • 异步处理

使封送最小化

提高系统吞吐量的一种方法是使封送最小化,要做到这一点,最好的方法是减少网站对其他位置上的组件的远程过程调用。许多电子商务站点驻留在专用 Web 服务器的领域中,而业务组件则位于单独的应用程序服务器群集中。虽然这个体系结构可有效确保系统的安全性,尤其是当应用程序服务器通过防火墙或包过滤交换机与 Web 领域分开时更是如此;但是该体系结构将对响应时间造成负面影响,因为对组件的每次调用都必须通过网络连接进行封送。

商务参考体系结构应用程序将组件部署在网站所在的同一服务器上,因此它避免了跨网络的封送,缩短了响应时间。(Commerce Server 提供了参考体系结构应用程序使用的大多数业务组件,这些组件将安装在 Web 服务器上。)

语言选择

语言选择也会影响性能。例如,虽然为 Commerce Server 管道创建的组件可以用脚本语言来编写,但是对于企业应用程序,组件应该使用更低级的语言(例如 Microsoft® Visual Basic® 开发系统或 C++)来构建以获得最佳性能。虽然类似于 ADO 这样的组件在 Visual Basic 组件和 C++ 组件中运行的速度几乎相同,但是复杂的业务例程在 C++ 中运行得更快一些。因此,选择 C++ 作为商务参考体系结构应用程序中所有组件的构建语言。

异步处理

为了使响应时间最小化,许多过程应被设计为异步运行。例如,在用户结帐时,在他收到界面响应之前,不必等待系统发送电子邮件进行确认。

可扩展性

解决可扩展性问题可能是一个非常艰巨的任务。扩展应用程序的第一个方法是向上扩展,主要是为单个服务器配置性能更好的硬件,从而提高速度。对于向上扩展来说,虽然所需考虑的设计因素相对较少,但是这种满足需要的方法过于昂贵,因为硬件价格随性能呈指数上升。

解决可扩展性问题的另一个方法是添加更多的服务器,这种方法被称为“向外扩展”。虽然向外扩展在硬件方面更为合算,但是它需要考虑更多的设计因素。正如以前所讨论的,向外扩展的最大问题是维护会话信息。

为了成功进行向外扩展,商务参考应用程序应满足以下要求:

  • 不使用 ASP Session 对象来维护会话状态,因为它引入了服务器会话亲合力,并要求 IIS 维护内存中的会话状态。
  • 在 Commerce Server 对象的协助下,两个页请求之间的用户会话状态被保存到数据库中,并可被新的页请求检索。虽然这种会话维护方法对于每个页请求来说导致了某些额外的数据库开销,但是它可以很好地满足站点的可扩展性需求。单个高端数据库服务器(或群集)可以为整个前端服务器领域提供状态保存服务。
  • 当用户登录时,会将一个每会话 cookie 发送给用户,并作为“查找字段”来检索相关用户帐户的状态数据。每会话 cookie 不存储在用户的硬盘上,因此即使在最具安全意识的用户浏览器上也可以启用它们。如果在用户浏览器中禁用了每会话 cookie,用户将无法登录到站点。

可管理性

如果选择 Windows 2000 Server 和 Microsoft Commerce Server,将会拥有强大的管理基础结构。正如以前所述的,Commerce Server 提供了 BizDesk 中的功能强大的管理界面,而 Windows 2000 通过 Microsoft Management Console 和其他组件也提供了功能强大的管理界面。

安全性

为了满足为参考体系结构应用程序定义的安全性需求,设计小组就以下问题作出了选择:

身份验证

在“假冒/委托”和“受托服务器”这两个可用的模型中,设计小组选择了“受托服务器”模型作为商务参考体系结构应用程序的身份验证方案。

因为“假冒”模型要假冒每个用户,因此解决方案必须为访问站点的每个用户管理帐户。在基于 Intranet 的小型应用程序中,用户很少,并且限制是基于用户的,因此“假冒”模型可以很好地运行;但是,在较大的解决方案中,“假冒”模型很快会难以控制。因此,设计小组选择了更为简单的“受托服务器”模型,它提供了更好的性能,管理起来也更为方便。

授权

为了遵循物理阶段的“研究”部分中所述的最小权限原理,设计小组是这样选择部署和设计方案的:

  • IIS 虚拟根目录权限:部署后,应将站点设置为对虚拟根目录具有只读权限。
  • NTFS 权限:部署后,用于匿名访问的 Windows 帐户对包含 Web 应用程序文件的文件夹只具有只读权限。
  • 匿名客户的权限:商务参考体系结构解决方案应使用 cookie 来标识未进行身份验证的用户,当用户希望结帐或访问任何配置文件管理页时,应将他们重定向到“登录”页。
  • 已验证客户的权限:即使是通过了身份验证的用户,也应该对他们加以适当限制。例如,系统管理员可以使用 Commerce Server BizDesk 工具,通过对用户隐藏或只允许只读权限,限制对特定配置文件设置的访问。

至于数据库本身,存在的授权问题就更多。用户对数据库具有的唯一直接权限是分配给中间层应用程序(本示例中是 Commerce Server)的帐户具有的权限,且该帐户权限应被限制为向站点提供数据服务所需的最小权限。因此,语句权限(例如 Drop Table)未分配给该帐户。

加密

作为一个应该易于安装和检查的示例应用程序,商务参考体系结构应用程序没有实现加密。不过,在电子商务系统产品中,传输诸如口令或信用卡细节这样的敏感数据时,应该使用加密会话。

虽然在电子商务产品环境中加密是必要的,但是在传输不敏感的数据时,应避免对连接使用 SSL。这是因为建立加密会话时需要一定的开销,其中涉及将服务器的公开密钥传送到浏览器,以及生成和交换加密会话所使用的会话密钥。

浏览器独立性

参考体系结构应用程序使用 XSLISAPI 过滤器来满足浏览器的独立性需求。它提供了将内容和表示形式分离的一流机制,这对于合理处理不同浏览器的功能非常必要。

实现

物理阶段的最后一步是将有关约束、需求和技术方面的决策应用到逻辑设计,并实际定义物理实现方案。在此阶段中,将确定编程模型、组件接口和每个组件的内部结构。

标识组件

商务参考体系结构的各个组件将在此“开发人员指南”的第二部分中进行详细讨论。将在该部分中说明编码方法、使用的组件,以及代码片断和它们的定义。有关详细信息,请参考代码本身提供的开发人员注释。

创建规范

在“实现”阶段结束时,开发小组必须将作出的决策文档化,形成一个详细的技术规范。该文档将成为构建应用程序的蓝图,开发小组在组建专家组、编制一览表、分配任务及创建测试和部署计划时,都要用它作参考。

总结

本章介绍了确定应用程序实际将包含哪些组件、使用哪些技术的物理设计阶段,该阶段可细分为三个更小的阶段;并概要说明了开发商务参考体系结构应用程序 ConsolidatedRetail.com 期间所作选择的依据。在整个项目开发周期中,物理设计阶段的最终目标是将实际的物理设计约束应用到逻辑设计,并编写出一个合理的技术规范来指导开发工作。

本指南的下一部分将着重介绍在“商务参考体系结构:企业对消费者”应用程序中提供的实际代码。正如以前所述的,此应用程序是作为一个参考示例开发的,在作为产品使用时需要进行一些修改。

第 5 章:实现概述

摘要:“Microsoft 商务参考体系结构:企业对消费者”提供了可重复使用和可定制的组件,可用于加快大中型企业的电子商务解决方案开发过程。本章概述了完全理解代码所需的知识,同时还简单介绍了此应用程序的组件。

后面的章节将深入讲解代码的具体方面,以及代码开发过程中遇到的具体实现问题。

简介

正如“开发人员指南”(英文)的第一部分所介绍的那样,“Microsoft 商务参考体系结构:企业对消费者”提供了可重复使用和可定制的组件,可用于加快大中型企业的电子商务解决方案开发过程。Microsoft 商务参考体系结构:B2C 解决方案由一些经过工程处理的代码组件组成,开发人员可利用这些组件来构建使用 Microsoft® Windows® 2000 Server 操作系统和 Microsoft® .Net Enterprise Server 产品(如 Microsoft® Commerce Server 2000 和 Microsoft® SQL Server 2000)的 B2C 零售网站。

该指南的第二部分面向应用程序开发人员和所有其他想学习使用 Microsoft 技术来构建电子商务解决方案的人。这一部分包含对此应用程序及其组件的初步概述和组件指南,并深入讨论了在其开发过程中遇到的实现问题。

实现功能

在参考体系结构应用程序中实现的 ConsolidatedRetail.com 站点包括电子商务解决方案中的许多常见功能。有关应用程序业务需求的完整说明,请参阅“开发人员指南”的第一部分。

建议的背景知识

要理解代码功能的详细信息,本文读者应具备以下知识:

  • ASP: 开发人员应了解如何使用 Microsoft® Visual Basic® Scripting Edition (VBScript) 来创建 Active Server Pages (ASP)。这包括了解核心 ASP 对象,并能实例化和使用组件对象模型 (COM) 和 COM+ 组件。此应用程序使用的大量代码都是 ASP 代码。
  • XML: 在此应用程序中,大量地使用了可扩展标记语言 (XML) 作为在应用程序组件之间传递数据的方法。开发人员应熟悉 XML,包括基本语法、架构和结构。某些代码利用 XML 文档对象模型 (DOM) 来对 XML 内容进行语法分析。
  • XSL: 要理解用户界面是如何工作的,开发人员需要充分掌握可扩展样式表语言转换 (XSLT)。
  • Microsoft Commerce Server 2000 及其对象: Commerce Server 是一个复杂的系统。因此,尽管此应用程序和文档提供了一些有关如何使用该系统的指导信息,开发人员还是应参阅 Commerce Server 文档,以熟悉 Commerce Server 的功能和组件。
  • SQL Server: 尽管此应用程序大量地使用了 SQL Server,但是大多数实际数据库存取操作仍然隐藏于幕后,需通过 Commerce Server 对象提供的抽象层来执行。不过,要真正了解数据的访问和存储方式,开发人员应仔细研究由 Commerce Server 在后台维护的表和存储过程。
  • Visual C++:为了获得最佳性能,此应用程序的自定义 COM 组件是使用 Microsoft® Visual C++® 开发系统编写的。虽然要从参考体系结构中获取有价值的信息并不需要完全理解组件的内部工作原理,但如果要完全理解其工作原理,则需要 C++ 技能。

有关为何选择这些技术的信息,请参阅“开发人员指南”的第一部分。

技术概述

以下各小节简单介绍了参考体系结构应用程序中是如何使用每项核心技术的。

ConsolidatedRetail.com 解决方案中的 XML

该解决方案是通过一个 Commerce Server 2000 站点来实现的。该站点使用 SQL Server 2000 进行数据存储;使用 Commerce Server Object Library 进行管理。该站点本身使用 XML 来表示网页的内容,并使用 XSL 样式表将内容转换为适合 Web 浏览器的超文本标记语言 (HTML)。使用一个 Internet 服务器应用程序编程接口 (ISAPI) 应用程序过滤器对 XML 进行处理,并应用必要的 XSL 转换。使用这种方法,可以轻而易举地将解决方案扩展到其它类型的客户端,而不必用 ASP 重新编写业务逻辑。

Microsoft 已经承诺采用 XML 作为分布式计算环境中传递结构化内容的标准。针对这种情况,商务参考体系结构代码在整个解决方案中都采用了 XML。使用 XML 的示例包括:

  • 从 PASP 脚本生成 XML 输出,然后用 XSLISAPI 过滤器进行 XSL 转换:实现解决方案站点前端功能的预处理 Active Server Pages (PASP) 脚本使用 XSLISAPI 过滤器来生成 XML 格式(而不是 HTML 格式)的输出。这使内容及其表现方式进一步分离。由这些 PASP 页生成的 XML 通过 XSL 样式表进行转换。有关如何使用 PASP 和 XSLISAPI 过滤器的详细信息,请参阅 XSLISAPI 过滤器文档,该文档可从 Microsoft 网站下载。

    注意:参考体系结构应用程序代码中使用了 XSLISAPI 过滤器的自定义版本 (XSLISAPI2.dll)。该版本不能从 www.microsoft.com 下载;不过,公共版(XSLISAPI.dll, 2.1 版)的文档也适用于自定义版本。

  • 从商务组件输出 XML:许多 Commerce Server 2000 对象可以使用 ActiveX 数据对象 (ADO) 或 XML 格式传递记录集(例如,产品列表)。在可能的情况下,ASP 脚本代码从商务对象请求 XML 格式的信息。

使用 ASP 编写前端脚本灵活自如

在 Microsoft 平台上,对于大多数基于 Web 的应用程序开发,驱动 Web 前端的代码都是通过使用 VBScript 编写的 ASP 文件执行的。将 ASP 用于 Web 编程的主要原因是它提供了灵活简便的开发。在迅猛发展的电子商务领域,能够迅速改变站点的外观或基本前端功能非常重要。

脚本语言的解释执行方式让编程人员和设计人员能够迅速改变站点的功能,而不必经历冗长的编译过程:您只需保存源文件,即已改变程序。不过,应该注意的是,为了获得最佳性能和可伸缩性,主要业务处理任务都是通过组件(由 Commerce Server 提供或自定义构建)来执行的。

商务参考体系结构 B2C 解决方案代码在 ASP 编程中加入了新的变化,使用 XSLISAPI 过滤器的功能将内容表现方式的生成与内容分离开。即:预处理的 Active Server Pages (PASP) 脚本不直接生成 HTML 内容,而是生成 XML 输出,然后再由 XSL 样式表将 XML 输出转换成正确的显示格式。这种转换可在服务器或客户机(若客户机具备此能力)上进行,从而缓解了 CPU 的工作压力。

Commerce Server 组件

Commerce Server 2000 为站点开发人员、系统管理员和业务经理提供了一套相当丰富的工具,可用于开发、部署和管理 Web 商务应用程序。与任何功能强大的大型软件系统一样,Commerce Server 提供的功能之强,范围之广绝对会将让您无所适从,除非您熟悉该系统。参考体系结构为自己提供了一个适当的定义:它是一个设计合理、说明充分的小型电子商务应用程序,显示了许多在用的 Commerce Server 对象。虽然没有用到 Commerce Server 的每一个功能,但参考体系结构 B2C 代码演示了如何实现以下功能:

  • 设置边界,划定哪些内容和操作可由匿名用户访问,哪些可由通过验证的用户访问。
  • 不依赖于引入服务器会话相关性的方法,保持用户的会话状态;并在跨越匿名访问和通过验证的访问之间的边界时,保留会话信息。
  • 创建产品目录,浏览目录层次,并通过目录显示产品的详细信息。
  • 在不要求验证的情况下,允许顾客将自己要买的产品收集在一起(使用购物车这一比喻)。
  • 存储和检索用户配置信息,这样,用户在以后访问时,就不需要重新输入这些信息。

参考体系结构应用程序中所用的 Commerce Server 2000 对象在本指南的以后各章中列出。

数据层中的存储过程

此应用程序没有编写任何自定义存储过程。SQL Server 上 Commerce 数据存储区中的所有存储过程都是由 Commerce Server 创建的,用于为许多 Commerce 对象提供数据访问功能。

用于输入检查的客户端脚本

此应用程序的用户界面使用客户端 JavaScript 代码,在多处进行输入验证。客户端脚本的功能并不十分复杂,而且在一定程度上与应用程序的核心功能并无多大关联,因此,本指南不对这些脚本进行详细介绍。

总结

本章介绍要充分利用本指南所需要的背景知识。另外,还简要概述了构成参考体系结构应用程序(ConsolidatedRetail.com 应用程序)的组件。下一章“解决方案指南”详细介绍代码组件,提供代码示例,并提供了指向其它信息的链接。

第 6 章:解决方案指南

摘要:本章概要介绍了解决方案实现组件,其中包括:应用程序的高级图形表示法、关于 PASP 文件和 XSLISAPI 过滤器如何发挥作用的说明、ConsolidatedRetail.com 应用程序中每种主要组件类别的说明。

简介

参考体系结构应用程序包含许多自定义的 ASP 文件和代码组件。本章后面的章节将详细介绍这些文件的源代码。图 6-1 直观清晰地显示了解决方案的各个组件及其相互关系。当阅读本指南的其余部分和查看源代码时,可将该图作为基本参考资料。

图 6-1:参考体系结构应用程序组件

正如您从该图中所看到的那样,用户使用浏览器(在此为 Internet Explorer 5)来访问该应用程序。网站中的 ASP 文件作为预处理的 ASP(PASP 脚本)实现,并使用 Commerce Server 2000 提供的可编程对象(如 ProductCatalogAuthManagerUserProfileOrderGroup)将数据发送到 SQL Server 2000 中的 Commerce Server 站点数据库和从该站点数据库检索数据。下订单或查看购物篮内容之类的业务流程通过 Commerce Server 管道实现。管道调用自定义 COM+ 排队组件来处理电子邮件消息传送功能。每一网页的输出都用 XML 表示,然后由 XSLISAPI 2.1 过滤器截取,而 XSLISAPI 2.1 过滤器将相应的 XSL 样式表应用到内容,将内容作为 HTML 呈现。

本章的下一节将说明 PASP 文件和 XSLISAPI 过滤器是如何发挥作用的,并提供了一个简单示例作为参考。然后,本章会介绍每个主要组件类别在 ConsolidatedRetail.com 应用程序中是如何实现的。我们的讨论采用了逻辑推理的方式,首先介绍 Web 服务,然后介绍 Commerce Server 对象,继而讨论管道组件。

PASP 文件和 XSLISAPI 过滤器

Active Server Page (ASP) 通常用于生成 Web 解决方案的表示逻辑。在 ConsolidatedRetail.com 站点中,使用它们的方法略有不同。在该站点,大多数 ASP 文件是作为预处理的 ASP(即 PASP 文件)实现的。PASP 文件将 XML 写入响应对象,它由 XSLISAPI 过滤器截取和处理。PASP 文件中的脚本用于调用 Commerce Server 2000 对象中的业务逻辑;而由 XSLISAPI 过滤器来处理表示形式。

XSLISAPI 过滤器对从 PASP 文件生成的 XML 输出进行语法分析,并检查 <?xml-stylesheet...?> 处理说明。如果该处理说明包含引用 XML 配置文件的 server-config 属性,则 XSLISAPI 过滤器读取该配置文件,以确定应将哪个 XSL 样式表应用于从 PASP 文件生成的 XML 输出。

XML 配置文件包含有关应将哪个样式表应用于特定客户程序类型的信息,如以下代码示例所示:

<?xml version="1.0" ?>
<server-styles-config>
<!-- 对于 WML 1.1 浏览器 -->
<device target-markup="WML1.1">
<stylesheet href="mypage-WML11.xsl"/>
</device>
<!-- 对于 IE 4.0 浏览器 -->
<device browser="IE" version="4.0">
<stylesheet href="mypage-IE5.xsl"/>
</device>
<!-- 对于 IE 5.0 浏览器 -->
<device browser="IE"  version="5.0">
<stylesheet href="mypage-IE5.xsl"/>
</device>
</server-styles-config>

IIS 只要检查随页请求发送的超文本传输协议 (HTTP) 请求标头,就可以识别客户程序。IIS 使用 WINNT/System32/Inetsrv 文件夹中 Browscap.ini 文件的条目,可以识别许多普通浏览器。XSLISAPI 应用程序在名为 Browscap-add.ini 的文件中为 WAP 电话之类的设备提供附加条目,用户应将该文件复制并追加到 Browscap.ini 文件。

简单示例

以下简单示例说明如何使用 XSLISAPI 过滤器来呈现由 PASP 文件生成的 XML 输出。

注意   该示例旨在帮助您理解 XSLISAPI 过滤器的功能。它并不说明 ConsolidatedRetail.com 站点中的任何实际页。

假定用户使用 Internet Explorer 5 请求名为 Myproducts.pasp 的页。系统将对该文件中的脚本进行解释,然后通过以下响应对象返回下列 XML:

<?xml version="1.0">
<?xml-stylesheet type="text/xsl"
server-config="productconfig.xml"
href="csproducts.xsl"?>
<productlist>
<product productname="widget"/>
<product productname="wrench"/>
</productlist>

现在,XSLISAPI 过滤器将对该 XML 进行语法分析,读取 xml-stylesheet 处理说明,该说明包含以下三个属性:

  • type:指要应用的样式表的类型(在此为 XSL)。
  • server-config:指 XML 配置文件,它定义要用于特定客户程序的样式表。
  • href:指在 server-config 文件中没有列出相应的样式表时,客户程序将下载并应用的默认样式表。

然后,XSLISAPI 过滤器读取指定的 server-config 文件 (Productconfig.xml),该文件包含以下条目:

  <device browser="IE" version="5.0">
<stylesheet href="products-ie5.xsl"/>
</device>

由于 IIS 已根据请求标头识别客户端浏览器为 Internet Explorer 5.0,现在,XSLISAPI 应用程序会将 Products-ie5.xsl 样式表应用于从 Myproducts.pasp 检索到的 XML。

假定 Products-ie5.xsl 样式表包含以下 XSL 代码:

<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
version="2.0">
<xsl:template match="/">
<HTML>
<TITLE>产品目录</TITLE>
<BODY>
<TABLE>
<xsl:for-each select="productlist/product">
<TR><TD>
<xsl:value-of select="@productname"/>
</TD></TR>
</TABLE>
</BODY>
</HTML>
</xsl:template>
</xsl:stylesheet>

该样式表将应用于从 Myproducts.pasp 得到的 XML 数据,从而生成以下 HTML,该 HTML 将发送到浏览器:

<HTML>
<TITLE>产品目录</TITLE>
<BODY>
<TABLE>
<TR><TD>小机械</TD></TR>
<TR><TD>扳手</TD></TR>
</TABLE>
</BODY>
</HTML>

显然,在 ConsolidatedRetail.com 之类的实际电子商务解决方案中,由 PASP 文件生成的 XML 以及用于呈现该 XML 的 XSL 样式表要远比该简单示例复杂得多。不过,原理是相同的。

网站文件

此处介绍一些 ASP 和 PASP 页以及相应的 XSL 文件,它们在 ConsolidatedRetail.com 零售站点的用户界面中经常用到。如前文所述,该应用程序所用的许多 ASP 脚本都是作为 PASP 脚本来实现的,该 PASP 脚本将 XML 输出提交给 XSLISAPI 过滤器。一些常规的 ASP 脚本还用作非显示脚本,接受来自其它页的贴子。

每个 PASP 页都有一个相关的<页名>-config.xml 文件,XSLISAPI 过滤器使用该文件来确定要用于特定客户程序类型的相应样式表。在本实现方案中,只提供了<页名>-ie5.xsl 文件,以使用与 Microsoft Internet Explorer 5.5 兼容的 HTML 形式来呈现页。为使输出能用于其它浏览器或设备,可以创建其它 XSL 文件。

初始化页和包含文件

ConsolidatedRetail.com 站点使用以下初始化脚本和包含文件:

  • Global.asa:Global.asa 是一种 Web 应用程序初始化脚本,它定义在 Web 应用程序的生存期中发生关键事件时要采取的措施。
  • /include/Site_Const.asp:该文件定义了许多常量,站点上的所有页都包含该文件。
  • /include/Common.asp:该文件定义一些函数,这些函数用于呈现 XML 标记形式的数据。站点上的所有 PASP 页都使用该文件来生成将传送给 XSLISAPI 过滤器的 XML。
  • /include/Profile.asp:该文件包含执行以下操作的所有函数:验证用户身份、给用户注册或注销用户、检索或更新用户的个人配置文件。
  • /include/basket.asp:该文件提供用户创建和维护购物篮(即在浏览产品目录期间累积订单)时所需的函数。
  • /include/UI_layout-IE5.xsl:该 XSL 样式表提供表示逻辑,用于呈现站点中各页的标准布局形式。所有页专用的 XSL 文件都包含该样式表,以确保站点呈现出统一的外观和行为。

目录浏览页

ConsolidatedRetail.com 零售站点使用以下与目录相关的页:

  • Default.asp:该页将用户重定向到站点的主页 (Index.pasp)。由于 Default.asp 是 ASP 应用程序的传统起始页名,所以将其置于此处,以将所有对 Default.asp 的请求重定向到应用程序的真正起始页。
  • Index.pasp:Index.pasp 是应用程序的默认起始页。当您只输入站点名而没有附加路径信息时,就会显示该页。当任务完成或出现错误时,若干其它页也会重定向到该页。
  • Category.pasp:该页为特定的页生成 XML,在这一特定的页中,用户可以查看给定产品目录特定类别中的产品和子类别。
  • Product.pasp:该页为特定的页生成 XML 内容,在这一特定的页中,可以显示产品目录中有关单个产品及其所有变体的详细信息。
  • SearchResults.pasp:当用户在产品搜索框中输入搜索标准时,该页接收输入内容,然后搜索与关键词匹配的产品,并将相匹配的产品列表返回给用户。

用户身份验证和配置文件管理页

ConsolidatedRetail.com 零售站点使用以下用户身份验证和管理脚本:

  • Registration.pasp:使用该页,用户可以在站点注册。
  • Login.pasp:使用该页,已注册的用户可以登录到站点。
  • ForgotPasswd.pasp:通过该页,用户能够请求站点用电子邮件将口令发送给自己。
  • Acct.pasp:该页显示当前已登录的用户的帐户详细信息。
  • UserProfile.pasp:在该页,用户可以编辑自己的配置文件。
  • AddressBook.pasp:在该页,用户可以查看并更新地址簿,其中包含发货地址和付款地址。

订单管理页

ConsolidatedRetail.com 零售站点使用以下订单管理页:

  • orderhistory.pasp:该页检索和显示当前用户的订单历史记录,允许用户查看自己在该站点购买的所有商品。
  • orderhistorydetail.pasp:该页显示用户的订单历史记录中特定订单的详细信息。
  • Basket.pasp:该页显示当前用户的购物篮内容,具体而言,就是与用户当前会话相关的 OrderGroup 对象中包含的产品。
  • Ordersummary.pasp:尽管该页的显示结果与 basket.pasp 页的显示结果大不相同,但是该页的脚本以及由脚本生成的 XML 内容却与 basket.pasp 页非常相似。
  • Shipping.pasp:当用户单击购物篮页的“结帐”链接或单击任何页顶部的“结帐”链接时,都将调用该页。用户在该页可以指定订单的发货地址。
  • ShippingMethod.pasp:在该页,用户可以选择订单的发货方法。
  • MultiShipping.pasp:在该页,用户可以为同一订单中的多个项目指定不同的发货地址。
  • ThankYou.pasp:在该页,通过运行一系列的 Commerce 管道进程,完成本应用程序的 Commerce Server 部分的订单处理工作。

Commerce Server 对象

本解决方案的另一重要特性在于将 Commerce Server 2000 可编程对象用于业务处理。任何能识别 COM 的客户程序(如 ASP 脚本)都可使用这些对象,以便检索站点数据库中的数据或执行验证用户身份之类的任务。在第 7 章中介绍站点提供的功能时,将详细探讨这些对象的用途。在这里,只简单介绍一下 ConsolidatedRetail.com 应用程序中使用的几个关键 Commerce Server 对象。

实用程序和配置对象

电子商务站点是一种相当复杂的应用程序,要求能提供各种信息和功能。为了简化电子商务解决方案的开发,Commerce Server 2000 提供了很多对象,它们可用于管理站点配置信息以及提供整个站点都可使用的一般功能。

事实上,Commerce Server 解决方案中所用的许多对象都是 Dictionary 对象或 SimpleList 对象。这些都是通用对象,可用来维护名称/值对的集合。例如,在 ConsolidatedRetail.com 应用程序中,Dictionary 对象用于存储站点所用管道的集合。有关 Dictionary 和 SimpleList 对象的详细信息,请参考 Commerce Server 2000 文档。

ConsolidatedRetail.com 应用程序在整个站点中使用以下实用程序和配置对象:

  • AppConfig:这是 Commerce Server 应用程序配置对象,它提供可用于检索站点配置情况的方法。其中包含 Options 字典对象,该对象可用于存储站点的各种配置选项。在 ConsolidatedRetail.com 站点中,选项字典用于存储数据源连接字符串和其它配置信息。
  • DataFunctions:这是定义数据格式的对象,存放有关站点区域设置、货币和其它与数据相关的问题的信息。该对象还提供处理数据类型特有任务的方法,如删除字符串中的空格或将数据从一种类型转换成另一种类型。
  • GenID:这是实用程序对象,用于生成全局唯一标识符 (GUID)。当需要提供唯一 ID 来标识指定项目时,这些标识符非常有用。
  • AppFramework:这是用于简化 HTML 窗体处理的实用程序对象。
  • MessageManager:该对象用于存储多语种错误消息,以供管道组件使用。
  • CacheManager:该对象用于管理站点的数据高速缓存。如果将目录、发货方法、用户配置文件等数据进行高速缓存,可显著提高系统性能。
  • DictionaryXMLTransforms:该对象使用特定的 XDR 架构,实现 Dictionary 对象的内容与 XML 两者之间的相互转换。
  • 有关 AppConfigDataFunctionsGenIDAppFrameworkMessageManagerCacheManager 和 DictionaryXMLTransforms 对象的详细信息,请参考 Commerce Server 2000 文档。

目录对象

能够从与站点相关的目录检索产品信息,这是电子商务站点中的网页必须完成的常见任务之一。Microsoft® Commerce Server 2000 支持在单个站点中使用多个目录。您可以用嵌套的类别将目录组织成一个分层结构,可以定义目录的任何级别(包括根)上的产品。另外,您还可以在分层结构中的任何位置,将任何类别或产品与其它类别或产品关联起来。例如,可将“软件”类别中名为 Windows® 2000 Server 的产品与“书”类别中名为 The Windows 2000 Server Administrators Guide 的产品关联起来。这样就能轻松地创建到相关产品或类别的链接,创造交叉销售的机会。图 6-2 显示了 Commerce Server 2000 支持的目录结构。

图 6-2:Commerce Server 2000 目录结构

使用 Commerce Server Business Desk,您既可以从头开始创建目录,也可以从 XML 或 CSV 文件导入目录。有关如何使用 Business Desk 管理目录的详细信息,请参考 Commerce Server 2000 文档。

Commerce Server 中的另一项目录支持功能是您可以创建“目录集”。目录集是指一个或多个目录的集合,基于用户配置文件中的属性而将这些目录分配给特定用户类。例如,您可以创建一个目录集,其中包含一些价格可打折的目录,以便为参与忠实客户计划的用户提供优惠。对于未分配目录集的用户,Commerce Server 站点包含为匿名用户提供的默认目录集以及为通过了身份验证的用户提供的默认目录集。

通过以下 Commerce Server 自动化对象可以提供对目录数据的程序访问:

  • CatalogManager:该对象代表整个目录管理系统。
  • CatalogSets:通过该对象可以访问站点中定义的目录集。
  • ProductCatalog:该对象代表特定目录。
  • Category:该对象代表目录中的类别。
  • Product:该对象代表产品。

上述对象提供了浏览站点的整个目录分层结构的方法。这些对象提供的许多方法和属性都返回 ADO 记录集,其中包含目录、类别或产品数据。

对于任何目录数据,必须先使到站点数据源的连接初始化,才能访问它们。通常,要完成此操作,必须调用 CatalogManager 对象的 Initialize 方法,将到站点数据库的 ADO 连接字符串或站点名作为参数传送。

有关 CatalogManagerCatalogSetsProductCatalogCategoryProduct 对象的详细信息,请参考 Commerce Server 2000 文档。

用户管理对象

Commerce Server 提供了若干对象,用于在用户与站点交互时管理用户信息和状态。这些对象包括:

  • AuthManager:该对象用于处理与安全性相关的功能,允许用户进行身份验证并登录,提供标识特定用户所作请求的方式。
  • ProfileService:该对象用于连接到数据源,那里存储着与站点的已注册用户相关的配置文件信息。
  • ProfileObject:该对象用于封装特定用户的配置文件数据,并可用于设置或检索以下值:“姓名”、“电话号码”、“电子邮件地址”或其它任何需要跟踪的用户属性。

有关 AuthManagerProfileServiceProfileObject 对象的详细信息,请参考 Commerce Server 2000 文档。

购物篮对象

当用户找到需要购买的产品时,允许用户将产品放入购物车或购物篮是网站的平常之举。事实上,购物篮概念只是软件对象的比喻说法,它代表用户在访问过程中选择的项目集合。

Commerce Server 2000 提供了以下对象用于购物篮的管理。

  • OrderGroup:该对象代表购物篮。
  • OrderForm:该对象代表购物篮中的项目集合。OrderForm 对象是作为 Dictionary 对象实现的,它包含订单的总体信息,而 SimpleList 对象则包含代表购物篮中单个项目的项目 Dictionary 对象。Addresses SimpleList 对象用于存储地址 Dictionary 对象的集合,代表与订单相关的发货地址和付款地址。

有关 OrderGroupOrderForm 对象的详细信息,请参考 Commerce Server 2000 文档。

管道

Commerce Server 2000 解决方案一般使用管道来处理业务。管道由一系列 COM 组件组成,这些组件将对 OrderForm 之类的业务对象进行操作。管道中的组件以及调用这些组件的顺序,在管道配置文件 (*.pcf) 中指定。您可以将管道视为一种生产线,其中每个组件执行特定的任务,然后再将业务对象传送给下一个组件。

ConsolidatedRetail.com 使用名为 PAGBasket.pcf、Total.pcf 和 Final.pcf 的三个管道。有关对它们的引用存储在应用程序级的 MSCSPipelines 字典变量中,分别为 PAGBasketPAGTotalPAGFinal。这些管道执行的特定进程将在以后讨论站点的功能时介绍。

管道组件

管道中的组件是实现 IPipelineComponent 接口的 COM 组件。该接口提供 Execute 方法,可以将字典对象传送给该方法。将 OrderForm 之类的业务对象依次传送给管道中每个组件的 Execute 方法,就可以实现业务流程。

Commerce Server 提供了大量可用于电子商务站点的管道组件。这些对象用于将折扣应用到订单、征收税金、安排发货等等。此外,您还可以创建自定义的管道组件来执行特定任务。ConsolidatedRetail.com 站点包含以下两个自定义管道组件:

  • PersistUtility:该自定义管道组件用于将 OrderGroup 对象的非持久性属性复制到持久性属性。通常,以下划线开头的 OrderGroup 对象属性都是临时值,在管道处理结束时,会毁坏这些值。由于本应用程序会将订单窗体导出到 Commerce Server 领域外,因此,需要保留一些通常被丢弃的 OrderGroup 值,以便提供外部处理时的上下文。PersistUtility 会将一些带下划线的属性复制到不带下划线的名称中,这样在管道处理完成后,这些值仍然存在着。
  • PipelineQueue.QueueEMail:该自定义管道组件从订单窗体中获取数据,将这些数据转换成 XML,然后使用 QueuedEMailer.CMailer 组件(将在“自定义业务组件”一节中介绍)将电子邮件消息发送给用户。

自定义业务组件

您可以创建一些自定义业务组件,给解决方案添加其它自定义功能。ConsolidatedRetail.com 站点就包含一个自定义业务组件:QueuedEMailer.CMailer。QueuedEMailer.CMailer 是一个自定义的 COM 组件,用于给用户发送电子邮件。进行订单确认或忘记口令时,需要使用该组件。该组件作为 COM+ 排队组件实现,通过消息队列可异步调用其方法。这样,用户会话不必等到电子邮件处理完毕即可继续,从而缩短了响应时间。

总结

本章简要介绍了参考体系结构应用程序中的主要代码类别,同时提供了一些实际代码片段作为参考。此时,您应该对代码的构成方法有了一个基本概念。

下一章将详细介绍 ConsolidatedRetail.com 应用程序的每一层,同时提供一些伪代码和实际代码示例来说明处理流程。

第 7 章:ConsolidatedRetail.Com 的功能

摘要:本章介绍了 ConsolidatedRetail.com 应用程序的功能,同时提供了伪代码和实际代码示例来详细说明处理流程。

简介

现在,您对解决方案的各个组成部分已经有了总体了解,可以开始检查站点提供的具体功能了。本章探讨了解决方案的以下几个方面,并着重讨论了为每个功能区编写代码时所面临的内在问题:

  • 表示服务
  • 用户身份验证和用户配置文件的维护
  • 产品目录
  • “购物篮”管理
  • 订单处理

阅读本章的各个小节时,您将了解具体功能的实现方法,以及如何在您自己的企业对消费者 (B2C) 解决方案中重新利用 ConsolidatedRetail.com 应用程序的部分或全部内容。

表示服务

ConsolidatedRetail.com 站点中的表示服务由 XSLISAPI 过滤器提供。面临的主要开发问题在于:如何在每个 PASP 脚本中生成合适的 XML,以及如何创建合适的 XSL 样式表将 XML 表示为 HTML(或其它表示格式)。

ConsolidatedRetail.com 中来自 PASP 文件的 XML 输出

在 ConsolidatedRetail.com 站点中,每个 PASP 文件都生成格式相同的 XML 文档。这是通过在站点的每一页中加入对 Common.asp 文件的 Include 引用,并调用其 PageStart 过程实现的。

Common.asp 文件中的 PageStart 过程包含以下代码:

Sub PageStart(strPageNameWithExtension)
' 除去 pasp 文件扩展名
Dim strPageName
strPageName = Trim(Left(strPageNameWithExtension, _
InstrRev(strPageNameWithExtension, _
".") - 1))
'XML 标头
Response.Write _
"<?xml-stylesheet type=""text/xsl" _
" server-config=""" & _
strPageName & "-Config.xml"" href=""" & _
strPageName & "-IE5.xsl""?>"
'页元素的根。
'结束标记在“PageEnd”子例程中生成。
Response.Write "<page pagename="" _
" & strPageNameWithExtension & """>" & vbcrlf
End Sub

此过程为 PASP 文件将要生成的文档创建 XML 标头。在每个 PASP 脚本的末尾,将调用 PageEnd 过程来生成 <page> 的结束标记并完成 XML 文档。下面列出了 PageEnd 过程中的相关代码:

Sub PageEnd()
'为了表达得更为清楚,这里省略了有关目录、注销和错误消息的代码
Call XMLEndTag("page")
End Sub

XMLEndTag 过程是 Common.asp 中的众多 XML Helper 过程之一。(有关详细信息,请参考本章中的“XML Helper 过程”一节。)它使用以下代码生成 XML 结束标记:

Sub XMLEndTag(strTagName)
Response.Write mc_strStartTag & mc_strForwardSlashTag & _
Lcase (Replace(strTagName, mc_strBlank,_
mc_strUnderScore)) & _
mc_strEndTag & vbCrLf
End Sub

使用 PageStartPageEnd 过程生成的 XML 文档类似于以下代码示例:

<?xml-stylesheet type="text/xsl"
server-config="filename-Config.xml"
href="filename-IE5.xsl"?>
<page pagename="filename.pasp">
<!-- XML 内容 -->
</page>

XML Helper 过程

除了上面提到的 XMLEndTag 过程之外,Common.asp 还包含多个与 XML 有关的实用程序例程。使用这些过程可以生成一些标记,这些标记将用于 ConsolidatedRetail.com 站点中的 PASP 页所生成的 XML 文档。

虽然在许多情况下,对所需的 XML 字符(例如 "<" 和 ">",请注意不包括引号)进行硬编码更为有效,但是,在各种 XML 生成过程中,大量 XML 字符常量均在 Common.asp 中进行声明。这样可增加代码的可读性,同时有助于调试。这些常量是:

Const mc_strStartTag = "<"
Const mc_strEndTag = ">"
Const mc_strForwardSlashTag = "/"
Const mc_strUnderScore = "_"
Const mc_strBlank = " "

以下 XML 生成过程使用了这些常量:

  • XMLBegTag:此实用程序根据 strTagName 参数编写一个 XML 开始标记(例如 <page>)。
  • XMLEndTag:此实用程序(在上文中已经做过介绍)根据 strTagName 参数编写一个结束标记(例如 </page>
  • XMLEmptyTag:此实用程序根据 strTagName 参数编写一个空 XML 标记(例如 <page/>)。
  • XMLTag:此实用程序根据 strTagNamestrTagValue 参数编写一个包含值的 XML 标记(例如 <page>myvalue</page>)。
  • GetXMLFromRS:此实用程序创建记录集的 XML 表示。
  • GetXMLFromRSRow:此实用程序创建记录集中当前行的 XML 表示。

此外,还有许多其它过程(统称为 xxxWithDsplyNm)用于生成包含 displayname 属性的 XML 标记,例如:

<f_name displayname="First Name">Joe</f_name>

标记的显示名称基于标记名,从 CatalogDefinitionProperties 应用程序级字典对象变量中检索。

要全面了解 XML Helper 过程提供的功能,请查看 Common.asp 中的源代码。

ConsolidatedRetail.com 站点中的 XSL 样式表

如前所述,每页都有一个相关联的“文件名”-Config.xml 文件,该文件列出了要应用于每种客户端浏览器或设备类型的 XSL 样式表。在本实现方案中,只支持 Microsoft® Internet Explorer 5.5,尽管可以创建替代样式表并将添加到站点来解决这一问题。

每个 PASP 页的 XSL 文件被命名为“页名”-IE5.xsl(其中“页名”是 PASP 页的非版本特定名称),该文件包含该页数据专用的 XSL 代码。但是,为了使站点保持统一的外观,需要在一个单独的 XSL 文件(名为 UI_layout-IE5.xsl)中定义所有页的公共用户界面 (UI) 元素。该文件存储在 Include 目录下。每一页专用的 XSL 文件使用 XSL Include 指令将 UI_layout-IE5.xsl 中的表示逻辑合并到当前页的呈现形式中,如以下代码段所示:

<xsl:include href="include/UI_layout-IE5.xsl"/>

使用 UI_layout-IE5.xsl 中的模板

UI_layout-IE5.xsl 文件包含几个模板。page 模板将应用于每个 PASP 页所生成的 XML 文档中的 <page> 元素。该模板引用 Stylesheet.css 级联样式表,并创建一个包含五行的 HTML 表,总体页将基于该 HTML 表。系统调用 UI_layout-IE5.xsl 文件中的其它模板来填充这些行。

表的第一行包含对 pageheader 模板的调用(该模板在后面的脚本中定义)。该模板呈现页首标题,包括链接到“购物篮”、“配置文件”和“主页”的图象。

第二行用于创建距第三行的间距,而第三行包含对 main 模板的调用。该模板包含呈现页左侧菜单面板(包括搜索表单)所需的逻辑。main 模板依次调用 getCatalogsForUser 模板、Exceptions 模板以及 advertisingprofilemenu 模板(这取决于是否存在 advertisingprofilemenu XML 元素)。

表的第四行与第二行类似,用于创建距第五行的间距;第五行则调用了 pagefooter 模板。该模板呈现页的底部面板,包括版权声明。

UI_layout-IE5.xsl 中的 page 模板如以下代码所示。可在 UI_layout-IE5.xsl 中查看 pageheadermainpagefooter 和其它用于呈现站点中的页的模板。

呈现 Index.pasp

您可以检查 Index.pasp 页,以查看站点中各个页之间的组合关系。Index.pasp 页是站点的默认页(即主页)。Index.pasp 包含执行以下任务的代码:

  1. 用值 Index.pasp 定义一个名为 mc_strPageName 常量。
  2. 调用 Common.asp 中的 PageStart 过程,以创建相应的 <page> 开始标记。
  3. 使用 XMLEmptyTag 过程创建空的 <advertising/> 标记。
  4. 调用 PageEnd 过程创建 </page> 结束标记。

以下代码执行上述任务:

 <!--#include file = "include/Site_Const.asp" -->
<!--#include file = "include/Common.asp"-->
<%
Const mc_strPageName = "Index.pasp"
Sub Main()
Call PageStart(mc_strPageName)
XMLEmptyTag(mc_strAdvertisingMenu)
Call PageEnd()
End Sub
Call Main()
%>

此代码生成以下 XML 文档:

<?xml-stylesheet type="text/xsl"
server-config="Index-Config.xml"
href="Index-IE5.xsl"?>
<page pagename="Index.pasp">
<advertising/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile/>
<exceptions></exceptions>
</page>

这些 XML 代码将被传递到 XSLISAPI 过滤器,该过滤器确定在 Index-Config.xml 中指定相应的样式表。XSLISAPI 过滤器检查 HTTP 请求标头信息以确定浏览器的类型和版本。然后,对浏览器信息与 Index-Config.xml 中的相应样式表进行比较。如果未找到匹配条目,则应用默认样式表 (Index-IE5.xsl)。

Index-Config.xml 类似于以下代码:

<?xml version="1.0" ?>
<server-styles-config>
<!-- 对于 HDML 3.0 浏览器 -->
<device target-markup="HDML 3.0">
<stylesheet href="Index-HDML3.xsl"/>
</device>
<!-- 对于 WML 1.1 浏览器 -->
<device target-markup="WML1.1">
<stylesheet href="Index-WML11.xsl"/>
</device>
<!-- 对于 IE 4.0 浏览器 -->
<device browser="IE" version="4.0">
<stylesheet href="Index-IE5.xsl"/>
</device>
<!-- 对于 IE 5.0 浏览器 -->
<device browser="IE"  version="5.0">
<stylesheet href="Index-IE5.xsl"/>
</device>
<!-- 对于 MME 浏览器 -->
<device browser="MME">
<stylesheet href="Index-WML11.xsl"/>
</device>
</server-styles-config>

从 Internet Explorer 5.0 浏览器收到请求时,使用 Index-IE5.xsl 样式表将页作为 HTML 呈现。(Index-Config.xml 文件中的其它条目只是作为例证;ConsolidatedRetail.com 站点中并不提供相应的样式表。)

Index-IE5.xsl 类似于以下代码:

此样式表包含上文介绍的 UI_layout-IE5.xsl 样式表。UI_layout-IE5.xsl 样式表用于呈现页的公共 UI 元素。在此示例中,XML 文档包含 <advertising/> 标记。因此,UI_layout-IE5.xsl 也使用 advertising 模板来呈现页的右侧面板。所得到的索引页类似于图 7-1。

图 7-1:索引页

ConsolidatedRetail.com 站点按照上文所述的方法呈现 PASP 页。使用这种方法(在 PASP 文件中生成 XML 内容,在 XSL 样式表中定义表示形式),您可以轻松更改用于显示页面的样式表,而不会影响 PASP 文件中的业务逻辑。因此,这个解决方案十分灵活,便于重用。

用户身份验证和用户配置文件的维护

Microsoft® Commerce Server 2000 支持可扩展的配置文件系统,可以存储大量用户数据。该用户数据(或用户配置文件)可以包含收货人地址和联系人信息。客户可以使用这些配置文件来存储个人数据,其中包括收货人地址和联系人信息,这样他们就不必在每次访问站点时都要重新输入此类信息。公司则可以将配置文件信息用于商业分析和开展有针对性的广告宣传活动。

要创建和维护配置文件,用户必须登录到站点并在站点上注册。这将创建一个唯一用户 ID,用于标识用户以及从数据库中检索相应的配置文件信息。用户的 ID 存储在客户端浏览器上的 cookie 中。如果用户未登录,cookie 将包含与匿名用户相关的用户 ID,此时配置文件信息不可用。当用户登录时,将检索相应的用户 ID 并将其写入 cookie,这样,在后面的会话过程中就能够使用配置文件信息。

请注意,此站点要求将浏览器配置为允许 cookie。如果用户使用配置为允许每会话 cookie(即不存储到用户硬盘上的 cookie)的浏览器来访问此站点,而不存储 cookie,则用户将能够登录到站点,也能使用站点,但是不能将产品放到购物篮中。如果根本不允许 cookie,则用户将不能访问此站点。

用户注册

用户可以使用 Registration.pasp 页注册到此站点。使用 Registration-IE5.xsl 样式表呈现该页时,该页包含一个向自身发回数据的表单。Registration.pasp 页包含执行以下任务的代码:

  1. 检查表单中的数据以确定 ModeProcessAction 参数。Mode 参数用于指定注册完成后应将用户重定向到的页(默认值是 Acct.pasp)。ProcessAction 参数用于确定是显示注册表单以便允许用户注册,还是将表单发回以进行处理。
  2. 将注册数据传送到 PutUserObject,以便创建新的配置文件。PutUserObject 在 Profile.asp 头文件中定义。
  3. 将用户重定向到 Acct.pasp 页(或 Mode 参数所指定的其它页)。
  4. Registration.pasp 页使用 MSCSAppFrameWork 应用程序级变量从查询字符串中检索 ModeProcessAction 值,如以下代码段所示:
    strPageMode = _
    Application("MSCSAppFrameWork").RequestString( _
    "Mode", Null, , , True, True, 0, Null)
    strProcessingAction = _
    Application("MSCSAppFrameWork").RequestString( _
    "ProcessAction", Null, , , True, True, 0, Null)
    

    Mode 通常为空,这表示用户已成功注册,应重定向到帐户管理页 (Acct.pasp)。在某些情况下,Mode 包含其它页的名称,应将用户重定向到该页。例如,如果匿名用户在注册前将产品添加到了购物篮,您可能希望在用户注册后将用户重定向到“结帐”页。

    ProcessAction 参数用于确定用户是从站点中的另一页进入注册页的,还是从注册页本身进入的。如果是前一种情况,将会呈现注册表单;如果是后一种情况,则会使用注册表单的内容来注册用户。如果 ProcessAction 参数是 EditUserObject,则可以使用该页上表单中的数据来注册用户。

  5. 然后,脚本将检索表单数据并将数据传递到 PutUserObject 函数,该函数在 Profile.asp 头文件中定义。该头文件包含了管理用户配置文件的各种过程,站点中的各页将要用到这些过程。PutUserObject 函数用于添加或更新用户配置文件,并返回指示成功或失败的布尔值。如果成功创建了用户,则允许用户登录并将其重定向到另一页。如果失败(例如由于指定的用户名已存在),则重新显示 Registration.pasp 页,并显示错误消息指出问题所在。
  6. 代码调用 Common.asp 中的 GetUserID 函数来检索用户 ID。该函数用于更新现有用户,本章稍后将对其进行详细讨论。用于注册新用户的下一行重要代码将检查是否存在具有指定用户名的用户:
    Set objMSCSProfile = _
    Application("MSCSProfileService").GetProfile(strUserName, _
    _  mc_strUserObject, blnReturnCode)
    If Not (objMSCSProfile Is Nothing) Then
    Call AddException(m_varrExceptions, 1, _
    "用户名已存在。", mc_strPageName)
    Set objMSCSProfile = Nothing
    
  7. 如果尚未使用过该用户名,代码将生成一个 GUID(唯一用户 ID)并添加该用户,同时调用 ProfileService 对象的 CreateProfile 方法。来自注册表单的值将赋给配置文件:
    strUserID = GenerateGUID()
    Set objMSCSProfile = _
    Application("MSCSProfileService").CreateProfile( _
    strUserName, mc_strUserObject)
    objMSCSProfile.Fields(mc_strGeneralInfo).Value _
    (mc_strUser_ID) = cstr(strUserID)
    objMSCSProfile.Fields(mc_strAccountInfo).Value _
    (mc_strAccount_Status) = CInt(1)
    objMSCSProfile.Fields(mc_strGeneralInfo).Value _
    ("user_type") = strUserType
    objMSCSProfile.Fields(mc_strGeneralInfo).Value _
    (mc_strUser_Security_Password) = strPassword
    
  8. 该页剩余部分中的大多数代码用于更新现有用户对象。最后,更新配置文件对象,函数返回 True
    objMSCSProfile.Update
    Set objMSCSProfile = Nothing
    PutUserObject = True
    

    该函数运行完毕后,新用户就注册到了站点数据库。

  9. 用户注册后,Registration.pasp 将浏览器重定向到 Acct.pasp(如果 Mode 参数是 NULL)或 Mode 参数所指定的其它页:
    If blnRegistered Then
    If IsNull(strPageMode) Then
    Response.redirect "Acct.pasp?Mode=" & strPageMode
    Else
    Response.Redirect strPageMode & "?Mode=" & strPageMode
    End If
    End If
    

对用户进行身份验证

已注册的用户必须登录并通过身份验证,才能访问其配置文件信息。如上文中所述,Registration.pasp 中的代码将使新注册的用户自动登录。但是,用户再次登录时,必须提供用户名和口令才能通过身份验证。

注意:  在本应用程序示例中,登录详细信息是使用 HTTP 协议以纯文本格式传递的。但在实际站点中,应使用安全超文本传输协议 (HTTPS) 对安全凭据进行加密。

用户可以使用 Login.pasp 页登录。与 Registration.pasp 一样,此页向自身发送数据并使用 Profile.asp 的功能来处理数据。Login.pasp 页包含执行以下任务的代码:

  1. 检查表单中的数据以确定 ModeProcessAction 参数。Mode 参数用于指定登录完成后应将用户重定向到的页(默认值是 Acct.pasp)。ProcessAction 参数用于指定是显示用户凭据表单以便允许用户登录,还是将表单发回以进行处理。
  2. 从该表单检索用户凭据。
  3. 将凭据传递到 Profile.asp 中的 Login 函数。
  4. 将用户重定向到 Acct.pasp 页(或 Mode 参数所指定的其它页)。
  5. Login.pasp 中最重要的代码是对 Profile.asp 中 Login 函数的调用:
    blnLoginSuccessful = Login(strUserName, strPassword)
    
  6. Login 函数用于校验用户的用户名和口令,并以 cookie 的形式将身份验证单发送到客户端浏览器。该函数执行的第一个任务是使用以下代码创建并初始化 AuthManager 对象:
    Set objMSCSAuthMgr = _
    Server.CreateObject(mc_strAuthManager)
    Call objMSCSAuthMgr.Initialize _
    (Application("MSCSDictConfig").s_SiteName)
    
  7. 然后,该代码将从前一登录中删除用户现有的所有身份验证单:
    If objMSCSAuthMgr.IsAuthenticated Then
    call objMSCSAuthMgr.SetAuthTicket _
    ("", blnCookieSupport, _
    Application("MSCSDictConfig")._
    i_FormLoginTimeOut)
    End If
    
  8. 检查并确保提供的用户名非空后,代码将使用应用程序级 MSCSProfileService 对象为具有指定名称的用户加载配置文件:
    Set objMSCSProfileObject = Application("MSCSProfileService").GetProfile(strUserName, mc_strUserObject, blnReturnCode)
    
  9. 如果为已注册的用户找到了匹配的配置文件,则检索口令和用户 ID,并将口令与用户提供的口令进行比较:
    strProfilePassword = _
    objMSCSProfileObject(mc_strGeneralInfo).Value _
    (mc_strUser_Security_Password)
    strUserID = objMSCSProfileObject(mc_strGeneralInfo). _
    Value (mc_strUser_ID)
    If CStr(strProfilePassword) = CStr(strPassword) Then
    ...
    
  10. 在以匿名方式浏览时能够将产品添加到购物篮,这是 ConsolidatedRetail.com 站点的设计特色之一。如果用户在登录前就将产品添加到了购物篮,则已给该用户签发了一张匿名配置文件单。AuthManager 对象现在要检索此单据,以便将匿名用户的购物篮内容传输到已通过身份验证的用户的购物篮:
    strProfileUserID = _
    objMSCSAuthMgr.GetUserID(mc_bytProfileTicketType)
    
  11. 为用户签发一个新单据(通过身份验证的用户单据),将匿名 cookie 设置为空白值,从而将其删除:
    Call objMSCSAuthMgr.SetAuthTicket _
    (strUserID, blnCookieSupport, _
    Application("MSCSDictConfig"). _
    i_FormLoginTimeOut)
    Call objMSCSAuthMgr.SetUserID(mc_bytAuthTicketType, _
    strUserID)
    Call objMSCSAuthMgr.SetUserID(mc_bytProfileTicketType, "")
    Call objMSCSAuthMgr.SetProfileTicket("", blnCookieSupport)
    
  12. 最后,传输匿名购物篮中的所有产品,并声明登录成功:
    If (Len(strProfileUserID) > 0) Then
    ' 从匿名会话获取配置文件
    '  对象
    Set objMSCSUnRegProfileObject = Application( _
    "MSCSProfileService").GetProfilebykey( _
    mc_strUser_ID, strProfileUserID, _
    mc_strUserObject, blnReturnCode)
    ' 如果返回匹配的匿名配置文件
    If Not (objMSCSUnRegProfileObject Is Nothing) Then
    ' 将购物篮内容从匿名 ID 传输到已注册的
    ' ID
    Call MoveBasketItems(strProfileuserid, strUserID)
    ' 从配置文件存储区中删除匿名配置文件
    Call Application("MSCSProfileService"). _
    DeleteProfileByKey(mc_strUser_ID, _
    strProfileUserID, mc_strUserObject)
    End If
    Set objMSCSUnRegProfileObject = Nothing
    End if
    ' 返回表示成功登录的值
    Logon = True
    

在之后的会话过程中,用户为每个请求提供身份验证 cookie,这样,代码就可以检索到用户的配置文件信息。

检索和更新配置文件信息

此站点允许用户在多个页上查看和编辑其配置文件信息。由于各页均使用类似的代码更新用户配置文件中的字段,因此本章只详细讨论 UserProfile.pasp 页。您也可以查看 EditAddressBook.pasp 和 ChangePasswd.pasp 中的代码,它们执行类似的功能。

UserProfile.pasp 页包含一个表单,用户可在该表单中查看和更改名字、姓氏、电子邮件地址和电话号码的配置文件值。其设计思路类似于上文所述的 Registration.pasp 页。UserProfile.pasp 页包含执行以下任务的代码:

  1. 检查 ProcessingAction 查询字符串的值。如果值为 EditUserObject,则该页已将表单内容发送给自身,必须更新配置文件。
  2. 从查询字符串中检索表单值。
  3. 将表单值传递到 Profile.asp 中的 PutUserObject 函数。
  4. 将配置文件值转换为 XML 格式。
  5. 如上文所述,PutUserObject 函数用于在收到用户名时注册新用户。如果未收到用户名,则该函数假定用户已存在并尝试更新现有配置文件中的数据。
  6. 要访问配置文件,PutUserObject 使用 Common.asp 中的 GetUserID 函数从身份验证单中检索当前用户的 ID,如下所示:
    Function GetUserID()
    Dim objMSCSAuthMgr '身份验证管理器
    Dim strUser_ID    ' 从身份验证管理器中
    ' 检索到的用户 ID
    ' 将 AuthManager 对象实例化和初始化。
    Set objMSCSAuthMgr = _
    Server.CreateObject(mc_strAuthManager)
    Call objMSCSAuthMgr.Initialize _
    (Application("MSCSDictConfig").s_SiteName)
    strUser_ID = Null
    ' 如果用户已通过身份验证且身份验证单未
    ' 超时
    If objMSCSAuthMgr.IsAuthenticated Then
    ' 获取进行身份验证时所用的唯一登录 ID。
    strUser_ID  = _
    objMSCSAuthMgr.GetUserID(mc_bytAuthTicketType)
    ' 否则,如果用户以匿名方式进行浏览
    Else
    ' 获取配置文件用户 ID(即 GUID)。
    ' 如果 getuserid 方法返回空字符串
    ' 将该字符串转换为 Null
    strUser_ID = _
    objMSCSAuthMgr._
    GetUserID(mc_bytProfileTicketType)
    If not isNull(strUser_ID) Then
    If len(trim(strUser_ID)) = 0 Then
    strUser_ID = Null
    End If
    End If
    End If
    ' 返回新的 GUID 或当前已通过身份验证的
    ' 用户名
    GetUserID = strUser_ID
    Set objMSCSAuthMgr = Nothing
    End Function
    
  7. 为了检索用户配置文件,Profile.asp 中的 PutUserObject 函数代码使用了 ProfileService 对象的 GetProfileByKey 方法:
                Set objMSCSProfile = Application("MSCSProfileService").GetProfilebyKey( _
    mc_strUser_ID, strUserID, mc_strUserObject, blnReturnCode)
  8. 最后,代码更新该用户的配置文件字段:
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strFirst_Name) = strFirstName
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strLast_Name) = strLastName
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strEmail_Address)= strEmailAddress
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strTel_Number) = strTelNumber
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strWork_Number) = strWorkNumber
    objMSCSProfile.Fields(mc_strGeneralInfo).Value( _
    mc_strWork_Extension) = strWorkExtension
    objMSCSProfile.Fields(mc_strProfileSystem).Value( _
    mc_strDate_Last_Changed) = Now
    objMSCSProfile.Update
    
  9. 更新配置文件后,UserProfile.pasp 中的代码调用 GetUserObjectXML 过程,将整个用户配置文件作为 XML 呈现(实际上只显示 UserProfile-IE5.xsl 中引用的字段,但是可以在不更改代码的情况下,对该样式表进行修改以显示其它配置文件信息)。GetUserObjectXML 过程使用 ProfileService 对象的 GetProfileByKey 方法来检索用户的配置文件。然后,使用 Include/common.asp 中的 XML Helper 例程呈现数据。GetUserObjectXML 例程类似于以下代码:
    Sub GetUserObjectXML()
    Dim objMSCSProfile
    Dim strUserID
    Dim Field
    Dim Group
    Dim blnReturnCode
    Const c_strProfile = "userobject"
    strUserID = GetUserID
    If Not IsNull(strUserID) Then
    ' 使用指定架构初始化配置文件服务并连接到配置文件存储区
    If Not Application("MSCSProfileService") Is Nothing Then
    ' 使用配置文件服务检索用户的配置文件对象,并将该对象指派
    ' 给函数的返回值
    Set objMSCSProfile = Application("MSCSProfileService") _
    .GetProfilebyKey(mc_strUser_ID, GetUserID, _
    mc_strUserObject, blnReturnCode)
    If Not objMSCSProfile Is Nothing Then
    Call XMLBegTag(c_strProfile)
    For Each Group In objMSCSProfile.Fields
    Call XMLBegTag(Group.Name)
    For Each Field In Group.value
    Call XMLTag(Field.Name, Field.Value)
    Next
    Call XMLEndTag(Group.Name)
    Next
    Call XMLEndTag(c_strProfile)
    End If
    Set objMSCSProfile = Nothing
    End If
    End If
    End Sub
    

此过程将生成以下 XML 输出:

<?xml-stylesheet type="text/xsl"
server-config="UserProfile-Config.xml"
href="UserProfile-IE5.xsl"?>
<page pagename="UserProfile.pasp">
<profilemenu/>
<pagemode/>
<userobject>
<accountinfo>
<org_id/>
<account_status>1</account_status>
<user_catalog_set/>
<date_registered/>
</accountinfo>
<advertising>
<campaign_history/>
</advertising>
<businessdesk>
<partner_desk_role/>
</businessdesk>
<generalinfo>
<user_id>
{8A7D56E9-DABA-499A-96B8-1F8DD93D032B}
</user_id>
<logon_name>Kim</logon_name>
<user_security_password>
password
</user_security_password>
<email_address>Kim@somecompany.com</email_address>
<user_type>1</user_type>
<user_title/>
<last_name></last_name>
<first_name></first_name>
<tel_number></tel_number>
<tel_extension/>
<fax_number></fax_number>
<fax_extension></fax_extension>
<user_id_changed_by/>
</generalinfo>
<profilesystem>
<date_last_changed>
2/8/2001 11:57:13 PM
</date_last_changed>
<date_created>2/8/2001 11:57:13 PM</date_created>
</profilesystem>
</userobject>
<!—该页的其余内容由 Common.asp 生成 -->
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

ConsolidatedRetail.com 站点提供了有效的配置文件功能,并演示了创建、检索和更新用户配置文件信息的基本方法。使用 Commerce Server Management Desk,您可以扩展此功能以创建自定义配置文件属性,并且可以使用配置文件信息为各个用户提供站点个性化服务。有关使用 Commerce Server 2000 配置文件功能的详细信息,请参考 Commerce Server 2000 文档。

产品目录

为用户提供浏览产品目录的简单方法,是设计电子商务站点时最重要的设计目标之一。ConsolidatedRetail.com 站点通过以下三种方式实现这一目标:

  • 目录始终在用户界面中列出。
  • 用户可以通过分层的类别结构进行浏览。
  • 用户可以在目录中搜索特定的字符串。

当前用户相关目录集中的目录始终列在用户界面的左侧窗格中。这是通过在 Common.asp 的 PageEnd 过程中加入确定并显示可用目录的代码来实现的:

  1. 该过程创建并初始化 CatalogSet 对象,然后调用其 GetCatalogsForUser 方法,传递 MSCSProfileService 应用程序级变量引用的 ProfileService 对象、当前用户(可能是匿名用户)的 ID 以及要使用的默认目录集名(如果未给用户指派特定目录集):

    Set objCatalogSets = _
    Server.CreateObject(mc_strCatalogSets)
    Call objCatalogSets.Initialize _
    (Application("MSCSDictConfig"). _
    s_CatalogConnectionString, _
    Application("MSCSDictConfig")._
    s_TransactionsConnectionString)
    Set rsCatalogs = objCatalogSets.GetCatalogsForUser( _
    Application("MSCSProfileService"), GetUserID & "", _
    GetDefaultCatalogSet)
    
  2. 这将返回一个 ADO 记录集对象,可以使用标准记录集编程技术(例如检查文件结束 (EOF) 标记和使用 MoveNext 方法)浏览此对象:
    Do While Not rsCatalogs.Eof
    Call XMLBegTag(c_strCatalog)
    Call getXMLFromRSwithdsplynm(rsCatalogs)
    Call XMLEndTag(c_strCatalog)
    rsCatalogs.MoveNext
    Loop
    
  3. 使用 UI_layout-IE5.xsl 样式表呈现该代码生成的 XML 时,所得到的网页将目录集中每个目录的名称显示为到 Category.pasp 的链接。生成的 XML 段类似于以下内容:
      <catalog>
    <catalogname>书籍</catalogname>
    </catalog>
    <catalog>
    <catalogname>硬件</catalogname>
    </catalog>
    

目录浏览功能

ConsolidatedRetail.com 解决方案中的目录是以分层结构实现的。“书籍”和“硬件”这两个目录分别包含若干类别。“书籍”目录还包含一个子类别层。产品可以存储在目录的任何一层上。

ConsolidatedRetail.com 站点中用于浏览目录数据的页是 Category.pasp。可以在两种模式下使用该页:根级模式或类别模式。在根级模式下,该页从指定目录的根来检索产品和类别。在类别模式下,该页从指定的类别来检索产品、子类别和父类别。

该页包含执行以下任务的代码:

  1. 使用 MSCSAppFrameWork 对象检索请求字符串中传递的 txtCatalogtxtCategory 值。如果未找到 txtCatalog 值,则该页将用户重定向到 Index.pasp。
  2. 调用 PageStart 生成页的 XML 标头。
  3. MSCSCatalogManager 应用程序变量检索指定目录的 ProductCatalog 对象,并将目录名写入 <searchscope> XML 元素。
  4. 将目录属性作为 XML 呈现。这样,可以在用户界面中呈现诸如目录名这样的属性。
  5. 确定是否在请求字符串中传递类别名。如果未指定类别名,则该页从目录的根来检索类别和产品并将其转换为 XML 格式。如果提供了类别,则该页从提供的类别来检索数据并将数据转换为 XML 格式。
  6. 调用 PageEnd 过程来关闭 XML 文档。

在使用 Commerce Server 目录对象检索目录信息前,该页使用以下代码来创建 <searchscope> 元素:

Call XMLTag(c_strSearchScope, strCatalogName)

UI_layout-IE5.xsl 样式表使用此元素将当前目录名传递到搜索功能,然后限定搜索范围。(本章稍后将对搜索功能进行详细讨论。)

实际目录数据是使用 Commerce Server 自动化对象的分层结构进行检索的。分层结构的顶层是 CatalogManager 对象,用于对目录系统进行所有程序访问。CatalogManager 对象包含一些 ProductCatalog 对象,这些对象代表站点中的目录。在 Category.pasp 中,MSCSCatalogManager 应用程序级变量的 GetCatalog 方法用于检索指定的目录,如以下代码段所示:

Set objMSCSPrdCat = Application("MSCSCatalogManager"). _
GetCatalog(strCatalogName)

使用 GetCatalogAttributes 方法,可以将 ProductCatalog 对象的属性作为 ADO 记录集进行检索。Category.pasp 中的代码使用此方法将记录集中的每一行传递到 Common.asp 中的 GetXMLFromRSWithDsplyNm 例程,该例程将行转换为 XML 格式:

Set rsProperties = _
objMSCSPrdCat.GetCatalogAttributes
If Not (rsProperties.Eof And rsProperties.Bof) Then
Call XMLBegTag(c_strGetCatalogAttributes)
Do While Not rsProperties.Eof
'获取记录集行的 xml 版本
Call GetXMLFromRSWithDsplyNm(rsProperties)
rsProperties.MoveNext
Loop
Call XMLEndTag(c_strGetCatalogAttributes)
End If

这将生成以下格式的 XML 代码段:

<getcatalogattributes>
<catalogname>书籍</catalogname>
<locale>8</locale>
<startdate>12/8/1999</startdate>
<enddate>12/8/2006</enddate>
<variantid>ISBN</variantid>
<productid>标题</productid>
<currency>USD</currency>
<weightmeasure>lbs</weightmeasure>
<catalogid>1</catalogid>
<customcatalog>False</customcatalog>
<freetextindexcreated>2/8/2001 11:56:22 PM</freetextindexcreated>
<producttableupdated>2/8/2001 11:53:37 PM</producttableupdated>
</getcatalogattributes>

也可以使用记录集对象来代表目录根中的类别。RootCategories 方法用于从 Category.pasp 中检索这些类别,如以下代码段所示(请注意,Fields 集合属性用于从记录集中检索指定的数据字段):

Set rsCategories = objMSCSPrdCat.RootCategories
'为了表达得更为清楚,此处省略了一些代码
Do While Not rsCategories.Eof
Call XMLBegTag(c_strRootCategory)
Call XMLTagWithDsplyNm("catalogname", objMSCSPrdCat.catalogname)
Call XMLTagWithDsplyNm("categoryname", _
rsCategories.fields("CategoryName").Value)
Call XMLEndTag(c_strRootCategory)
rsCategories.MoveNext
Loop

这将生成一个 XML 代码段,如下所示:

  <rootcategory>
<catalogname>书籍</catalogname>
<categoryname>商业软件</categoryname>
</rootcategory>
<rootcategory>
<catalogname>书籍</catalogname>
<categoryname>开发工具</categoryname>
</rootcategory>
<rootcategory>
<catalogname>书籍</catalogname>
<categoryname>特色产品</categoryname>
</rootcategory>
...

与之类似,对于目录根中的产品,可以使用 RootProducts 方法对包含这些产品的记录集进行检索:

Set rsProducts = objMSCSPrdCat.RootProducts

rsProducts 记录集中的每个产品生成的 XML 类似于以下代码:

如果代码需要下钻到更深的目录并检索其中某个类别的内容,可以使用 Category 对象。Category.pasp 页使用 Category 对象访问指定类别的内容。该对象使用 ProductCatalog 对象的 GetCategory 方法进行实例化,如以下代码段中所示:

Set objMSCSCategory = _
objMSCSPrdCat.GetCategory(strCategoryName)

您可以使用 Products 属性将类别中的产品作为记录集检索:

Set rsProducts = objMSCSCategory.products

Category 对象还提供 ChildCategories 属性来检索子类别的记录集,提供 ParentCategories 属性返回父类别的记录集。

无论它们在分层结构中的位置如何,您都可以使用 Commerce Server Business Desk 来创建产品目录中类别和产品间的关系。Category 对象提供 RelatedCategoriesRelatedProducts 属性来检索包含相应内容的记录集。您在 Category.pasp 中可以看到有关如何使用这些对象的示例。

查看产品

用于呈现目录数据的 Category-IE5.xsl 样式表会生成 HTML,这样,在用户单击特定产品的“获取详细资料”链接时,将请求 Product.pasp 页。该页将显示所选产品的特定数据。

Product.pasp 中的代码开头部分与 Category.pasp 很相似。该代码在请求字符串中检索目录、产品 ID 和可选的产品变量值。如果没有目录或产品 ID 参数,则将用户重定向到 Index.pasp。然后,代码调用 PageStart 开始为该页生成 XML。

使用 GetProduct 方法从目录对象检索 Commerce Server 产品对象时,代码变得有趣起来:

Set objMSCSPrd = objMSCSPrdCat.GetProduct(strProductID)

您可以使用 GetProductProperties 方法在记录集对象中检索产品的一些属性,如产品名和价格:

Set rsProduct = objMSCSPrd.GetProductProperties

Commerce Server 目录中的产品支持变体(如颜色或大小不同的产品)。您可以使用 Variants 属性将特定产品的变量列表作为记录集检索。此外,还可以使用 RelatedProductsRelatedCategories 属性来检索任何相关的产品或类别。利用这些属性可以创建链接,获得交叉销售机会。Product.pasp 页中使用所有这些属性来为产品生成 XML 数据,下面列出了一段 XML 代码。然后使用 Product-IE5.xsl 样式表来呈现这些数据。

搜索目录

除了提供用户用于浏览目录的页面之外,一个高效的站点还应提供搜索功能。在 ConsolidatedRetail.com 站点中,用户可以输入搜索标准,在目录中搜索特定产品。

使用 Commerce Server 2000 中的搜索功能可以在当前目录中进行搜索,也可以在基于当前用户的目录集中进行搜索。搜索功能是在 SearchResults.pasp 中实现的,SearchResults.pasp 包含执行以下任务的代码:

  1. 从查询字符串中检索搜索短语、目录、要返回的行数和开始位置参数(根据需要为返回的行数和开始位置参数赋予默认值,并在未提供搜索短语的情况下产生异常)。
  2. 如果未指定目录,则检索基于当前用户配置文件的目录集。
  3. 在指定目录或用户目录集的目录列表中搜索指定的搜索短语。
  4. 将搜索结果作为 XML 呈现。

搜索目录的实际代码使用应用程序级 MSCSCatalogManager 对象的 FreeTextSearch 方法来检索记录集中的搜索结果。此方法接受以下参数:

  • 搜索短语
  • 要搜索的目录列表(以逗号分隔)
  • 应返回的属性列表(以逗号分隔)
  • 作为结果排序依据的属性列表
  • 表示按升序进行排序的布尔值
  • 开始进行搜索的记录号
  • 要返回的号码或行数
  • 输出参数,表示实际返回的总行数

如果用户未指定目录,将把当前用户的默认目录集作为记录集进行检索,并将每个目录名连接成以逗号分隔的字符串:

Set objCatalogSets = Server.CreateObject(mc_strCatalogSets)
Call objCatalogSets.Initialize _
(Application("MSCSDictConfig").s_CatalogConnectionString, _
Application("MSCSDictConfig").s_TransactionsConnectionString)
Set rsCatalogs = objCatalogSets.GetCatalogsForUser _
(Application("MSCSProfileService"), GetUserID & "", _
GetDefaultCatalogSet)
strCatalogsToSearch = ""
If Not (rsCatalogs.EOF And rsCatalogs.BOF) Then
Do While Not rsCatalogs.EOF
strCatalogsToSearch = strCatalogsToSearch & "," & _
rsCatalogs.Fields("CatalogName").Value & ""
rsCatalogs.MoveNext
Loop
strCatalogsToSearch = Trim(Mid(strCatalogsToSearch, 2))
End If

或者,如果指定了目录,只需将目录名赋给 strCatalogsToSearch 变量即可:

strCatalogsToSearch = strCatalogName

最后,调用 FreeTextSearch 方法:

Set rsProducts = _
Application("MSCSCatalogManager").FreeTextSearch _
(strSearchPhase, strCatalogsToSearch, , _
"CatalogName, CategoryName, DefinitionName, _
OriginalPrice, cy_list_price, i_ClassType,
ProductID, Description, image_filename, _
image_width, image_height, Name", _
"i_ClassType, CatalogName", _
True, _
lngSearchStartPos, _
lngSearchRowToReturn, _
lngTotalRecordsInQuery)

该页上其余的代码只是将 FreeTextSearch 返回的记录集中的行转换为 XML 格式,这样,XSLISAPI 应用程序就可以呈现搜索结果,以便进行显示。为搜索结果生成的 XML 具有以下格式:

“购物篮”管理

与大多数 B2C 站点一样,ConsolidatedRetail.com 解决方案使用购物车或购物篮的概念将用户选定要购买的产品集中在一起。无论是已登录的用户还是匿名用户,ConsolidatedRetail.com 都允许他们向购物篮添加产品;但是匿名用户在结帐前必须登录。

向购物篮中添加产品

当用户在 Product.pasp 页上单击“添加到购物车”链接时,会将产品和数量信息发送给 _additem.asp 页。在将用户重定向到显示购物篮内容的 Basket.pasp 页之前,该页包含执行以下任务的代码:

  1. 检索查询字符串中记录的类别、产品、变量和目录值。
  2. 校验在查询字符串中传递的数量值(如果未传递数量,则添加项目的 1 个实例)。
  3. 将用户的购物篮载入一个 OrderGroup 对象。
  4. 如果产品已在购物篮中列出,则将指定的数量添加到现有条目;否则为此产品创建新条目。
  5. 将用户重定向到 Basket.pasp。

_additem.asp 页调用 Basket.asp 头文件中的 LoadBasket 函数来检索包含当前用户购物篮的 OrderGroup 对象。LoadBasket 函数类似于以下代码:

Function LoadBasket(strUserID)
Dim objMSCSOrderGroup
Set objMSCSOrderGroup = Server.CreateObject( _
mc_strOrderGroup)
Call  objMSCSOrderGroup.Initialize(Application( _
"MSCSDictConfig").s_TransactionsConnectionString, _
strUserID)
Call objMSCSOrderGroup.LoadBasket()
Set LoadBasket = objMSCSOrderGroup
Set objMSCSOrderGroup = Nothing
End Function

请注意:OrderGroup 对象是通过传递 MSCSDictConfig 应用程序变量中定义的事务连接字符串和当前用户的用户 ID 来创建和初始化的。(用户 ID 取自 Profile.asp 头文件中的 GetGuaranteedUserID 函数。对于已登录的用户,将返回通过身份验证的用户 ID,对于匿名用户,则返回配置文件用户 ID。)

然后,调用 OrderGroup 对象的 LoadBasket 方法来检索与当前用户相关联的购物篮内容。

加载购物篮后,_additem.asp 中的代码搜索购物篮中的明细项目,查看是否列出了请求的产品。如果产品已在购物篮中,则在订单上增加指定的数量,如下所示:

blnSkuMatched = False
'如果明细项目存在
If objMSCSOrderGroup.Value("total_lineitems") > 0 Then
'循环搜索每个明细项目,查找匹配项
For Each colItem in objMSCSOrderGroup.Value _
("OrderForms").Value("default").Items
If IsNull(strVariantID) Then
If (Trim(Cstr(colItem.product_id)) = _
Trim(Cstr(strProductID))) And _
IsNull(colItem.product_variant_id) Then
blnSkuMatched = True
Exit For
End If
Else
If (Trim(Cstr(colItem.product_id)) = _
Trim(Cstr(strProductID))) And _
Trim(Cstr(colItem. _
product_variant_id)) = _
Trim(Cstr(strVariantID)))  Then
blnSkuMatched = True
Exit For
End If
End If
Next
If blnSkuMatched Then
' 如果项目已在购物篮中,则将新的项目数量加到
' 现有项目数量上
colItem.Quantity = colItem.Quantity + intProductQty
'保存新的购物篮
Call objMSCSOrderGroup.SaveAsBasket()
...

如果产品未在购物篮中列出,则代码调用 AddItemToBasket 局部函数,将其添加到购物篮中。AddItemToBasket 函数创建一个字典对象来表示该项目,并使用 OrderGroup 对象的 AddItem 方法将该项目添加到购物篮中。然后,使用 SaveAsBasket 方法保存购物篮。AddItemToBasket 函数的代码类似于以下代码:

Function AddItemToBasket(objMSCSOrderGroup, _
strProductID, _
strCatalogName, intProductQty, _
strVariantID, strCategoryName)
Dim objMSCSProductDictionary
'创建产品字典
Set objMSCSProductDictionary = _
Server.CreateObject(mc_strDictionary)
objMSCSProductDictionary.lineitem_uid = GenerateGUID()
objMSCSProductDictionary.product_id = strProductID
objMSCSProductDictionary.product_catalog = strCatalogName
objMSCSProductDictionary.Quantity = intProductQty
If Not IsNull(strVariantID) Then
objMSCSProductDictionary.product_variant_id = _
strVariantID
End If
If Not IsNull(strCategoryName) Then
objMSCSProductDictionary.product_category = _
strCategoryName
End If
Call objMSCSOrderGroup.AddItem(objMSCSProductDictionary)
Call objMSCSOrderGroup.SaveAsBasket()
Set objMSCSProductDictionary = Nothing
End Function

请注意:添加项目是通过创建一个表示该项目的字典对象,然后将该项目传递给 OrderGroup(表示购物篮)的 AddItem 方法来实现的。

更新购物篮后,_additem.asp 中的代码将用户重定向到 Basket.pasp。

查看购物篮

用户可以使用 Basket.pasp 页查看和编辑购物篮的内容。Basket.pasp 包含执行以下任务的代码:

  1. 检查购物篮的完整性。
  2. 检索用户的用户 ID(从身份验证单或匿名配置文件单中检索)。
  3. 如果用户的购物篮非空,则使用 PAGBasket 管道来检索要显示的购物篮信息。
  4. 将明细项目总计数写入 <totallineitems> XML 标记。
  5. 使用 Commerce Server DictionaryXMLTransforms 对象将购物篮内容转换为 XML,并将其写入响应。

该页通过使用以下代码加载用户的购物篮并检查购物篮中是否包含产品:

Set objMSCSOrderGroup = LoadBasket(strUserID)
'检查购物篮中是否包含项目
If Not IsBasketEmpty(objMSCSOrderGroup) Then
blnBasketIsEmpty = False
End If

Include/basket.asp 中的 IsBasketEmpty 函数检查 OrderGroup 中是否包含项目。如果购物篮包含产品,则将购物篮传递给 PAGBasket 管道以便为显示做准备:

Set objMSCSPipelines = Application("MSCSPipelines")
intErrorLevel = _
RunMtsPipeline(objMSCSPipelines.PAGBasket, _
objMSCSPipelines.LogFolder & strUserID & _
".log", _
objMSCSOrderGroup)

PAGBasket 管道

管道用于配置一系列组件,这些组件将以固定顺序对一个业务对象进行操作。管道的操作分为几个“阶段”。在本示例中,PAGBasket 管道包含的组件对 OrderGroup 对象进行操作,该对象表示用户的购物篮。您可以使用 Commerce Server 管道编辑器来查看 PAGBasket 管道的配置,只需要打开站点上“管道”文件夹中的 PAGBasket.pcf 文件即可。PagBasket 管道如图 7-2 所示。

图 7-2:PAGBasket 管道

每次显示购物篮时,PAGBasket 管道收集显示购物篮所需的所有数据并进行必要的计算。除了在显示购物篮之前运行该管道之外,在结帐过程的最后阶段也要运行它,以便进行必要的计算来创建最终订单总计。

产品信息阶段

该管道从“产品信息”阶段开始执行。此阶段用于管理产品信息,涉及以下两个组件:

  • QueryCatalogInfoQueryCatalogInfo 组件为订单中的每个项目从目录系统中检索产品信息。它将检索到的信息添加到订单表单中的每个项目字典。
  • RequiredProdInfoRequiredProdInfo 组件检查 OrderFormitems 集合的所有项目,并删除 delete 键设置为 1 的所有项目。

订单初始化阶段

然后进入“订单初始化”阶段,初始化 OrderGroup 中的相应值。此阶段只涉及一个组件:RequiredOrderInitCy。首先,RequiredOrderInitCy 确保 order_id 键有值。如果没有值,RequiredOrderInitCy 将生成一个唯一订单 ID,并将其赋给此键。然后,为了确保订单完整性,该组件将把 NULL 值赋给各 total 键,将其初始化。最后,对于 items 集合中的每个项目,RequiredOrderInitCy 将存储在 quantity 中的值复制到 _n_unadjusted 键,初始化未打折的项目数量,并将 _oadjust_adjustedprice (项目的总费用)初始化为零。

订单检查阶段

“订单检查”阶段校验显示的订单是否有效以及是否包含后续处理所需的所有条目。此阶段只涉及一个组件:RequiredOrderCheckRequiredOrderCheck 组件确保 OrderForm 的项目列表不为空。

项目定价阶段

“项目定价”阶段为订单表单中的每个项目设置 _iadjust_regularprice。此阶段涉及以下两个组件:

  • DefaultItemPriceCy:对于订单表单中的每个项目(即 items 集合中的每个项目),DefaultItemPriceCy_iadjust_regularprice 键指派给 the _cy_product_list_price 中存储的值。管道中“项目定价”阶段后面的各个阶段所包含的一些组件依赖于所设置的 _iadjust_regularprice 键。如果未给此键赋值,则这些组件将失败。
  • RequiredItemPriceCyRequiredItemPriceCy 组件校验是否为项目列表中的每个项目设置了 _cy_iadjust_regularprice。在运行 RequiredItemPrice 组件前,应使用 DefaultItemPriceCy 组件将项目列表初始化,以便包含项目的最新定价信息。如果 _cy_iadjust_regularprice 不包含值,将产生错误。

项目调价阶段

“项目调价”阶段为订单表单中的每个项目设置 _iadjust_currentprice。此阶段只涉及一个组件:RequiredItemAdjustPriceCyRequiredItemAdjustPriceCy 校验项目列表中每个项目的当前价格 (_cy_iadjust_currentprice) 是否存在。如果此值不存在,则该组件创建它并将它初始化为常规价格 (_cy_iadjust_regularprice)。此外,该组件对照当前价格 (cy_iadjust_currentprice) 来检查放置价格 (cy_placed_price),看看自用户将产品放入购物篮后当前价格是否发生了更改。如果放置价格不存在,该组件创建它并将它设置为当前价格 (cy_iadjust_currentprice)。如果放置价格存在但是不等于当前价格,则 RequiredItemAdjustPriceCy 从 MessageManager 中检索无效放置价格的警告消息文本,并将消息写入订单表单中的 _Basket_Errors 集合。

订单调价阶段

“订单调价”阶段为订单表单中的每个项目设置 _oadjust_adjustedprice,以便将价格折扣的因素考虑进去。此阶段只涉及以下一个组件: RequiredOrderAdjustPriceCy:对于每个项目,RequiredOrderAdjustPriceCy 首先计算不打折数量的费用。方法是:将项目的当前价格 (_cy_iadjust_currentprice) 与项目不打折的数量 (n_unadjusted) 相乘,然后将乘积加到项目的总费用 (_cy_oadjust_adjustedprice) 上。然后,计算项目折扣 (cy_oadjust_discount)。方法是:用项目的当前价格 (_cy_iadjust_currentprice) 乘以总数量 (quantity) 得出项目的总费用,然后减去以前算出的不打折的项目费用,这样就计算出了项目折扣。

订单小计阶段

“订单小计”阶段在订单表单上设置 _oadjust_subtotal。此阶段涉及以下三个组件:

  • DefaultOrderSubTotalCy:将 OrderFormSimpleList 项目中的每个项目总费用 (_cy_oadjust_adjustedprice) 加起来,计算出小计值 (_cy_oadjust_subtotal)。
  • PersistUtilityPersistUtility 是一个自定义组件,用于复制 OrderForm 中的值。它主要用来使值持久化。否则,名称以 _ 开头的 OrderForm 值将不会持久保存。PersistUtility 组件的源代码是用 Visual C++ 编写的,随商务参考体系结构一起提供。
  • RequiredOrderSubTotalCyRequiredOrderSubtotalCy 检查OrderForm 中的 _oadjust_subtotal 键,确保赋给该键的值不是 NULL。

将购物篮内容转换为 XML

运行管道后,Basket.pasp 中的代码再次检查 OrderGroup 对象,以确保该对象仍包含一些项目(因为某些项目可能已在管道中删除)。如果购物篮包含项目,则使用 Commerce Server DictionaryXMLTransforms 对象将内容转换为 XML 并将其写入 Response 对象。DictionaryXMLTransforms 对象生成的 XML 类似于以下代码:

然后,使用 Basket-IE5.xsl 样式表将购物篮页作为 HTML 呈现。

订单处理

用户决定结帐时,必须执行以下步骤:

  1. 为订单指定收货人地址(或选择将不同项目发送到不同地址)。
  2. 选择发货方法。
  3. 提供订单的付款信息。
  4. 确认订单详细信息。
  5. 完成订货过程。

ConsolidatedRetail.com 站点使用 Commerce Server 对象和管道组件来处理这些流程。

指定收货人地址

用户通常使用 Shipping.pasp 来指定收货人地址。该页包含一个表单,该表单列出了用户配置文件中的所有收货人地址,还带有创建新地址或编辑现有地址的选项。用户只需要指定项目要发往的地址即可。然后,该页将表单发回给自身,用指定地址更新表示购物篮内容的 OrderGroup 对象。

用户指定收货人地址时,Shipping.pasp 中的代码使用 Common.asp 头文件中的 GetUserID 函数来检索当前用户的 ID。然后,Shipping.pasp 调用 Basket.asp 头文件中的 LoadBasket 例程填充 OrderGroup 对象,如下所示:

strUserID = GetUserID
Set objMSCSOrderGrp = LoadBasket(strUserID)

接着,代码从查询字符串中检索提供的地址 ID,用指定的地址 ID 更新购物篮中的每个项目:

<orderform
For Each strOrderFormName In objMSCSOrderGrp.Value.OrderForms
Set objMSCSOrderForm = objMSCSOrderGrp.Value.OrderForms _
(strOrderFormName)
For each colItem in objMSCSOrderForm.Items
colItem.value("shipping_address_id") = strAddressID
Next
Next

最后,设置整个 OrderGroup 的收货人地址;对于以前设置的地址,如果没有用到则将其删除;然后保存购物篮。将用户重定向到 ShippingMethod.pasp,为订单指定发货方法:

Call objMSCSOrderGrp.SetShippingAddress(strAddressID)
Call objMSCSOrderGrp.SaveAsBasket()
Set objMSCSOrderGrp = Nothing
Response.Redirect "ShippingMethod.pasp"

但是,如果用户是首次访问 Shipping.pasp 页,则必须从用户配置文件中检索可能的地址列表以便在表单中显示该列表。Shipping.pasp 中的代码使用 ADO 查询在站点数据库的地址表中进行检索,如下所示:

如果用户的配置文件中未列出地址,则使用 mode 参数将用户重定向到 EditAddressBook.pasp 页。该参数在添加地址后将用户返回到此页:

If rsAddress.EOF Then    '没有为该用户 ID 输入任何地址
rsAddress.Close
Set rsAddress = Nothing
cnBizDesk.Close
Set cnBizDesk = Nothing
Response.Redirect "EditAddressBook.pasp?Mode=" & mc_strPageName
End If

如果用户配置文件包含一个或多个地址,则会将地址信息添加到购物篮(指定的最后一个地址是默认收货人地址)并将其作为 XML 呈现:

Call XMLBegTag(mc_strAddresses)
Do While Not rsAddress.EOF
strAddressID = rsAddress.Fields("address_id").value
strDescription = rsAddress.Fields("description").value
strAddressName = rsAddress.Fields("address_name").value
strAddressLine1 = rsAddress.Fields("address_line1").value
strAddressLine2 = rsAddress.Fields("address_line2").value
strCity = rsAddress.Fields("city").value
strRegionCode = rsAddress.Fields("region_code").value
strRegionName = rsAddress.Fields("region_name").value
strPostalCode = rsAddress.Fields("postal_code").value
blnSaveSuccessful = PutOrderAddress(objMSCSOrderGrp, _
mc_lngShippingAddress, strAddressID, strAddressName, _
strAddressLine1, strAddressLine2, strCity, strRegionName, _
strPostalCode, strDescription)
If blnSaveSuccessful Then
Call XMLBegTag(mc_strAddress)
Call XMLTag(mc_strAddress_ID, strAddressID)
Call XMLTag(mc_strDescription, strDescription)
Call XMLTag(mc_strAddress_Name, strAddressName)
Call XMLTag(mc_strAddress_Line1, strAddressLine1)
Call XMLTag(mc_strAddress_Line2, strAddressLine2)
Call XMLTag(mc_strCity, strCity)
Call XMLTag(mc_strRegion_Code, strRegionName)
Call XMLTag(mc_strRegion_Name, strRegionName)
Call XMLTag(mc_strPostal_Code, strPostalCode)
Call XMLEndTag(mc_strAddress)
'设置付款地址
If rsAddress.Fields("address_type").value = 2 Then
objMSCSOrderGrp.Value("OrderForms").Value _
("default").Value("billing_address_id") = strAddressID
End If
End If
rsAddress.MoveNext
Loop
Call XMLEndTag(mc_strAddresses)
End If

此代码所生成的 XML 格式如以下代码所示:

<addresses>
<address>
<address_id>{0243FFF8-E633-4DE4-AA2E-2083E9D5ABB4}</address_id>
<description>家庭地址</description>
<address_name>Kim Abercrombie</address_name>
<address_line1>我的住宅号</address_line1>
<address_line2>我所在的街道</address_line2>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
<address>
<address_id>{B87EDB27-8FBE-4BDA-83BE-ED037A0B7E4C}</address_id>
<description>工作地址</description>
<address_name>Kim Abercrombie</address_name>
<address_line1>Microsoft Corp.</address_line1>
<address_line2>1 Microsoft Way</address_line2>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>54321</postal_code>
</address>
</addresses>

指定发货方法

用户为订单指定收货人地址后,必须选择发货方法。Commerce Server 2000 支持配置多种发货方法,每个方法的价格范围各不相同,这取决于重量、项目数量或订单总费用。(有关定义发货方法的详细信息,请参考 Commerce Server 2000 文档。)

使用 ShippingMethod.pasp 页,用户可以从站点数据库中定义的众多发货方法中选择一个。该页包含一个表单,将各种发货方法作为选项列出。用户选择了一个选项后,将把所作的选择发回该页,进行处理。从应用程序级 ShippingMethodsXML 变量检索发货方法,以便显示。

可用发货方法列表是从应用程序级 ShippingMethodsXML 变量检索的,使用 Global.asa 中的 GetShippingMethodsXML 函数将该变量初始化,如下所示:

ShippingMethod.pasp 页包含可用发货方法列表,如以下 XML 格式所示:

<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-003688009465}
</shipping_method_id>
<shipping_method_name>快递</shipping_method_name>
</shipping_method>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-001140002642}
</shipping_method_id>
<shipping_method_name>标准</shipping_method_name>
</shipping_method>

用户选择发货方法后,必须将该方法的 ID 和名称存储在 OrderGroup 中。由于从表单传递的只是 ID,因此必须使用 Commerce Server ShippingMethodManager 对象从数据库中检索名称。此对象提供 GetInstalledMethodList 方法,使用该方法可以将匹配指定标准的方法作为记录集进行检索。根据提供的 ID 检索相关方法名称的代码类似于以下内容:

Set objMSCSShpMthMgr = _
Server.CreateObject(mc_strShippingMethodManager)
objMSCSShpMthMgr.Initialize(Application("MSCSDictConfig"). _
s_BizDataStoreConnectionString)
set rsShpMthName = objMSCSShpMthMgr.GetInstalledMethodList _
("shipping_method_id = '" &
strShippingMethodID & "'", "", _
Array(mc_strshipping_method_name))
set objMSCSShpMthMgr = nothing
If Not (rsShpMthName.EOF and rsShpMthName.BOF) then
strShippingMethodName = rsShpMthName.Fields(0).Value
End If
rsShpMthName.Close
Set rsShpMthName = Nothing

检索到发货方法名称后,将使用发货方法数据更新购物车中的项目,并将用户重定向到 Payment.pasp 以指定付款信息:

For Each strOrderFormName In objMSCSOrderGrp.Value.OrderForms
Set objMSCSOrderForm = objMSCSOrderGrp.Value. _
OrderForms(strOrderFormName)
For each colItem in objMSCSOrderForm.Items
colItem.value(mc_strshipping_method_id) = strShippingMethodID
colItem.value(mc_strshipping_method_name) = strShippingMethodName
Next
Next
Call objMSCSOrderGrp.SaveAsBasket()
Set objMSCSOrderGrp = Nothing
Response.Redirect "Payment.pasp"

指定多个收货人地址和发货方法

用户可以为购物车中的每个项目指定不同的收货人地址和发货方法。支持此功能的代码位于 MultiShipping.pasp 页中。该页包含一组 txtShippingMethodIDtxtAddressID 输入字段(购物篮中的每个项目都有一个输入字段)。

用户请求每个项目的发货方法和地址时,将窗体发回 MultiShipping.pasp,同时更新代表购物篮的 OrderGroup。以下代码用于更新每个项目的发货方法和地址数据:

' 用发货方法 ID 填充每个项目
For Each strOrderFormName In objMSCSOrderGrp.Value.OrderForms
Set objMSCSOrderForm = _
objMSCSOrderGrp.Value.OrderForms(strOrderFormName)
For each colItem in objMSCSOrderForm.Items
intIndex = intIndex + 1
strShippingMethodID = _
Request.Form("txtShippingMethodID").Item(intIndex)
strShippingMethodName = Null
If Not IsNull(strShippingMethodID) Then
' 发货方法名称
Set objMSCSShpMthMgr = Server.CreateObject _
(mc_strShippingMethodManager)
objMSCSShpMthMgr.Initialize (Application("MSCSDictConfig"). _
s_BizDataStoreConnectionString)
Set rsShpMthName = objMSCSShpMthMgr.GetInstalledMethodList _
("shipping_method_id = '" & strShippingMethodID & _
"'", "", Array(mc_strshipping_method_name))
Set objMSCSShpMthMgr = nothing
If Not (rsShpMthName.EOF and rsShpMthName.BOF) then
strShippingMethodName = rsShpMthName.Fields(0).Value
else
strShippingMethodID = Null
End If
rsShpMthName.Close
Set rsShpMthName = Nothing
End If
strAddressID  = Request.Form("txtAddressID").Item(intIndex)
colItem.value(mc_strshipping_method_id) = strShippingMethodID
colItem.value(mc_strshipping_method_name) = strShippingMethodName
colItem.value("shipping_address_id") = strAddressID
Next

指定付款信息

作为结帐流程的一部分,用户必须为订单指定付款详细信息。Payment.pasp 页按订单处理顺序处理这一事项。

注意:  在本应用程序示例中,付款详细信息是使用 HTTP 以纯文本的形式进行传输的。在实际站点中,应使用 HTTPS 对付款数据进行加密。

Payment.pasp 页包含一个表单,允许用户从配置文件指定信用卡和付款地址详细信息。如果配置文件中未定义付款地址,则将用户重定向到可以添加此地址的 EditAddressBook.pasp 页。Payment.pasp 页读取该表单中的付款详细信息,然后将信息写入代表用户购物篮的 OrderGroup 对象。

strUserID = GetUserID
Set objMSCSOrderGrp = LoadBasket(strUserID)
For Each strOrderFormName In objMSCSOrderGrp.Value.OrderForms
Set objMSCSOrderForm = objMSCSOrderGrp.Value. _
OrderForms(strOrderFormName)
' 付款方法 - 站点只支持信用卡
objMSCSOrderForm.value("payment_method") = "credit_card"
' 信用卡全称
objMSCSOrderForm.value("cc_name") = strPaymentMethod
' 信用卡内部代码
objMSCSOrderForm.value("cc_code") = strPaymentMethod
' 信用卡持有人姓名
objMSCSOrderForm.value("cc_Account_Holder") = strNameOnAcct
' 信用卡号
objMSCSOrderForm.value("cc_number") = strCardNo
' 信用卡到期月份
objMSCSOrderForm.value("cc_expmonth") = strCardExpMth
' 信用卡到期年份
objMSCSOrderForm.value("cc_expyear") = strCardExpYr
Next
Call objMSCSOrderGrp.SaveAsBasket()
Response.Redirect "OrderSummary.pasp"

填写完付款信息后,用户将被重定向到 OrderSummary.pasp 页。

确认订单详细信息

在进入订单处理的最后一个步骤前,用户还有机会确认或更改订单详细信息。订单详细信息显示在 OrderSummary.pasp 页上。

代码使用两个管道来检索订单详细信息,以便显示该信息。首先,PAGBasket 管道检索购物篮内容。然后,PAGTotal 管道计算装运费用和小计。有关 PAGBasket 管道的说明,请参考本章中的“查看购物篮”一节。PAGTotal 管道将在下一节中介绍。

PAGTotal 管道

PAGTotal 管道如图 7-3 所示。使用 Commerce Server 管道编辑器打开 Total.pcf 就可以进行查看。

图 7-3:PAGTotal 管道

货物分拆阶段

“货物分拆”阶段准备发货字典。该阶段涉及以下两个组件:

  • Commerce.Splitter:分拆组件生成发货字典 (shipments),此字典位于 OrderForm 中。此字典包含要单独发送的货物,它们分别根据订单中的 shipping_address_idshipping_method_id 值来创建。
  • Commerce.ShippingMethodRouterShippingMethodRouter 组件使用 CacheManager 对象读取 ShippingManagerCache,以将发货方法映射到特定的发货组件。该组件浏览发货方法列表,如果存在使用指定方法发送的货物,它将收集这些货物的字典,将货物传递给发货组件,然后运行相关的发货组件。各个发货组件为传来的货物计算总装运费用。处理完发送的所有货物后,将计算总的装运费用。

发货阶段

“发货”阶段将发货折扣应用于订单。该阶段只涉及一个组件:ShippingDiscountAdjust。此组件检查 _shipping_discount_type 并调整订单的总装运费用。如果折扣为空白,则组件不执行任何操作。如果折扣类型为 1 或 2,则组件应用折扣。如果折扣既不为空白也不属于折扣类型 1 或 2,则组件返回一个错误。使用 OrderDiscount 对象确定 _shipping_discount_type 值。

经办阶段

“经办”阶段在订单表单上设置 _handling_total。此阶段涉及以下两个组件:

  • DefaultHandlingCy:此组件将零分配给订单字典中的总经办费用。它是一个占位符组件,实际使用时将被另一个经办组件替代。
  • RequiredHandlingCyRequiredHandlingCy 组件校验 order._handling_total 键是否存在。

税金阶段

“税金”阶段在订单表单上设置 _tax_total_tax_included 值。此阶段涉及以下两个组件:

  • SampleRegionalTax:此组件根据 RegionalTaxCache 中的税金信息将税金字段 (_cy_tax_totalshipments._cy_tax_totalcy_tax_included) 设置为合适的纳税值。
  • RequiredTaxCyRequiredTaxCy 组件校验订单表单中是否存在 _cy_tax_total 和 _cy_tax_included 键。如果某个键不存在,则 RequiredTaxCy 使用 pur_badtax 常量从 MessageManager 中检索错误消息文本,并将消息存储在 OrderForm_Purchase_Errors 集合中。

订单合计阶段

“订单合计”阶段在订单表单上设置 _total_total 值。此阶段涉及以下三个组件:

  • DefaultTotalCy:此组件校验订单上是否设置了全部四个合计项。如果这四个值都存在,将它们加起来并将和值分配给订单字典的最终合计项。如果某些值缺失,组件将停止运行并返回一个错误。
  • PersistUtilityPersistUtility 是一个自定义组件,用于复制 OrderForm 中的值。它主要用来使值持久化。否则,名称以下划线 (_) 开头的 OrderForm 值不会持久化。PersistUtility 组件的源代码是用 Visual C++ 编写的,随商务参考体系结构一起提供。
  • RequiredTotalCyRequiredTotalCy 组件浏览 _Verify_With 字典中的键和值,确保每个键在订单表单中存在并具有相同的值。

修正阶段

最后一个阶段是“修正”阶段,它只涉及一个组件:Truncate Description。在 Commerce Server 2000 中,任何长度超过 255 个字符的字段将被归于 Text 类型;而此组件是用 VBScript 编写的,提供了解决这个问题的方法。

获取用户的电子邮件地址

运行管道后,OrderSummary.pasp 中的代码为当前用户检索 ProfileObject,同时提取电子邮件地址以供订单确认消息使用。然后将地址添加到 OrderGroup 的默认 OrderForm,保存 OrderGroup

'获取默认 orderform
Set objMSCSOrderForm = objMSCSOrderGroup.Value("OrderForms"). _
Value("default")
'设置电子邮件地址以供订单确认消息使用。
Set objMSCSProfileObject = GetUserObject()
objMSCSOrderForm.user_email_address = objMSCSProfileObject _
(mc_strGeneralInfo).Value(mc_strEmail_Address)
Set objMSCSProfileObject = Nothing
Call objMSCSOrderGroup.SaveAsBasket()

最后,使用 Commerce Server DictionaryXMLTransforms 对象将 OrderGroup 的内容转换为 XML 并将其写入 Response 对象,如以下代码所示:

Set objXMLTransforms = _
Server.CreateObject(mc_strXMLTransforms)
Set objXMLOrderForm = _
objXMLTransforms.GenerateXMLForDictionaryUsingSchema _
(objMSCSOrderForm, Application("TransformSchema"))
Set objMSCSOrderForm = Nothing
Set objXMLTransforms = Nothing
If not isEmpty(objXMLOrderForm) Then
Response.Write objXMLOrderForm.xml
Else
Call AddException(m_varrExceptions, "1222", _
"将 orderform 转换为 XML 时出错。", _
"basket.asp")
End if

这将生成用以下 XML 格式表示的 OrderGroup

完成订单处理

用户确认订单详细信息后,订单处理就完成了。有关订单处理的最后一个步骤的代码是在 Thankyou.pasp 页中实现的。

运行以前所述的 PAGBasketPAGTotal 管道,即开始运行 Thankyou.pasp 页了。然后,运行 PAGFinal 管道完成订单处理。

PAGFinal 管道

使用 Commerce Server 管道编辑器打开 Final.pcf,就可以查看 PAGFinal 管道。PAGFinal 管道如图 7-4 所示。

图 7-4:PAGFinal 管道

订购流程中的唯一阶段名为 OrderTransfer。该阶段只涉及一个组件:QueueEmail Class。这是一个自定义管道组件,用于将订单确认消息以电子邮件的形式发送给默认 OrderForm 中的 user_e-mail_address 字段。

发送订单确认电子邮件

QueueEmail Class 是一个管道组件(COM 对象),用于实现 IPipelineComponent 接口。QueueEmail Class 组件从 OrderForm 获取信息,将信息转换为 XML,然后使用自定义的 QueuedEMailer.CMailer 排队组件将信息作为电子邮件消息发送。由于调用了排队组件来发送电子邮件消息,因此这一进程是异步进行的,这样可以防止电子邮件进程导致的延迟对用户结帐的响应时间产生不良影响。

QueueEmail Class 组件的自定义属性页用于在管道编辑器中配置该组件。这将允许您设置相应的消息队列、标记和 ProgID 以便将排队组件实例化,该组件将实际用于发送电子邮件,如图 7-5 所示。

图 7-5:QueueEmail Class 自定义属性页

QueuedEMailer.CMailer

实际发送电子邮件的过程由 QueuedEMailer.Cmailer 排队组件来处理。该组件安装在被标记为排队的 COM+ 应用程序中。此外,由于 _CMailer 接口也被标记为排队,从而允许通过消息队列异步调用该接口上的方法。_CMailer 接口只包含一个方法 (SendMail),该方法是使用以下方法签名来定义的:

HRESULT SendMail(
[in] BSTR strXMLOrderForm,
[in] VARIANT_BOOL blnUseHTMLMail);

请注意以上代码中的两个参数都被标记为 [in],这是因为排队组件不支持[out] 或 [retval] 参数。strXMLOrderForm 参数用于将订单表单的 XML 表示传递给该组件。blnUseHTMLMail 参数值是布尔值,用于确定是以 HTML 格式还是以纯文本格式发送电子邮件消息。

也可以将 QueuedEMailer.CMailer 组件配置为支持对象构建。这意味着该组件可以实现 IobjectConstruct 接口,该接口包含名为 Construct 的方法,当对象实例化时,COM+ 将调用这一方法。将构造函数字符串传递给 Construct 方法,该方法包含对象可以使用的配置信息。传送给 QueuedEMailer.CMailer 的构造函数字符串采用以下 XML 格式:

<config>
<from>support@consolidatedretail.com</from>
<subject>订单确认</subject>
<TextXSL>C:/Inetpub/b2cref/xml/emailtext.xsl</TextXSL>
<HTMLXSL>C:/Inetpub/b2cref/xml/emailhtml.xsl</HTMLXSL>
<SMTPServer></SMTPServer>
<SMTPPort></SMTPPort>
<SMTPTimeout></SMTPTimeout>
<UseSSL>False</UseSSL>
<SMTPUserName></SMTPUserName>
<SMTPPassword></SMTPPassword>
<SMTPAuthMethod></SMTPAuthMethod>
</config>

使用 Component Services Microsoft Management Console (MMC) 管理单元(如图 7-6 所示)来配置此构造函数。

图 7-6:Component Services MMC 管理单元

QueuedEMailer.CMailer 对象使用 MSXML3CDOSYS 对象。XML 和 XSL 的加载是借助使用 MSXML3 对象的 XMLDomDocument 完成的。电子邮件消息的实际发送是由 IMessage 对象(CDO 协作数据对象)完成的,该对象要用到 CDOSYS 对象。用于 Windows 2000 的协作数据对象 (CDO) Cdosys.dll 实现了 CDO API 规范的 2.0 版本,它是一个 COM 组件,专用于简化创建或操纵 Internet 消息的程序编写工作。

总结

现在,您应该已经了解了 ConsolidatedRetail.com 应用程序背后的基本功能和开发策略。文中的代码注释提供了更为详细的信息。

第 8 章 调试和测试

摘要:本章简要介绍调试和测试过程,包括有关调试 ConsolidatedRetail.com 应用程序的通用信息、调试 PASP 脚本输出的说明、测试过程简介以及评估结果的步骤。本章采用的一些具体示例来自开发参考应用程序 ConsolidatedRetail.com 时实际执行的测试活动。

在为基于此参考体系结构的自定义软件编写测试计划时,您可以参考本文档。

简介

和开发其他任何应用程序一样,开发人员的任务是要确保企业对消费者 (B2C) 电子商务应用程序:

  • 正确实现业务功能
  • 性能和可伸缩性达到要求

为了确保应用程序实现目标,您必须深入调试并进行性能测试。

本章第一部分说明调试 ConsolidatedRetail.com 站点的步骤,以及如何查看和调试 PASP 脚本的 XML 输出。然后介绍测试的类型和级别、功能测试过程、性能测试,以及关于评估测试结果的一般性指导。

调试 ConsolidatedRetail.com 站点

调试网站给开发人员带来许多挑战,如果站点中包含 ASP 脚本程序之类的服务器端逻辑,这一点就更为突出。在运行 Microsoft® Windows® 的计算机上创建和调试基于 Web 的应用程序时,首选的开发环境是 Microsoft® Visual Studio® 开发系统,该系统包括了网站开发套件 Microsoft® Visual InterDev®。在网站中添加 Microsoft® FrontPage® Server Extensions,并基于该站点创建一个新的 Visual InterDev 工程,您就可以运用上述环境来调试 B2C 参考体系结构应用程序。

有关使用 Visual InterDev 进行调试的详细信息,请参考 Microsoft® MSDN® 开发人员程序库中的 Visual InterDev 文档。

调试 PASP 脚本的 XML 输出

调试 ConsolidatedRetail.com 站点时,要面对的另一个挑战是如何查看 PASP 脚本生成的 XML 输出。来自这些页面的响应数据流被 XSLISAPI 过滤器截取,然后使用指定的样式表显示。然而,有时候您需要在不应用样式表的请况下检查由脚本生成的 XML。

要查看 XML 输出,最简单的方法是为站点中的各个 *.pasp 文件制作 *.asp 副本,然后用 Microsoft Internet Explorer 访问这些 *.asp 文件。由于 *.pasp 文件的 *.asp 版本不会被 XSLISAPI 应用程序截取,所以 XML 响应数据将返回到浏览器,并可以通过显示结果页的源代码来查看。对于许多脚本,只需指定文件的 URL 即可访问,而对于其他一些脚本,必须在附加到 URL 的查询字符串中传递参数才能访问。下表说明了如何查看站点中各 PASP 文件的 XML 结果。

注意:  附录 A 提供了 ConsolidatedRetail.com 站点中 PASP 页的 XML 输出。

  • Acct.pasp:把该页另存为 Acct.asp,然后导航到 ConsolidatedRetail.com 站点并登录(否则当您尝试查看 Acct.asp 时会被重定向)。这样就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/Acct.asp)来访问 Acct.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • AddressBook.pasp:把该页另存为 AddressBook.asp,然后导航到 ConsolidatedRetail.com 站点并登录(否则当您尝试查看 AddressBook.asp 时会被重定向)。这样就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/AddressBook.asp)来访问 AddressBook.asp。要查看 XML,请单击“查看”菜单上的“源文件”。要获得更有意义的结果,请先使用该站点向您的地址簿中添加至少一个地址。
  • Basket.pasp:把该页另存为 Basket.asp。然后就可以使用 Internet Explorer,通过指定无参数的 URL(其格式为 http://服务器名称:81/Basket.asp)来访问 Basket.asp。要查看 XML,请单击“查看”菜单上的“源文件”。要获得更有意义的结果,请先使用该站点向您的购物篮中添加一些项目。
  • Category.pasp:把该页另存为 Category.asp。然后就可以使用 Internet Explorer,通过指定带有两个参数的 URL 来访问 Category.asp。(这两个参数分别是 txtCatalog 和 txtCategory,前者是您要浏览的目录名,后者是可选项,指定目录中的特定类别名。)例如,您可以通过指定以下 URL 来查看 Books 目录的 XML 表示:

    http://服务器名称:81/Category.asp?txtCatalog=Books

    要查看 Books 目录中的 Games 类别,可以使用以下 URL:

    http://localhost/Category.asp?txtCatalog=Books&txtCategory=Games

    返回相应的页面后,单击“查看”菜单上的“源文件”以查看 XML 代码。

  • Changepasswd.pasp:把该页另存为 Changepasswd.asp,然后导航到 ConsolidatedRetail.com 站点并登录(否则当您尝试查看 Changepasswd.asp 时会被重定向)。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/Changepasswd.asp)来访问 Changepasswd.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • EditAddressBook.pasp:把该页另存为 EditAddressBook.asp,然后导航到 ConsolidatedRetail.com 站点并登录(否则您将看不到任何地址信息)。查看该页所用的 URL 可以包括以下参数:
    • txtAddressType – 地址类型,例如付款地址或发货地址。如果未提供任何值,则使用发货地址。
    • txtAddressID – 地址的全局唯一标识符 (GUID)。如果指定一个值,则返回相应的地址。

    例如,要查看用户添加新发货地址时生成的 XML,请使用 Internet Explorer 导航至以下 URL:

    http://服务器名称:81/EditAddressBook.asp

    要查看用户添加新付款地址时生成的 XML,请使用 Internet Explorer 导航至以下 URL:

    http://服务器名称:81/EditAddressBook.asp?txtAddressType=Billing

    要查看用户编辑特定地址时生成的 XML,请使用 Internet Explorer 导航至以下 URL:

    http://服务器名称:81/EditAddressBook.asp?txtAddressID=地址 GUID

    完成检索该页后,单击“查看”菜单上的“源文件”以查看 XML 代码。

  • ForgotPasswd.pasp:把该页另存为 ForgotPasswd.asp,然后导航到 ConsolidatedRetail.com 站点并登录(否则当您尝试查看 ForgotPasswd.asp 时会被重定向)。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/ForgotPasswd.asp)来访问 ForgotPasswd.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • Index.pasp:把该页另存为 Index.asp。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/Index.asp)来访问 Index.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • Login.pasp:把该页另存为 Login.asp。关闭当前使用 ConsolidatedRetail.com 站点的任何会话(否则当您尝试访问 Login.asp 时会被重定向到 Acct.pasp)。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/Login.asp)来访问 Login.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • Multishipping.pasp:把该页另存为 MultiShipping.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试访问 MultiShipping.asp 时会被重定向到 Login.pasp)。请向您的购物篮中添加至少一个项目(否则当您尝试访问 MultiShipping.asp 时会被重定向到 Basket.pasp)。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/MultiShipping.asp)来访问 MultiShipping.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • OrderHistory.pasp:把该页另存为 OrderHistory.asp。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/OrderHistory.asp)来访问 OrderHistory.asp。要查看 XML,请单击“查看”菜单上的“源文件”。您可以在不登录的情况下查看此页,但为了获得更有意义的数据,在查看此页前,您应该登录到该站点并至少提交一个订单。
  • OrderHistoryDetail.pasp:把该页另存为 OrderHistoryDetail.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试访问 OrderHistoryDetail.asp 时将产生错误)。提交至少一个订单,然后可以使用 Internet Explorer,通过指定有一个参数(即订单,它应该是标识现有订单的 GUID)的 URL 来查看 OrderHistoryDetail.asp(否则将出错)。访问该页所使用的 URL 应该类似于以下示例:

    http://服务器名称:81/OrderHistoryDetail.asp?order= {0FA626B0-852E-4707-93D5-A00619C6A35B}

    完成检索该页后,单击“查看”菜单上的“源文件”以查看 XML 代码。

  • OrderSummary.pasp:把该页另存为 OrderSummary.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试访问 OrderSummary.asp 时将产生错误)。提交一个订单并确认发货地址、发货方式和付费信息。然后导航至 http://服务器名称:81/OrderSummary.asp。单击“查看”菜单上的“源文件”以查看 XML 代码。
  • Payment.pasp:把该页另存为 Payment.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试访问 Payment.asp 时将产生错误)。向购物篮中添加一个项目(否则当您尝试查看 Payment.asp 时会被重定向到 Basket.pasp)。然后导航至 http://服务器名称:81/Payment.asp 以查看 Payment.asp。单击“查看”菜单上的“源文件”以查看 XML 代码。
  • Product.pasp:把该页另存为 Product.asp。然后就可以使用 Internet Explorer,通过指定带两个参数的 URL 来访问 Product.asp 页。这两个参数分别是 txtCatalog 和 txtProductID,前者是您要浏览的目录名,后者是您要查看的产品的 ProductID。还有一个可选项 txtVariantID,是产品的变量 ID。例如,您可以通过指定如下 URL 来查看名为 Code 的图书的 XML 表示方法:

    http://服务器名称:81/Product.asp?txtCatalog=Books&txtProductID=Code

    单击“查看”菜单上的“源文件”以查看 XML 代码。

  • Registration.pasp:把该页另存为 Registration.asp。然后就可以使用 Internet Explorer,通过指定无参数的 URL(格式为 http://服务器名称:81/Registration.asp)来访问 Registration.asp。要查看 XML,请单击“查看”菜单上的“源文件”。
  • SearchResults.pasp:把该页另存为 SearchResults.asp。然后就可以使用 Internet Explorer,通过指定具有四个参数的 URL 来访问 SearchResults.asp。这四个参数分别是:txtSearchPhrase,您要搜索的短语;txtCatalog,您要搜索的目录名;可选项 txtSearchRowsToReturn,您要返回到用户界面 [UI] 上的结果编号;可选项 txtSearchStartPos,您要开始搜索的起始行号。例如,您可以通过指定如下 URL 来在 Books 目录中搜索词语 Age

    http://服务器名称:81/SearchResults.asp?txtSearchPhrase=age&txtCatalog=Books

  • Shipping.pasp:把该页另存为 Shipping.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试查看 Shipping.asp 时会被重定向到 Login.pasp)。提交至少一个订单(否则会被重定向到 Basket.pasp),然后就可以使用 Internet Explorer,通过指定如下 URL 来查看 Shipping.asp:http://服务器名称:81/Shipping.asp

    完成检索该页后,您可以通过单击“查看”菜单上的“源文件”来查看 XML 代码。

  • ShippingMethod.pasp:把该页另存为 ShippingMethod.asp,然后导航至 ConsolidatedRetail.com 站点并登录(否则当您尝试访问 ShippingMethod.asp 时会被重定向到 Login.pasp)。提交至少一个订单(否则会被重定向到 Basket.pasp),然后就可以使用 Internet Explorer,通过指定如下 URL 来查看 ShippingMethod.asp:

    http://服务器名称:81/ShippingMethod.asp

    完成检索该页后,单击“查看”菜单上的“源文件”以查看 XML 代码。

  • ThankYou.pasp:把该页另存为 ThankYou.asp,然后导航至 ConsolidatedRetail.com 站点并登录。提交至少一个订单,按照订购程序进行操作,直到显示 OrderSummary.pasp。然后就可以使用 Internet Explorer,通过指定如下 URL 来查看 ThankYou.asp:

    http://服务器名称:81/ThankYou.asp

    完成检索该页后,单击“查看”菜单上的“源文件”以查看 XML 代码。

  • UserProfile.pasp:把该页另存为 UserProfile.asp,然后导航至 ConsolidatedRetail.com 站点并登录。您可以使用 Internet Explorer,通过指定如下 URL 来查看 UserProfile.asp:

    http://服务器名称:81/UserProfile.asp

    完成检索该页后,单击“查看”菜单上的“源文件”以查看 XML 代码。

制订测试策略

进行测试要有侧重点,因为可能的测试区域很多,每一个测试区域又有不同的测试类型。由于总是存在资源限制(包括时间、人员或资金限制),所以按重要性划分要完成的测试区域以及测试类型和级别是非常重要的,并且这是初步测试计划的重点。

可能的测试区域

以下是测试时需要注意的可能区域:

  • 用户界面 (UI) 测试:这些测试检查表单和一致性。检查内容包括屏幕显示效果(字体、大小、颜色和总体外观),以及该应用程序的所有表单中所有字段的数据确认。这两种测试都应根据软件规范文档进行。
  • 业务逻辑测试:功能规范文档定义了期望在实际运营中实现的业务逻辑。因此,必须用一套测试案例来检查业务逻辑。对于参考体系结构的实施,这一测试过程可以从 UI 或从 Commerce Server BizDesk 实用程序(一个管理模块)来完成。该测试过程应该包括对不同类型的用户和不同站点进入路径的测试。
  • 后端测试:在理想条件下,后端测试应该在数据库中进行。由于参考体系结构使用与 Microsoft® SQL Server™ 2000 表紧密集成的 Microsoft Commerce Server 2000,因此测试小组可以使用 Commerce Server 对象与这些表交互作用。测试小组可以编写占位程序以隔离方式测试 Commerce Server 对象,然后与代码生成的 XML 输出的占位程序的结果进行比较。您也可以在 UI 层进行比较。

可能的测试类型

测试小组可能会执行以下类型的测试:

  • 功能测试确保该系统提供的功能与功能规范文档所述的相符。
  • 回归测试确认当反复执行一系列相同的操作时,应用程序的响应相同。
  • 安全性测试保证只有具有适当权限的用户才可以使用系统中指定的功能。由系统工程师为测试环境中的每个用户建立不同的安全设置。
  • 性能测试确保应用程序在用户可以接受的时间范围内做出响应。
  • 强度测试确认应用程序能适当地对多个用户和同时发生的活动做出响应。用户的数量必须提前约定,系统测试采用的硬件环境必须符合实际运营条件。
  • 自动测试可用于回归和功能测试。如果系统稳定并且不频繁更改,这种测试就很有用。
  • 平台测试确认应用程序在主测试计划中规定的操作系统和浏览器组合中能正确运行。
  • Internet 服务提供商 (ISP) 快速测试确认应用程序能对通过 ISP 连接发出的请求做出响应。
  • 端对端界面测试检查所有输入、输出和系统。该测试确保应用程序与功能规范文档中规定的外部系统能够正确地交互作用。
  • 应用程序重复实例测试确定当客户端运行相同程序的多个副本时是否会导致阻塞或其他问题。
  • 输入和边界测试保证该系统只接受正确的输入。该测试确保输入的字符数不超过字段规定的最大字符数,以及在边界条件下工作正常(例如有效范围和 1 超界、空值、最大值、最小值、屏幕上字段的 Tab 键切换顺序等等)。
  • Windows/Internet GUI 标准测试验证应用程序具有标准的观感。
  • 本地化测试保证应用程序可在不同的语言环境中运行。
  • 欧元兼容性测试保证正确显示欧元。如果应用程序要接收来自欧洲经济与货币联盟 (EMU) 的货币值就要进行该测试。
  • 转换测试检测需要经过转换,应用程序才能正常运行的所有数据。这些转换可能来自于旧系统或新架构所需的变更。
  • 安装/升级测试检测安装/升级程序以确保该产品可以在现有版本的基础上进行安装。测试小组可以决定只测试完整版本,还是同时测试升级安装版本。
  • 易用性测试确保应用程序易于使用,没有过多击键,并且易于理解。执行该测试的最好方法是找一些高级、中级和初级用户,然后听取他们对该应用程序可用性的意见。
  • 随意测试用非结构化的场景来测试系统,确保它能正确做出响应。要实现这一目的,您必须请其他人在不知道操作步骤的情况下执行某一功能。
  • 环境安全测试保证该应用程序能在实际运营环境中安装和运行。进行此测试时,SQL Server 和 Internet Information Services (IIS) 的安全设置必须与实际运营时的设置相同。
  • 网络测试确定不同网络条件对应用程序的影响。例如,通过这种测试,能够发现使用低速网络连接时可能出现的问题。
  • 灾难恢复(备份/恢复)测试确保万一出现灾难性事件,用户能够按照一定步骤恢复应用程序及其数据存储区。该测试应由运营支持部门负责。
  • 基于应用程序的故障转移功能测试确保出现已有文档记录的故障情况时,基于应用程序的故障转移功能起作用。
  • 用户接受性测试通常由那些具有与目标用户相似的技能和背景的用户来执行。目的是为了确定该应用程序满足用户要求和期望的程度(即面向用户要求的测试)。注意,测试小组并不实际执行该测试,但可能要监督或设计该测试。
  • 内存溢出和内存泄漏测试确保应用程序可在技术文档指定的内存容量下运行。该测试还通过多次启动和关闭应用程序来检测相关的内存泄漏问题。
  • 旧版本操作系统移植测试确保应用程序在安装更新版本的操作系统后仍能运行。
  • 帮助测试确保联机帮助提供的内容与当前问题相关,并提供了解决办法。验证联机帮助内容时,测试小组并不检查业务规则的正确性。

在上述每个测试区域中,测试小组都必须决定所需完成的测试等级。如下所示:

  • - 非常重要,需彻底测试此区域。
  • - 执行标准测试
  • - 如果时间允许则测试

下一部分着重讨论功能测试。

功能测试

在开发电子商务解决方案的过程中,您应该仔细测试每个内部版本,确保应用程序具有功能规范文档所述的功能。这涉及到保证当出现应用程序设计中确定的各个用户场景时,应用程序按照设计期望的方式运行。

测试方法

在多数大中型项目中,将指定一个测试小组来执行功能测试。应用程序生成和测试交替进行,最终获得软件的发行版本。

图 8-1 显示了典型的应用程序开发和测试周期。有关测试周期中各阶段的详细信息,请参阅本章中相应的小节。


请单击此处,查看完整的图片。

图 8-1:典型的测试周期

阶段 1 - 编写测试目标和主计划文档

测试过程的第一步,是以文档形式明确测试目标,以及如何实现这些目标。确定测试的所有相关因素并形成文档是非常重要的,这包括测试假定、时间表、测试优先级、测试级别、职责、预期效果和决定因素、风险及其规避等等。完成计划后,将得到主测试计划文档,它在整个测试生命周期中是一个动态的文档。

在这一阶段需要的源文档是功能规范文档和代码的高级版本发布时间表。

请参阅本章前面“制订测试策略”中,测试小组在编制主测试计划时应考虑的测试区域和测试类型。

阶段 2 - 编写详细测试计划

详细测试计划描述所有用户或帐号的各种不同使用场景和进入路径。这些测试使用场景以应用程序设计过程中确定的使用场景为基础。详细测试计划还确定要测试的每个场景的优先级。

这一阶段需要的源文档是功能规范文档和高级应用程序和体系结构设计。

阶段 3 - 审核详细测试计划

开发小组必须审核详细测试计划,确保它符合应用程序的测试要求。测试计划获得批准后,才可以开始测试。

阶段 4 - 定义测试案例

应根据已批准的详细测试计划来产生详细测试案例,定义要在应用程序上执行的操作、输入的数据和预期的结果,以及记录结果时应采用的预定格式。在这个阶段,应按照要测试的功能的重要性来确定测试案例的优先级。(有时需要将详细测试计划中的每个场景都扩充成详细测试案例文档中的一个或多个案例。)另外,您可能需要制作一个测试案例执行顺序文档,从而节省执行时间。

阶段 5 - 测试应用程序

在实际测试阶段,您应该以端对端的方式测试所有应用程序路径,以确保它们符合功能规范。测试小组使用缺陷跟踪工具向项目管理人员报告测试过程中发现的所有缺陷。另外,测试小组可能要隔离这些缺陷。

此阶段需要的文档是详细测试案例。

阶段 6 - 确定生成/测试周期是否完成

仅在一轮测试过后便准备发布应用程序是不太可能的。是否重复执行另一轮生成和测试取决于很多因素,包括现存错误的严重性、预算限制和时间期限等。您的项目计划应该允许在发布前重复进行若干次生成/测试。

阶段 7 - 举行鉴定会议

测试小组、项目管理小组和开发小组将在鉴定会议中讨论缺陷的状况,以及哪个缺陷指定给哪个开发人员去解决。

阶段 8 - 消除错误

开发小组必须协同工作,消除鉴定会议上确定的所有错误。完成之后,将每个错误返回给相应的负责人(提交该错误的测试工程师)进行验证,如果验证合格就结束这个错误。错误消除之后,将每个错误返回给相应的负责人(验证该错误的测试工程师),如果该错误已修正就结束,否则就采取进一步行动。

阶段 9 - 编写测试报告

测试报告包含测试计划中列出的具体项目的状态信息,以及按严重程度分类的缺陷说明信息。

该报告对于发布决策会议(将在以下小节讨论)非常重要。

阶段 10 - 发布决策会议

测试完成后,程序管理小组举行发布决策会议,确定是否可以发布该应用程序。除了项目管理小组外,测试小组和开发小组都应参加此会议。

此会议要用到的文件主要是发布标准(在主测试计划阶段确定)和测试报告。

性能测试

在运营环境中部署电子商务解决方案之前,必须彻底测试应用程序,确保其满足性能和可伸缩性方面的要求。总的来说,应该以响应时间吞吐量来测试应用程序,验证在预期数量的用户使用它时是否能提供可接受的性能水平。

响应时间

响应时间是从单个用户的角度来衡量应用程序性能的一项指标。它衡量从用户发出请求到应用程序做出响应所需的时间。可接受响应时间随站点的不同而有所不同,甚至与网页有关。例如,在进行身份验证时用户预期等待的时间,可能要比显示指定类别的产品时所能等待的时间要长一些。虽然“越快越好”的准则看起来是首选的设计模式,但是您应该清楚在某些情况下,响应时间应该为提供足够的安全性和可伸缩性而做出让步。

在电子商务应用程序中,影响响应时间的两个主要因素是网络延迟应用程序处理时间。网络延迟可以用各种方法缩短。例如:

  • 使用合理的基础设施体系结构来部署应用程序。例如,使用比集线器更快的交换机,并选择高性能的网络硬件。
  • 缩短应用层之间的物理距离。
  • 减少组件之间在网络上的功能调用。
  • 缓存数据以避免不必要的数据库访问调用。

应用程序处理时间是应用程序执行特定任务需要的计算时间。您可以通过改善代码编写,并确保该应用程序使用解析脚本和编译代码的适当组合,来减少处理时间。另外,如果可能,使用异步编程模型可以大大优化响应时间。

响应时间通常随应用程序负荷的增加而增加。另外,某些程序错误(例如那些导致内存泄漏的错误)只在大负荷条件下才能检测出来。因此,执行响应时间测试时必须合理地模拟预期负荷。

吞吐量

吞吐量是对应用程序性能更整体的评价。它衡量应用程序处理多个并行用户带来的负荷的能力。吞吐量通常按每秒页数或每秒请求数来衡量。它是在大量用户访问应用程序时,该应用程序可伸缩性的指标。

提高吞吐量的策略包括:向外扩展(使用负载平衡群集中配置的多台服务器来分担用户负载),使用随机值作为分配关键字在多台数据库服务器上分配数据,利用缓冲技术(例如数据库连接缓冲和 COM+ 对象缓冲)减少资源抢用,以及向上扩展(增加服务器的硬件资源以处理更多负荷)。

要在电子商务站点上准确测试吞吐量,您必须归纳用户将会执行的活动类型。特别地,您必须确定期望的“购买/浏览”比率(预期的执行网上购物的用户百分比与仅仅浏览目录的用户的百分比)。该比率可能随不同类型的站点而变化很大(例如,在 B2C 零售站点,可能只有百分之二十左右的用户会有购买行为,而在 Internet 银行解决方案中,绝大多数用户都将进行某种交易)。很大程度上,此信息只有在站点投入运营后才能准确确定,但您可以使用根据类似站点的指标获得的最准确估算来进行测试。为了进行测试而模拟用户负载时,您应该反映出尽可能多的预期使用模式,以便更准确地掌握该应用程序在实际运营中的运行情况。

测试应该基于现实状况来执行。测试的基础设施体系结构应该尽可能接近您要用来部署应用程序的运营环境。例如,您应该使用配置了某种基于 IP 的负载平衡机制的多个 Web 服务器。不能相信仅从一台计算机上测试获得的性能指标!请记住,防火墙和加密设置等安全措施都会影响性能,测试环境应包含这些措施。

性能测试工具和实用程序

有很多用于收集性能统计信息的工具。这些工具包括:监视工具(例如 Microsoft Windows 2000 系统监视器和 Netmon、SQL Server Profiler),系统日志文件(例如 IIS 生成的文件),专用测试工具(例如 Microsoft Web Application Stress [WAS] 工具),以及其他一些第三方提供的强度测试工具。每个工具都有自己的优点和缺点,因此,要准确掌握应用程序的性能,不能只依靠一个工具。相反,应该使用多种工具测试应用程序。

Web Application Stress (WAS) 工具可用于模拟大量并行用户负载加到应用程序的情形。要应用它,您可以记录站点的一系列 HTTP 请求,然后让 WAS 工具按照指定的并行用户数量发出请求。该工具收集响应时间和吞吐量统计信息,可用于评估应用程序的性能。WAS 工具可以从 http://Webtool.rte.microsoft.com/default.htm(英文)免费下载,在此站点中您还可以获得有关使用此工具测试 Web 应用程序的更多信息。

当使用类似 WAS 的强度测试工具时,应创建若干个脚本(而不是一个脚本)来模拟不同的用户场景。这样,需要确定具体的性能瓶颈时,您可以运行一个脚本;在应用程序上模拟实际负载时,则可以同时运行多个脚本。多数强度测试工具允许您将各个脚本按相对强度比例加到系统上,这样可以使您更准确地反映预期在运营中出现的使用场景。

性能测试方法

B2C 站点的用户希望该站点始终运行良好。应用程序的性能、可伸缩性和整体可靠性是 Web 应用程序设计的要素。

性能分析方法包括以下不同的步骤:

  1. 准备分析
  2. 创建强度脚本
  3. 执行测试
  4. 分析结果
  5. 记录和提交结果

上述每个步骤将在以下各节中讨论。

准备分析

分析的第一个步骤涉及到收集信息。此信息应该提供复制应用程序环境和理解应用程序使用方式所必要的详细资料,并应该告知您全部现有性能问题。这些信息的来源包括市场预测、运营 IIS 日志、性能日志和应用程序的功能规范。当然,很多这些信息只在现在已投入运营的站点上才可能获得。对于新站点来说,您可以借助市场预测和类似站点的数据。您所收集的信息对于成功地进行性能分析至关重要。它有助于确定对测试环境的要求,并在从模拟环境到分析测试结果的整个分析阶段得到运用。

在开始分析之前,您还应该确定性能分析应交付哪些内容。应将性能分析的交付内容看作测试小组和应用程序所有者之间的合同要求。通常情况下,执行性能分析时,应用程序所有者可能不知道他们到底要从分析中获得什么。创建性能分析的交付内容清单可以为他们回答这个问题。

创建运营环境的副本

要获得最准确的测试结果,测试设备应该模拟当前或预期的运营环境,包括硬件和软件配置。如果实际运营中部署了负载平衡解决方案(例如 Microsoft 网络负载平衡或 Microsoft Windows NT® 负载平衡服务 [NLB/WLBS]),则测试环境应该反映出这一点。

测试设施也应该反映在运营中分配的服务器角色和服务器数量。例如,如果您在运营中使用具有三个 IIS 服务器的群集,请在强度测试实验室中使用与此匹配的配置。每个 IIS 服务器的 CPU、RAM 和磁盘配置也应该符合实际运营条件。Service Pack、驱动器和硬件的 BIOS 版本也必须匹配。匹配硬件和软件可以使您获得更准确的测试数据,从而无需进行推断。

由于预算或其他方面的限制,您可能无法创建与运营环境完全相同的测试环境。在这种情况下,进行数据分析时请务必注意不同之处。

要测试 ConsolidatedRetail.com 站点,请按图 8-2 所示将应用程序部署在用于测试的设施中。


请单击此处,查看完整的图片。

图 8-2:测试设施

工作站用于模拟 Internet 客户端(其中一台计算机运行 Windows 2000 Professional,另外两台运行 Windows 98),通过一个三级交换机访问站点。Web 层上的 IIS 服务器通过一个二级交换机与数据库服务器传输数据。所有 Commerce Server 对象和管道均部署在 IIS 服务器上(即,不存在单独的应用服务器的物理层),并且所有的站点数据(除了直接邮件数据库)都存储在位于二级交换机之后的 SQL Server 数据库服务器上。直接邮件数据库部署在 IIS 服务器上。

以上设施的设计目的是模拟应用程序的部署环境。

应用程序所有者的参与

很多时候,应用程序所有者已经自己进行了一些调查研究。与应用程序所有者讨论性能问题可以节约您的时间。他们可以帮助您更深入敏锐地观察其应用程序的性能。尤其是应用程序开发人员可能具有管理员无法提供的特定考虑和知识。如果他们的研究已经发现了瓶颈,那您的任务只是验证这些有问题的区域,并为开发人员提供更详细的信息。

理解应用程序背后的技术

在继续进行前先理解应用程序背后的技术是很有必要的。对应用程序理解得越深,则您的性能/强度分析将越透彻。例如,如果您知道某个应用程序大量使用 XML,那么您应该掌握 XML 的性能调试方法。

对于 ConsolidatedRetail.com 应用程序,测试小组必须非常熟悉 Commerce Server 2000 的部署和使用以及 XML 和 XSLISAPI 过滤器。

定义交易/用户场景

要成功地完成性能/强度分析,您必须清楚最终用户使用应用程序的日常情况。您会发现某项任务往往比其他任务执行得多。您的性能/强度脚本应该反映出这种使用模式。确定使用模式时,请务必与市场和产品支持人员进行交流。通常他们与用户接触更多,并对这些统计信息有更深刻的理解。IIS 日志文件也是很好的资源,有助于掌握应用程序组件或网页的访问频率。日志非常有用,不仅可以用于在脚本中定义用户场景,还可用于比较验证强度测试分布和运营中的实际页面浏览量分布。

对于 ConsolidatedRetail.com 站点,预期的使用模式是百分之八十仅浏览,百分之二十进行购买。另外,在那百分之二十执行购买的用户中,期望其中一半是已经注册过的回头用户,另一半是在付款前需要先注册的新用户。

定义目标

务必定义清楚分析的目标,并将这些目标包含在测试计划中,使大家对要提交哪些分析内容达成共识。这可以减少被迫重新运行测试脚本的风险。重新运行脚本将浪费时间和资源,并对分析产生不良影响,因为测试小组往往会因为缺乏时间而赶工。

创建强度脚本

收集完所需信息并准备好测试环境后,性能/强度分析的下一步是创建能准确模拟预期站点流量的强度脚本。这可以使用从当前版本站点获取的历史数据或市场和业务分析家的预期数据来完成。要生成高度可靠的强度脚本,请考虑以下各小节所讨论的因素:

创建多个脚本

处理多个场景时,应该避免用单个脚本来包含所有场景。使用单个巨大的脚本将导致很难隔离出使整个脚本运行缓慢的特定场景。例如,要模拟普通电子商务站点,您可能需要一个用户浏览目录和产品的场景,一个用户将产品添加到购物篮然后付款的场景,还有一个用户搜索产品的场景。如果测试小组创建三个独立的脚本,就可以分别执行强度测试,确定每种用户场景的瓶颈,也可以同时按预期情况模拟混合流量。

避免记录和播放

静态网站早已成为过去。如今的多数站点(尤其是那些用于电子商务目的的站点)都具有完全动态的内容。正是由于这一原因,您不能简单地通过记录和播放基本的 getpost 命令来准确地模拟站点。您可能需要自定义是否让站点自动生成一些项目,如购物者 ID、购物篮 ID、订单 ID 和 GUID 等。很多测试工具具有捕获各线程(虚拟用户)的动态变化变量的功能,但您需要验证脚本结果以确保正确生成变量。

很多测试工具还具有从 .csv 或 .txt 文件导入数据的能力。该功能允许您从 SQL 数据库中导出产品和目录列表,然后用此文件包含的数据提高脚本的动态性(从而避免脚本反复使用相同的产品)。WAS 工具可以创建一系列变量用于您的脚本,例如,用户名、密码、产品和类别都可以通过变量来生成。

验证强度工具的操作

在进行大范围测试之前,您应该先验证强度工具是否作为真实用户准确地使用该站点。为此,您必须理解在 IIS 服务器和 SQL Server 上,各 ASP 页访问和执行的内容。在跟踪 ASP 页的运行时,IIS 服务器日志文件和 SQL Profiler/Trace 文件是可供利用的极好资源。更准确的方法是使用浏览器浏览整个站点,并记录所有的 SQL 命令和为每个页面调用的存储过程。另外,请注意所有显示在强度脚本所包含页面的 Web 内容(例如 GIF、XML、ASP 和 HTML 文件)都会出现在 IIS 日志中。然后,您可以对一个用户播放脚本并仅运行一次此场景,同时验证 SQL 跟踪文件和 IIS 日志是否记录了相应的服务器端活动。

执行性能/强度测试

在这一步,您应该事先准备好用于运行该应用程序的服务器环境和模拟客户端负载的脚本。性能/强度分析的第三步是运行脚本,执行强度测试。以下各小节概述了执行性能/强度测试的一些要点。

快速测试站点

快速测试可以确定发现应用程序系统瓶颈所需要的客户端数量和线程数量。Microsoft 建议使用多个客户端运行较少量的线程,而不是使用单个客户端运行多个线程。这里的快速测试是指运行若干个简短的强度测试,从而找到客户端和虚拟用户线程之间的优化比率。优化的意思是该比率在服务器上,而不是在强度客户端上,造成性能下降或瓶颈。

开始收集性能数据

当您获得客户端和线程之间的正确比率后,请启动所有服务器的系统监视器,打开每个计数器,开始进行测试。对于持续 30 分钟左右的测试,您可以以 15 秒或 30 秒为间隔。对于更长时间的测试,则以 60 秒到 300 秒的间隔使日志文件大小保持最小。

在开始测试前重置 IIS 日志

清除 IIS 日志可使数据分析过程更容易一些。您可以通过使用 iisreset 命令或 net stop iisadmin /y 命令来关闭 IIS Administrator Service (iisadmin),从而重置日志。然后,删除 C:/Winnt/System32/Logs 中的 IIS 日志文件, 使用 net start w3svc 命令重新启动 w3svc 服务。

清除 Windows 事件系统、安全和应用程序日志

清除 Windows 事件日志可让您确定在强度测试过程中,站点所产生的任何异常错误消息。

配置和启动 SQL Profiler

在 SQL Server 上,启动 SQL Profiler/Trace 并只添加 T-SQLLocks 事件。这将显示所有 SQL 命令和存储过程、读取、写入及命令周期和任何发生的死锁。注意,SQL 跟踪文件的大小将随测试的进行而快速增大。因此,只有不超过 30 分钟的测试,才可以一次收集整个测试的信息。而对于更长时间的测试,Microsoft 建议在测试的开始、中间和结束分别按 30 分钟间隔运行 SQL Profiler。如果还有其他的 SQL Server,您可以将跟踪设置为将信息记录到数据库(而不是文件)中。

创建受控环境

如果可能,尽量在 IIS 群集或 SQL Server 上没有其他活动时执行强度测试。使用受控环境,您可以确保不存在来源于您的强度客户端之外的异常错误消息、页面浏览、网络通信或负载。

分析结果

在运行测试并生成测试数据之后,就进入分析阶段。首先,您应该验证强度测试成功进行了模拟,然后再进行完整的数据分析。此过程将在以下各节中简单说明。

停止模拟和暂停数据采集

停止运行强度脚本、系统监视器和测试环境中所有客户端和服务器上的 SQL Profiler/Trace。确保系统监视器、SQL Trace/Profiler、IIS 日志和 Windows 事件日志已经保存在单独的目录中,这样您可以归档和组织测试数据。

检查 Windows 事件日志

浏览 Windows 事件日志并确保其中没有您的强度脚本产生的异常消息。作为强度测试结果的错误是可以接受的。

分析性能监视器数据

系统监视器数据有助于确定系统 CPU 使用、内存使用、磁盘队列和 w3svc 计数器等指标。

分析 SQL 跟踪文件

在分析 SQL 跟踪文件时,请搜索具有较长周期(一秒以上)的 SQL 命令和存储过程、以及大量的 SQL 读或写操作。如果不熟悉 SQL Server 的性能调试,请将您的跟踪结果转交给 SQL 设计者进行更深入的分析。调试 SQL Server 可能需要添加一些索引并在存储过程中更改代码,甚至可能会涉及改变数据库设计的体系结构。

验证访问的页面

IIS 日志可以帮助识别在强度测试期间被访问的所有页面。另外,Commerce Server 2000 统计信息和 IIS 日志文件可以导入到数据仓库,并可以使用 Commerce Server Business Desk 的报表模块进行检查。这可以进一步揭示测试期间站点活动的情况。

测量站点的吞吐量

吞吐量是根据已成功完成的用户场景来计算的。例如,成功创建购物篮、处理订单和执行搜索都被视为电子商务站点上的成功完成的场景。不仅开发人员,其他大多数人也都能够理解这些指标。要定义吞吐量,一个最简单最准确的方法是使用数据库中的表,然后计算测试前后的改变量。然后使用 IIS 日志合并处理这些数据,并计算实际页面浏览次数。

SQL 表和吞吐量分析

正如上面提到的,SQL 数据库中的表还可用于计算成功交易的数量。例如,如果有一个购物篮表,请在强度测试前后计算行数。两者之差便是强度测试期间创建的购物篮数量。查询的结果以及测试的开始和结束时间记录,有助于确定交易/时间吞吐量比率。

验证吞吐量

在此验证过程中要回答的关键问题是,IIS 日志指示的交易数目与数据库条目表明的全新交易数目是否相符,如果不相符,原因何在。用于验证吞吐量的另一个潜在信息来源是强度工具报告,尽管这些报告与服务器端数据相比总体上会高估了实际情况。因此,如果存在差异,您可能要采用服务器端的数据。通过两个或更多信息来源验证吞吐量,可以使您的结果更可信。

附录 A:ConsolidatedRetail.com 站点的 XML 输出

摘要:本附录包含参考应用程序、ConsolidatedRetail.com 和 ConsolidatedRetail.com PASP 文件的 XML 输出。PASP 文件及其相应的 XML 输出按字母顺序排列。

Acct.pasp

以下 XML 是已验证用户访问 Acct.pasp 时所生成输出的示例。

<?xml-stylesheet type="text/xsl" server-config="Acct-Config.xml"
href="Acct-IE5.xsl"?>
<page pagename="Acct.pasp">
<pagemode/>
<profilemenu/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Addressbook.pasp

以下 XML 是已验证用户访问 AddressBook.pasp 时所生成输出的示例。

<?xml-stylesheet type="text/xsl"
server-config="AddressBook-Config.xml"
href="AddressBook-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="AddressBook-Config.xml"
href="AddressBook-IE5.xsl"?>
<page pagename="AddressBook.pasp">
<profilemenu/>
<addresses>
<address>
<address_id>{75C86243-63F2-4B13-A260-2F7BA25E954C}</address_id>
<address_type>1</address_type>
<address_name>Kim Abercrombie</address_name>
<description>住宅</description>
<address_line1>我的家</address_line1>
<address_line2/>
<city>Redmond</city>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
</addresses>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Basket.pasp:Basket.pasp

以下 XML 是已验证用户访问 Basket.pasp,并且其购物篮中有物品时所生成输出的示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderSummary-Config.xml"
href="OrderSummary-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="Basket-Config.xml"
href="Basket-IE5.xsl"?>
<page pagename="Basket.pasp">
<advertising/>
<totallineitems>1</totallineitems>
<orderform orderform_id="{500B4B28-466B-4249-803E-B35A7F4490B1}"
saved_cy_oadjust_subtotal="74.95">
<Items quantity="1" product_id="651" product_catalog="硬件"
product_category="" description="即将推出"
d_DateCreated="21/02/2001 15:33:31"
cy_lineitem_total="74.95" cy_unit_price="74.95"
lineitem_uid="{3F2B9804-42AE-4EE4-827C-360F9A177CB9}"
_product_Name="Microsoft&#174 SideWinder&#174 Freestyle Pro"
_cy_oadjust_adjustedprice="74.95"/>
</orderform>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Category.pasp

以下 XML 是 Category.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderSummary-Config.xml"
href="OrderSummary-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="Category-Config.xml"
href="Category-IE5.xsl"?>
<page pagename="Category.pasp">
<searchscope>hardware</searchscope>
<getcatalogattributes>
<catalogname>硬件</catalogname>
<startdate>10/2/2000</startdate>
<enddate>10/2/2000</enddate>
<variantid>SKU</variantid>
<productid>prodid</productid>
<currency>USD</currency>
<weightmeasure>lbs</weightmeasure>
<catalogid>2</catalogid>
<customcatalog>False</customcatalog>
<freetextindexcreated>2/20/2001 3:35:44 PM</freetextindexcreated>
<producttableupdated>2/20/2001 3:34:52 PM</producttableupdated>
</getcatalogattributes>
<rootcategories>
<rootcategory>
<catalogname>硬件</catalogname>
<categoryname>特色产品</categoryname>
</rootcategory>
<rootcategory>
<catalogname>硬件</catalogname>
<categoryname>游戏设备</categoryname>
</rootcategory>
<rootcategory>
<catalogname>硬件</catalogname>
<categoryname>键盘</categoryname>
</rootcategory>
<rootcategory>
<catalogname>硬件</catalogname>
<categoryname>鼠标</categoryname>
</rootcategory>
<selectiontitle>浏览类别:</selectiontitle>
</rootcategories>
<rootproducts>
<selectiontitle>
在硬件类别的根级别中未找到产品。
</selectiontitle>
</rootproducts>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

ChangePasswd.pasp

以下 XML 是 ChangePasswd.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderSummary-Config.xml"
href="OrderSummary-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="ChangePasswd-Config.xml"
href="ChangePasswd-IE5.xsl"?>
<page pagename="ChangePasswd.pasp">
<profilemenu/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

EditAddressBook.pasp

以下 XML 是在未指定地址参数时,EditAddressBook.pasp 所生成输出的示例:

<?xml-stylesheet type="text/xsl"
server-config="EditAddressBook-Config.xml"
href="EditAddressBook-IE5.xsl"?>
<page pagename="EditAddressBook.pasp">
<addresstype></addresstype>
<pagemode/>
<profilemenu/>
<code_table>
<region_name>Alabama</region_name>
<region_name>Alaska</region_name>
<region_name>Arizona</region_name>
<region_name>Arkansas</region_name>
<region_name>California</region_name>
<region_name>Colorado</region_name>
<region_name>Connecticut</region_name>
<region_name>Delaware</region_name>
<region_name>District of Columbia</region_name>
<region_name>Florida</region_name>
<region_name>Georgia</region_name>
<region_name>Hawaii</region_name>
<region_name>Idaho</region_name>
<region_name>Illinois</region_name>
<region_name>Indiana</region_name>
<region_name>Iowa</region_name>
<region_name>Kansas</region_name>
<region_name>Kentucky</region_name>
<region_name>Louisiana</region_name>
<region_name>Maine</region_name>
<region_name>Maryland</region_name>
<region_name>Massachusetts</region_name>
<region_name>Michigan</region_name>
<region_name>Minnesota</region_name>
<region_name>Mississippi</region_name>
<region_name>Missouri</region_name>
<region_name>Montana</region_name>
<region_name>Nebraska</region_name>
<region_name>Nevada</region_name>
<region_name>New Hampshire</region_name>
<region_name>New Jersey</region_name>
<region_name>New Mexico</region_name>
<region_name>New York</region_name>
<region_name>North Carolina</region_name>
<region_name>North Dakota</region_name>
<region_name>Ohio</region_name>
<region_name>Oklahoma</region_name>
<region_name>Oregon</region_name>
<region_name>Pennsylvania</region_name>
<region_name>Rhode Island</region_name>
<region_name>South Carolina</region_name>
<region_name>South Dakota</region_name>
<region_name>Tennessee</region_name>
<region_name>Texas</region_name>
<region_name>Utah</region_name>
<region_name>Vermont</region_name>
<region_name>Virginia</region_name>
<region_name>Washington</region_name>
<region_name>West Virginia</region_name>
<region_name>Wisconsin</region_name>
<region_name>Wyoming</region_name>
</code_table>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

以下 XML 是在指定了地址参数时,EditAddressBook.pasp 所生成输出的示例:

<?xml-stylesheet type="text/xsl"
server-config="EditAddressBook-Config.xml"
href="EditAddressBook-IE5.xsl"?>
<page pagename="EditAddressBook.pasp">
<addresstype>运送</addresstype>
<pagemode/>
<profilemenu/>
<address>
<generalinfo>
<address_id>{75C86243-63F2-4B13-A260-2F7BA25E954C}</address_id>
<id>{31317332-6951-4460-9C14-0114341CC33B}</id>
<last_name/>
<first_name/>
<address_name>Kim Abercrombie</address_name>
<address_type>1</address_type>
<description>住宅</description>
<address_line1>我的家</address_line1>
<address_line2/>
<city>Redmond</city>
<region_code>WA</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
<country_code/>
<country_name/>
<tel_number>019182782</tel_number>
<tel_extension/>
<locale/>
<user_id_changed_by/>
</generalinfo>
<profilesystem>
<date_last_changed>2/21/2001 11:26:05 AM</date_last_changed>
<date_created>2/21/2001 11:26:05 AM</date_created>
</profilesystem>
</address>
<code_table>
<region_name>Alabama</region_name>
<region_name>Alaska</region_name>
<region_name>Arizona</region_name>
<region_name>Arkansas</region_name>
<region_name>California</region_name>
<region_name>Colorado</region_name>
<region_name>Connecticut</region_name>
<region_name>Delaware</region_name>
<region_name>District of Columbia</region_name>
<region_name>Florida</region_name>
<region_name>Georgia</region_name>
<region_name>Hawaii</region_name>
<region_name>Idaho</region_name>
<region_name>Illinois</region_name>
<region_name>Indiana</region_name>
<region_name>Iowa</region_name>
<region_name>Kansas</region_name>
<region_name>Kentucky</region_name>
<region_name>Louisiana</region_name>
<region_name>Maine</region_name>
<region_name>Maryland</region_name>
<region_name>Massachusetts</region_name>
<region_name>Michigan</region_name>
<region_name>Minnesota</region_name>
<region_name>Mississippi</region_name>
<region_name>Missouri</region_name>
<region_name>Montana</region_name>
<region_name>Nebraska</region_name>
<region_name>Nevada</region_name>
<region_name>New Hampshire</region_name>
<region_name>New Jersey</region_name>
<region_name>New Mexico</region_name>
<region_name>New York</region_name>
<region_name>North Carolina</region_name>
<region_name>North Dakota</region_name>
<region_name>Ohio</region_name>
<region_name>Oklahoma</region_name>
<region_name>Oregon</region_name>
<region_name>Pennsylvania</region_name>
<region_name>Rhode Island</region_name>
<region_name>South Carolina</region_name>
<region_name>South Dakota</region_name>
<region_name>Tennessee</region_name>
<region_name>Texas</region_name>
<region_name>Utah</region_name>
<region_name>Vermont</region_name>
<region_name>Virginia</region_name>
<region_name>Washington</region_name>
<region_name>West Virginia</region_name>
<region_name>Wisconsin</region_name>
<region_name>Wyoming</region_name></code_table>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

ForgotPasswd.pasp

以下 XML 是 ForgotPasswd.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="ForgotPasswd-Config.xml"
href="ForgotPasswd-IE5.xsl"?>
<page pagename="ForgotPasswd.pasp">
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Index.pasp

以下 XML 是 Index.pasp 的输出示例。

<?xml-stylesheet type="text/xsl" server-config="Index-Config.xml"
href="Index-IE5.xsl"?>
<page pagename="Index.pasp">
<advertising/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Login.pasp

以下 XML 是 Login.pasp 的输出示例。

<?xml-stylesheet type="text/xsl" server-config="Login-Config.xml"
href="Login-IE5.xsl"?>
<page pagename="Login.pasp">
<advertising/>
<pagemode/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile/>
<exceptions></exceptions>
</page>

MultiShipping.pasp

以下 XML 是 MultiShipping.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="MultiShipping-Config.xml"
href="MultiShipping-IE5.xsl"?>
<page pagename="MultiShipping.pasp">
<pagemode>MultiShipping.pasp</pagemode>
<addresses>
<address>
<address_id>{75C86243-63F2-4B13-A260-2F7BA25E954C}</address_id>
<address_name>Kim Abercrombie</address_name>
<address_line1>我的家</address_line1>
<address_line2/>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
<address>
<address_id>{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}</address_id>
<address_name>Kim Abercrombie</address_name>
<address_line1>1 Microsoft Way</address_line1>
<address_line2/>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
</addresses>
<orderform orderform_id="{500B4B28-466B-4249-803E-B35A7F4490B1}"
saved_cy_oadjust_subtotal="74.95">
<Addresses address_name="Kim Abercrombie" address_line1="我的家"
city="Redmond" region_code="WA" postal_code="12345"
country_code="US" description="Home"
AddressesDictKey="{75C86243-63F2-4B13-A260-2F7BA25E954C}"/>
<Addresses address_name="Kim Abercrombie"
address_line1="1 Microsoft Way" city="Redmond"
region_code="WA" postal_code="12345" country_code="US"
description="办公室"
AddressesDictKey="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"/>
<Items quantity="1" product_id="651" product_catalog="硬件"
product_category="" description="即将推出"
d_DateCreated="21/02/2001 15:33:31"
cy_lineitem_total="74.95" cy_unit_price="74.95"
lineitem_uid="{3F2B9804-42AE-4EE4-827C-360F9A177CB9}"
orig_lineitem_uid="{3F2B9804-42AE-4EE4-827C-360F9A177CB9}"
_product_Name="Microsoft&#174 SideWinder&#174 Freestyle Pro"
_cy_oadjust_adjustedprice="74.95"/>
</orderform>
<code_table>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-003380005417}
</shipping_method_id>
<shipping_method_name>快递</shipping_method_name>
</shipping_method>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-001861008604}
</shipping_method_id>
<shipping_method_name>标准</shipping_method_name>
</shipping_method>
</code_table>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

OrderHistory.pasp

以下 XML 是 OrderHistory.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistory-Config.xml"
href="OrderHistory-IE5.xsl"?>
<page pagename="OrderHistory.pasp">
<profilemenu/>
<history>
<order>
<item>
<ordergroup_id>
{45CA0421-9DFD-481B-B60E-C2ECDE28E5DF}
</ordergroup_id>
<order_number>1001</order_number>
<saved_cy_total_total>26.99</saved_cy_total_total>
<d_datelastchanged>2/21/2001 3:14:59 PM</d_datelastchanged>
</item>
</order>
<order>
<item>
<ordergroup_id>
{0FA626B0-852E-4707-93D5-A00619C6A35B}
</ordergroup_id>
<order_number>1000</order_number>
<saved_cy_total_total>48.98</saved_cy_total_total>
<d_datelastchanged>2/21/2001 1:59:37 PM</d_datelastchanged>
</item>
</order>
</history>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

OrderHistoryDetail.pasp

以下 XML 是 OrderHistoryDetail.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?>
<page pagename="OrderHistoryDetail.pasp">
<orderform orderform_id="{AF2DDB69-5694-49B3-AB4A-AB95F77FB4DB}"
payment_method="credit_card"
billing_address_id="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"
saved_cy_oadjust_subtotal="39.98"
saved_cy_total_total="48.98" cc_name="Visa"
cc_expyear="2002" cc_expmonth="01"
cc_number="1234567890123"
user_email_address="kim@somecompany.com"
cc_Account_Holder="Kim Abercrombie" cy_tax_total="4"
cy_shipping_total="5">
<Addresses address_name="Kim Abercrombie" address_line1="我的家"
city="Redmond" region_code="WA" postal_code="12345"
country_code="US" description="住宅"
AddressesDictKey="{75C86243-63F2-4B13-A260-2F7BA25E954C}"/>
<Addresses address_name="Kim Abercrombie"
address_line1="1 Microsoft Way" city="Redmond"
region_code="WA" postal_code="12345" country_code="US"
description="办公室"
AddressesDictKey="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"/>
<Items quantity="2" product_id="Microsoft Age of Empires II:
The Age of Kings: Inside Moves" product_Name="Microsoft Age of Empires II:
The Age of Kings: Inside Moves" product_catalog="书籍" product_category=""
description="使用此指南,可以掌握所有致胜策略、技巧和诀窍,进入新版的
Microsoft Age of Empires! MICROSOFT AGE OF EMPIRES II: AGE OF KINGS:
INSIDE MOVES 告诉您如何在游戏中立于不败之地。"
shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"
shipping_method_name="快递"
d_DateCreated="21/02/2001 12:14:53" cy_lineitem_total="39.98"
cy_unit_price="19.99"
lineitem_uid="{0F79FBF7-AA37-4C37-84CD-B0BDDCD971DD}"/>
<shipments
shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"/>
</orderform>
<profilemenu/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

OrderSummary.pasp

以下 XML 是 OrderSummary.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderSummary-Config.xml"
href="OrderSummary-IE5.xsl"?>
<page pagename="OrderSummary.pasp">
<orderform orderform_id="{500B4B28-466B-4249-803E-B35A7F4490B1}"
payment_method="credit_card"
billing_address_id="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"
saved_cy_oadjust_subtotal="74.95"
saved_cy_total_total="87.45" cc_name="American Express"
cc_expyear="2002" cc_expmonth="01" cc_number="123456789"
_cy_shipping_total="5" _cy_tax_total="7.5"
user_email_address="kim@somecompany.com"
cc_Account_Holder="Kim Abercrombie" cy_tax_total="7.5"
cy_shipping_total="5">
<Addresses address_name="Kim Abercrombie" address_line1="我的家"
city="Redmond" region_code="WA" postal_code="12345"
country_code="US" description="住宅"
AddressesDictKey="{75C86243-63F2-4B13-A260-2F7BA25E954C}"/>
<Addresses address_name="Kim Abercrombie"
address_line1="1 Microsoft Way" city="Redmond"
region_code="WA" postal_code="12345" country_code="US"
description="办公室"
AddressesDictKey="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"/>
<Items quantity="1" product_id="651"
product_Name="Microsoft&#174 SideWinder&#174 Freestyle Pro"
product_catalog="Hardware" product_category=""
description="即将推出"
shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"
shipping_method_name="快递"
d_DateCreated="21/02/2001 15:33:31"
cy_lineitem_total="74.95" cy_unit_price="74.95"
lineitem_uid="{3F2B9804-42AE-4EE4-827C-360F9A177CB9}"
_product_Name="Microsoft&#174 SideWinder&#174 Freestyle Pro"
_cy_oadjust_adjustedprice="74.95"/><shipments
shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"
_cy_shipping_total="5"/>
</orderform>
<shipping_methods>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-003380005417}
</shipping_method_id>
<shipping_method_name>快递</shipping_method_name>
</shipping_method>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-001861008604}
</shipping_method_id>
<shipping_method_name>标准</shipping_method_name>
</shipping_method>
</shipping_methods>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Payment.pasp

以下 XML 是 Payment.pasp 的输出示例。

<?xml-stylesheet type="text/xsl" server-config="Payment-Config.xml"
href="Payment-IE5.xsl"?>
<page pagename="Payment.pasp">
<advertising/>
<billingaddressstatus>
{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}
</billingaddressstatus>
<orderform orderform_id="{500B4B28-466B-4249-803E-B35A7F4490B1}"
payment_method="credit_card"
billing_address_id="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"
saved_cy_oadjust_subtotal="74.95"
saved_cy_total_total="87.45" cc_name="American Express"
cc_expyear="2002" cc_expmonth="01" cc_number="123456789"
_cy_shipping_total="5" _cy_tax_total="7.5"
user_email_address="kim@somecompany.com"
cc_Account_Holder="Kim Abercrombie" cy_tax_total="7.5"
cy_shipping_total="5">
<Addresses address_name="Kim Abercrombie" address_line1="我的家"
city="Redmond" region_code="WA" postal_code="12345"
country_code="US" description="住宅"
AddressesDictKey="{75C86243-63F2-4B13-A260-2F7BA25E954C}"/>
<Addresses address_name="Kim Abercrombie"
address_line1="1 Microsoft Way" city="Redmond"
region_code="WA" postal_code="12345" country_code="US"
description="办公室"
AddressesDictKey="{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}"/>
<Items quantity="1" product_id="651"
product_Name="Microsoft&#174 SideWinder® Freestyle Pro"
product_catalog="Hardware" product_category=""
description="即将推出"
shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"
shipping_method_name="快递"
d_DateCreated="21/02/2001 15:33:31"
cy_lineitem_total="74.95" cy_unit_price="74.95"
lineitem_uid="{3F2B9804-42AE-4EE4-827C-360F9A177CB9}"
_product_Name="Microsoft® SideWinder® Freestyle Pro"
_cy_oadjust_adjustedprice="74.95"/>
<shipments shipping_address_id="{75C86243-63F2-4B13-A260-2F7BA25E954C}"
shipping_method_id="{00000000-0000-0000-0000-003380005417}"
_cy_shipping_total="5"/>
</orderform>
<address>
<generalinfo>
<description>住宅</description>
<tel_number>832832892</tel_number>
<address_id>{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}</address_id>
<address_type>2</address_type>
<address_name>Kim Abercrombie</address_name>
<address_line1>街道</address_line1>
<address_line2/>
<city>Redmond</city>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</generalinfo>
</address>
<code_table>
<payment_method>
<payment_method_id>amex</payment_method_id>
<payment_method_name>American Express</payment_method_name>
</payment_method>
<payment_method>
<payment_method_id>discover</payment_method_id>
<payment_method_name>Discover</payment_method_name>
</payment_method>
<payment_method>
<payment_method_id>mastercard</payment_method_id>
<payment_method_name>Master Card</payment_method_name>
</payment_method>
<payment_method>
<payment_method_id>visa</payment_method_id>
<payment_method_name>Visa</payment_method_name>
</payment_method>
<month>
<month_id>01</month_id><month_name>一月</month_name>
</month>
<month>
<month_id>02</month_id><month_name>二月</month_name>
</month>
<month>
<month_id>03</month_id><month_name>三月</month_name>
</month>
<month>
<month_id>04</month_id><month_name>四月</month_name>
</month>
<month>
<month_id>05</month_id><month_name>五月</month_name>
</month>
<month>
<month_id>06</month_id><month_name>六月</month_name>
</month>
<month>
<month_id>07</month_id><month_name>七月</month_name>
</month>
<month>
<month_id>08</month_id><month_name>八月</month_name>
</month>
<month>
<month_id>09</month_id><month_name>九月</month_name>
</month>
<month>
<month_id>10</month_id><month_name>十月</month_name>
</month>
<month>
<month_id>11</month_id><month_name>十一月</month_name>
</month>
<month>
<month_id>12</month_id><month_name>十二月</month_name>
</month>
<year>2001</year>
<year>2002</year>
<year>2003</year>
<year>2004</year>
<year>2005</year>
<year>2006</year>
</code_table>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Product.pasp

以下 XML 是 Product.pasp 的输出示例。

<?xml-stylesheet type="text/xsl" server-config="Product-Config.xml"
href="Product-IE5.xsl"?>
<page pagename="Product.pasp">
<searchscope>书籍</searchscope>
<advertising/>
<getproduct>
<book>
<catalogname>书籍</catalogname>
<definitionname>SDKBook</definitionname>
<cy_list_price displayname="价格">27.99</cy_list_price>
<originalprice displayname="您的价格">27.99</originalprice>
<i_classtype>4</i_classtype>
<productid>Code</productid>
<variantid/>
<author displayname="Author">Charles  Petzold</author>
<description>
使用常用的家用物件和熟悉的语言系统,如 Morse 代码、Braille 和 CODE
来揭示计算机和其他智能机器的内部运作。
</description>
<image_filename>boxshots/press/2352.gif</image_filename>
<image_height>120</image_height>
<image_width>120</image_width>
<isbn displayname="ISBN">0-7356-0505-X</isbn>
<name displayname="名称">Code</name>
<pagecount displayname="页数">400</pagecount>
<producturl displayname="产品信息 Url">
a href=http://mspress.microsoft.com/prod/books/2352.htm  target =_a
http://mspress.microsoft.com/prod/books/2352.htm /a
</producturl>
<publication_year displayname="出版年份">
1999
</publication_year>
<publisher displayname="出版商">Microsoft Press</publisher>
<reading_level displayname="阅读程度">
All Levels
</reading_level>
<title displayname="书名">Code</title>
</book>
</getproduct>
<relatedproducts/>
<relatedcategories/>
<parentcategories>
<parentcategory>
<catalogname>书籍</catalogname>
<categoryname>商务参考</categoryname>
</parentcategory>
<parentcategory>
<catalogname>书籍</catalogname>
<categoryname>教育参考</categoryname>
</parentcategory>
<parentcategory>
<catalogname>书籍</catalogname>
<categoryname>编程语言</categoryname>
</parentcategory>
<selectiontitle>浏览类别:</selectiontitle>
</parentcategories>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

Registration.pasp

以下 XML 是 Registration.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="Registration-Config.xml"
href="Registration-IE5.xsl"?>
<page pagename="Registration.pasp">
<advertising/>
<pagemode/>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile/>
<exceptions></exceptions>
</page>

SearchResults.pasp

以下 XML 是在 Books 目录中搜索 age 时,SearchResults.pasp 所生成输出的示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="SearchResults-Config.xml"
href="SearchResults-IE5.xsl"?>
<page pagename="SearchResults.pasp">
<advertising/>
<searchscope>书籍</searchscope>
<searchstring>年龄</searchstring>
<searchcount>4</searchcount>
<searchrowstoreturn>15</searchrowstoreturn>
<searchstartpos>1</searchstartpos>
<searchresults>
<book>
<catalogname>书籍</catalogname>
<definitionname>SDKBook</definitionname>
<originalprice displayname="您的价格">19.99</originalprice>
<cy_list_price displayname="价格">19.99</cy_list_price>
<i_classtype>4</i_classtype>
<productid>
Microsoft Age of Empires II: The Age of Kings: Inside Moves
</productid>
<description>
使用此指南,可以掌握所有致胜策略、技巧和诀窍,进入新版的 Microsoft Age of Empires!
MICROSOFT AGE OF EMPIRES II: AGE OF KINGS:
INSIDE MOVES 告诉您如何在游戏中立于不败之地。
</description>
<image_filename>boxshots /press/2388.gif</image_filename>
<image_width>120</image_width>
<image_height>120</image_height>
<name displayname="名称">
Microsoft Age of Empires II: The Age of Kings: Inside Moves
</name>
</book>
<book>
<catalogname>书籍</catalogname>
<definitionname>SDKBook</definitionname>
<originalprice displayname="您的价格">16.99</originalprice>
<cy_list_price displayname="价格">16.99</cy_list_price>
<i_classtype>4</i_classtype>
<productid>Microsoft Age of Empires: Inside Moves</productid>
<description>
MICROSOFT AGE OF EMPIRES: INSIDE MOVES 提供战略提示、策略和战术以协助您击败原始部落
并将他们带入伟大的文明。
</description>
<image_filename>boxshots/press/1049.gif</image_filename>
<image_width>120</image_width>
<image_height>120</image_height>
<name displayname="名称">
Microsoft Age of Empires: Inside Moves
</name>
</book>
<book>
<catalogname>书籍</catalogname>
<definitionname>SDKBook</definitionname>
<originalprice displayname="您的价格">29.99</originalprice>
<cy_list_price displayname="价格">29.99</cy_list_price>
<i_classtype>4</i_classtype>
<productid>Grown-Ups Guide to Computing</productid>
<description>
在 GROWN-UPS GUIDE TO COMPUTING 中,您将发现各行各业的人(无论老幼)如何通过使用计算机
来丰富他们的生活,
您也可以!如果您也和许多中老年人一样对计算机的用途和如何开始使用感兴趣,这本书是最佳选择。
</description>
<image_filename>boxshots/press/3221.gif</image_filename>
<image_width>120</image_width>
<image_height>120</image_height>
<name displayname="Name">Grown-Ups Guide to Computing</name>
</book>
<book>
<catalogname>书籍</catalogname>
<definitionname>SDKBook</definitionname>
<originalprice displayname="您的价格">16.99</originalprice>
<cy_list_price displayname="价格">16.99</cy_list_price>
<i_classtype>4</i_classtype>
<productid>
Microsoft Age of Empires: Inside Moves,修订版和扩充版
</productid>
<description>
MICROSOFT AGE OF EMPIRES: INSIDE MOVES,修订版和扩充版,告诉您在玩“帝国时代”时
如何部署致胜策略及一些内幕技巧和提示。Microsoft Age of Empires: Inside
Moves (Microsoft Press, 1997) 第一版畅销书已完全更新,其中包含“帝国时代”部分和全新的扩充内容“罗马的崛起”。
</description>
<image_filename>boxshots/press/2470.gif</image_filename>
<image_width>120</image_width>
<image_height>120</image_height>
<name displayname="Name">
Microsoft Age of Empires: Inside Moves, Revised and Expanded Edition
</name>
</book>
<selectiontitle>
找到与搜索标准 'Age'匹配的产品
</selectiontitle>
</searchresults>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile/>
<exceptions></exceptions>
</page>

Shipping.pasp

以下 XML 是 Shipping.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="Shipping-Config.xml"
href="Shipping-IE5.xsl"?>
<page pagename="Shipping.pasp">
<pagemode>Shipping.pasp</pagemode>
<advertising/>
<addresses>
<address>
<address_id>{75C86243-63F2-4B13-A260-2F7BA25E954C}</address_id>
<description>住宅</description>
<address_name>Kim Abercrombie</address_name>
<address_line1>我的家</address_line1>
<address_line2/>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
<address>
<address_id>{C062AAA0-D54B-480D-ACE8-EB73662DA1F5}</address_id>
<description>Office</description>
<address_name>1 Kim Abercrombie</address_name>
<address_line1>1 Microsoft Way</address_line1>
<address_line2/>
<city>Redmond</city>
<region_code>Washington</region_code>
<region_name>Washington</region_name>
<postal_code>12345</postal_code>
</address>
</addresses>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

ShippingMethod.pasp

以下 XML 是 ShippingMethod.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="ShippingMethod-Config.xml"
href="ShippingMethod-IE5.xsl"?>
<page pagename="ShippingMethod.pasp">
<pagemode>ShippingMethod.pasp</pagemode>
<advertising/>
<code_table>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-003380005417}
</shipping_method_id>
<shipping_method_name>快递</shipping_method_name>
</shipping_method>
<shipping_method>
<shipping_method_id>
{00000000-0000-0000-0000-001861008604}
</shipping_method_id>
<shipping_method_name>标准</shipping_method_name>
</shipping_method>
</code_table>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

ThankYou.pasp

以下 XML 是 ThankYou.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="OrderHistoryDetail-Config.xml"
href="OrderHistoryDetail-IE5.xsl"?xml-stylesheet type="text/xsl"
server-config="ThankYou-Config.xml"
href="ThankYou-IE5.xsl"?>
<page pagename="ThankYou.pasp">
<advertising/>
<ordernumber>1002</ordernumber>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catalogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

UserProfile.pasp

以下 XML 是 UserProfile.pasp 的输出示例。

<?xml-stylesheet type="text/xsl"
server-config="UserProfile-Config.xml"
href="UserProfile-IE5.xsl"?>
<page pagename="UserProfile.pasp">
<profilemenu/>
<pagemode/>
<userobject>
<accountinfo>
<org_id/>
<account_status>1</account_status>
<user_catalog_set/>
<date_registered/>
</accountinfo>
<advertising>
<campaign_history/>
</advertising>
<businessdesk>
<partner_desk_role/>
</businessdesk>
<generalinfo>
<user_id>{31317332-6951-4460-9C14-0114341CC33B}</user_id>
<logon_name>kim</logon_name>
<user_security_password>密码</user_security_password>
<email_address>kim@somecompany.com</email_address>
<user_type>1</user_type>
<user_title/>
<last_name></last_name>
<first_name></first_name>
<tel_number></tel_number>
<tel_extension/>
<fax_number></fax_number>
<fax_extension></fax_extension>
<user_id_changed_by/>
</generalinfo>
<profilesystem>
<date_last_changed>2/21/2001 11:21:51 AM</date_last_changed>
<date_created>2/21/2001 11:21:51 AM</date_created>
</profilesystem>
</userobject>
<getcatalogsforuser>
<selectiontitle>浏览目录:</selectiontitle>
<catalog>
<catalogname>书籍</catalogname>
</catalog>
<catalog>
<catHardwareogname>硬件</catalogname>
</catalog>
</getcatalogsforuser>
<profile auth="auth"/>
<exceptions></exceptions>
</page>

商务参考体系结构:企业对消费者 (B2C)相关推荐

  1. b2c o2o_企业对消费者– B2C | 第三部分

    b2c o2o B2E(企业对员工) (B2E (Business to Employee)) B2E (business to employee) is the business relations ...

  2. b2c o2o_企业对消费者– B2C | 第2部分

    b2c o2o B2C的模型 (Models of B2C) There are three types of B2C models, such as auctions, online stores ...

  3. 传统企业如何建设B2C平台做网络营销?

    空城说要写一个系列文章,讲解传统企业怎结合各种平台或是模式来做网络营销,昨天写了一篇<传统企业如何结合C2C平台做网络营销?>,反响还不错,今天打算写把企业的网络营销结合B2C模式来写,不 ...

  4. 消费者的“隐私悖论”与企业对消费者信息收集的合理措施 ——基于“支付宝账单事件“的思考

    本文基于<中国管理案例共享中心案例库>中"支付宝账单事件:如何保护消费者信息与隐私--<ISO26000>的指引".(案例正文可以自行寻找) -- 本文未经 ...

  5. B2B企业CRM与B2C企业CRM有哪些区别?

    在各种可用的CRM客户关系管理软件解决方案中,大多数是为B2B的信息化管理而构建的.一个简单的CRM关键字搜索将返回一页又一页的这样的产品,当然,您一定会发现这其中还有面向B2C企业的CRM. 这两个 ...

  6. 渠道是创业企业与消费者或者用户建立联系的桥梁

    渠道是创业企业与消费者或者用户建立联系的桥梁,渠道策略决定了创业企业的商品如何到达目标市场和目标顾客手中.在创业中,不仅要让商品的目标顾客能够意识到自身的需求和难题,更重要的是要让这部分目标顾客能够方 ...

  7. 领英LinkedIn的个人商务会员和企业销售会员我们应该怎么选?

    在详细介绍领英LinkedIn会员之前先解释下为什么要开通.也就是领英免费用户会有哪些限制以至于我们需要付费去开通会员: 1.有限的搜索次数 使用免费的 LinkedIn 帐户,您将在一个月内搜索约 ...

  8. 商务酷炫企业年终工作总结述职报告PPT模板

    模板介绍 本套商务酷炫企业年终工作总结述职报告PPT模板,模板编号:P64124,大小10MB,共27页,比例为16:9,由封面.目录.转场页.内容.结尾5个部分构成. 内含蓝色,灰色,红色多种配色, ...

  9. 小白必看!领英LinkedIn的个人商务会员和企业销售会员应该怎么选?

    在详细介绍领英LinkedIn会员之前先解释下为什么要开通.也就是领英免费用户会有哪些限制以至于我们需要付费去开通会员: 1.有限的搜索次数 使用免费的 LinkedIn 帐户,您将在一个月内搜索约 ...

最新文章

  1. Redis——由分布式锁造成的重大事故
  2. 35天 GRE: V160+Q168+W3.5
  3. layui js添加html,layui.js如何声明全局变量?
  4. 云原生时代老牌IDC巨头谋求转型,世纪互联成立新品牌“互联科技(NEOLINK)”
  5. java bean状态_无状态和有状态企业Java Bean
  6. javadoc 程序包android.content不存在,Eclipse中的Javadoc无法识别包
  7. [转] C#中Dispose和Close的区别
  8. SPSS统计基础-均值功能的使用
  9. VB一个可以改变箭头方向的气泡提示
  10. 机器人终于有触觉了!甚至可以实现膝跳反射丨Science
  11. 19-background
  12. UE4蓝图基础02-节点的基本知识
  13. 图像原点矩、二阶中心矩物理意义推导
  14. 谷歌play商店_不断关闭时如何修复Google Play商店
  15. 服务器3D场景建模(三):体素场景(一)
  16. Codeforeces——69A Young Physicist
  17. Android 4高级编程(第3版)》
  18. 重学 Java 设计模式:实战访问者模式「模拟家长与校长,对学生和老师的不同视角信息的访问场景」
  19. python实训总结万能版3000字_万能实习报告论文范文3000字
  20. 杀掉(kill)指定进程及其子进程

热门文章

  1. 熊孩子太调皮,送他Airblock无人机变身小神童
  2. 我吐了72行金色爱心代码(༗清ཻ辞ཻ࿐入门原创)———永远清楚,不是只有你一个人在努力
  3. 从excel表格读取日期利用python简单实现农历转阳历功能(1901-2099年之间)
  4. 付费会员亿时代即将来临,如何才能打造“终身俱乐部”?
  5. 使用Python连接crossbar.io 报错 could not create serializer for “cbor“ (available: [‘json‘])
  6. CSS解决hover选择器生硬效果
  7. vscode下载太慢,快速下载vscode方法!
  8. 华农华迪实训训练-获得词频前10的字段数据-requests+Spark RDD
  9. Debussy-54v9安装
  10. Day215.课程详细页面功能完善、Echarts统计分析模块[生成统计数据+生成图表]前后端整合 -谷粒学院