`
mmdev
  • 浏览: 12953954 次
  • 性别: Icon_minigender_1
  • 来自: 大连
文章分类
社区版块
存档分类
最新评论

商务参考体系结构:企业对消费者 (B2C电子商务实践) 第 7 章:ConsolidatedRetail.Com 的功能 (全文结束)

阅读更多

商务参考体系结构:企业对消费者

第 7 章:ConsolidatedRetail.Com 的功能
Microsoft Corporation
2001年5月

摘要:本章介绍了 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 和其它用于呈现站点中的页的模板。

<?xml version="1.0" ?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

<xsl:template match="*|/">
<xsl:apply-templates/></xsl:template>

<xsl:template match="text()|@*"><xsl:value-of select="."/></xsl:template>

<xsl:template match="page">
   <html>
      <head>
         <title>ConsolidatedRetail.com</title>
         <link rel="stylesheet" type="text/css" href="stylesheet.css"/>
      </head>
      <body bgcolor="#ceb6d5" leftmargin="0" marginwidth="0" topmargin="0" marginheight="0" onload="Focus()">
         <table width="100%" cellpadding="0" cellspacing="0" border="0">
            <tr>
               <td>
                  <xsl:call-template name="pageheader"/>
               </td>
            </tr>
            <tr>
               <td height="10"/>
            </tr>
            <tr>
               <td>
                  <xsl:call-template name="main"/>
               </td>
            </tr>
            <tr>
               <td height="10"/>
            </tr>
            <tr>
               <td>
                  <xsl:call-template name="pagefooter"/>
               </td>
            </tr>
         </table>
      </body>
   </html>
</xsl:template>
  <!-- 为了表达得更为清楚,此处省略了其它模板的代码 -->
</xsl:stylesheet>

呈现 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 类似于以下代码:

<?xml version="1.0" ?>

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="2.0">

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

<xsl:template match="*|/"><xsl:apply-templates/></xsl:template>

<xsl:template match="text()|@*"><xsl:value-of select="."/></xsl:template>

<xsl:template name="pageleft"/>

<xsl:template name="pagecenter">
      <script language="JavaScript">
      <![CDATA[
         function Focus(){
            document.formSearch.txtSearchPhrase.focus()
            }   
      ]]>
      </script>
      <table width="100%" cellpadding="0" cellspacing="0" border="0" bgcolor="#ffffff">
         <tr>
            <td width="11">
               <img src="/china/msdn/images/spacer.gif" width="1" height="1" border="0"/>
            </td>
            <td valign="top" class="content-text">
               <table width="100%" cellpadding="0" cellspacing="0" border="0">
                  <tr>
                     <td valign="top" colspan="3" class="content-text">
                        <p class="headline-text-purple">欢迎来到 ConsolidatedRetail.com</p>
                        <p>欢迎来到本站点,您在这里可以方便地购买到所需的任何商品。</p>
                        <p align="center"><a href="Registration.pasp"><img src="/china/msdn/images/home_freeshipping.gif" width="271" height="46" vspace="5" border="0"/></a><br/><br/></p>
                     </td>
                  </tr>
                  <tr>
                     <td valign="top" class="content-text">
                        <p><b> </b>
                        <br/> <img src="/china/msdn/images/spacer.gif" width="200" height="1" border="0"/></p>
                        
                     </td>
                     <td width="1" bgcolor="#ceb6d5">
                        <img src="/china/msdn/images/spacer.gif" width="1" height="1" border="0"/>
                     </td>
                     <td width="125" align="right" valign="top" class="content-text">
                        <img src="/china/msdn/images/giftregistries.gif" width="118" height="30" border="0"/><br/>
                        <p>在不久的将来就可实现此功能。</p>
                     </td>
                  </tr>
               </table>
            </td>
            <td width="11">
               <img src="/china/msdn/images/spacer.gif" width="1" height="1" border="0"/>
            </td>
         </tr>
      </table>
   </xsl:template>

<xsl:template name="pageright"/>

</xsl:stylesheet>

此样式表包含上文介绍的 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 类似于以下代码:

<book>
  <oid>64</oid>
  <definitionname>SDKBook</definitionname>
  <cy_list_price displayname="价格">19.99</cy_list_price>
  <originalprice displayname="购买价">19.99</originalprice>
  <i_classtype>4</i_classtype>
  <parentoid>-1</parentoid>
  <productid>
    Microsoft Age of Empires II: The Age of Kings: Inside Moves
  </productid>
  <variantid/>
  <title displayname="标题">Microsoft Age of Empires II: The Age of Kings: Inside Moves</title>
  <isbn displayname="ISBN">0-7356-0513-0</isbn>
  <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_height>120</image_height>
  <image_width>120</image_width>
  <author displayname="作者">Microsoft Corporation</author>
  <name displayname="名称">Microsoft Age of Empires II: The Age of Kings: Inside Moves</name>
  <pagecount displayname="页数">280</pagecount>
  <producturl displayname="产品信息Url">a href=http://mspress.microsoft.com/prod/books/2388.htm  target =_a   http://mspress.microsoft.com/prod/books/2388.htm /a</producturl>
  <publication_year displayname="出版年份">1999</publication_year>
  <publisher displayname="出版者">Microsoft Press</publisher>
  <reading_level displayname="阅读级别:">所有级别</reading_level>
  <catalogname>书籍</catalogname>
</book>

如果代码需要下钻到更深的目录并检索其中某个类别的内容,可以使用 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 样式表来呈现这些数据。

<getproduct>
  <book>
    <catalogname>书籍</catalogname>
    <definitionname>SDKBook</definitionname>
    <cy_list_price displayname="价格">19.99</cy_list_price>
    <originalprice displayname="购买价">19.99</originalprice>
    <i_classtype>4</i_classtype>
    <productid>Microsoft Age of Empires II: The Age of Kings: Inside Moves</productid>
    <variantid/>
    <author displayname="作者">Microsoft Corporation</author>
    <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_height>120</image_height>
    <image_width>120</image_width>
    <isbn displayname="ISBN">0-7356-0513-0</isbn>
    <name displayname="名称">Microsoft Age of Empires II: The Age of Kings: Inside Moves</name>
    <pagecount displayname="页数">280</pagecount>
    <producturl displayname="Product Info. Url">a href=http://mspress.microsoft.com/prod/books/2388.htm  target =_a   http://mspress.microsoft.com/prod/books/2388.htm /a</producturl>
    <publication_year displayname="出版年份">1999</publication_year>
    <publisher displayname="出版者">Microsoft Press</publisher>
    <reading_level displayname="阅读级别:">所有级别</reading_level>
    <title displayname="标题">Microsoft Age of Empires II: The Age of Kings: Inside Moves</title>
  </book>
</getproduct>

搜索目录

除了提供用户用于浏览目录的页面之外,一个高效的站点还应提供搜索功能。在 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 具有以下格式:

<searchscope>书籍</searchscope>
<searchstring>Age of Empires</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>
<!-- 为了表达得更为清楚,此处省略了其它结果 -->
<selectiontitle>根据搜索标准 'Age of Empires' 找到的产品。</selectiontitle>
</searchresults>

“购物篮”管理

与大多数 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。此阶段涉及以下两个组件:

相关推荐

Global site tag (gtag.js) - Google Analytics