Tailspin Spyworks指南第五讲:业务逻辑

Part 5: Business Logic
第五讲:业务逻辑

By  Joe Stagner |July 21, 2010
Translated By   litdwg  |March 12,2014

Tailspin Spyworks demonstrates how extraordinarily simple it is to create powerful, scalable applications for the .NET platform. It shows off how to use the great new features in ASP.NET 4 to build an online store, including shopping, checkout, and administration.

通过Tailspin Spyworks 演示在.NET平台创建功能强大,结构良好的应用程序有多么简单。演示如何使用ASP.NET 4的新特性创建一个包含购物、结算和管理功能的在线网店。

This tutorial series details all of the steps taken to build the Tailspin Spyworks sample application.  Part 5 adds some business logic.

本系列指南对构建案例程序的每一步做了详细的解释。第五部分添加一些业务逻辑。

添加Business Logic

添加业务逻辑

We want our shopping experience to be available whenever someone visits our web site. Visitors will be able to browse and add items to the shopping cart even if they are not registered or logged in. When they are ready to check out they will be given the option to authenticate and if they are not yet members they will be able to create an account.

我们希望访问我们网站的任何人都能体验购物。无论顾客是否登录或注册都可以浏览产品,添加产品到购物车。当顾客准备结算时再进行身份验证,如果顾客还不是我们的用户,可能会完成注册操作。

This means that we will need to implement the logic to convert the shopping cart from an anonymous state to a "Registered User" state.

这也就是说我们需要实现购物车从匿名状态到“已注册用户”状态转换的逻辑。

Let's create a directory named "Classes" then Right-Click on the folder and create a new "Class" file named MyShoppingCart.cs

创建一个名为“Classes”的文件夹,在其上右击选择创建新的类文件,命名为MyShoppingCart.cs。

As previously mentioned we will be extending the class that implements the MyShoppingCart.aspx page and we will do this using .NET's powerful "Partial Class" construct.

之前提到我们将在实现MyShoppingCart.aspx 页时对这个类进行扩展,这是通过.NET强大的”Partial Class“结构实现的。

The generated call for our MyShoppingCart.aspx.cs file looks like this.

自动生成的MyShoppingCart.aspx.cs文件是这样的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace TailspinSpyworks
{
    public partial class MyShoppingCart : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {

        }
    }
}

注意关键词 "partial" .

我们刚创建的类,自动生成的代码是这样的:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;

namespace TailspinSpyworks.Classes
{
    public class MyShoppingCart
    {
    }
}

We will merge our implementations by adding the partial keyword to this file as well.

在此添加partial关键词,将它们合二为一。

Our new class file now looks like this.

修改后的类文件应该是这样的:

namespace TailspinSpyworks.Classes
{
    public partial class MyShoppingCart
    {
    }
}

The first method that we will add to our class is the "AddItem" method. This is the method that will ultimately be called when the user clicks on the "Add to Art" links on the Product List and Product Details pages.

第一个要添加的方法是”AddItem“,用户在产品列表或产品详情页面点击”添加到购物车“时将调用此方法。

Append the following to the using statements at the top of the page.

在首部添加引用:

usingTailspinSpyworks.Data_Access;

And add this method to the MyShoppingCart class.

 MyShoppingCart类添加如下方法:

//------------------------------------------------------------------------------------+
public void AddItem(string cartID, int productID, int quantity)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try 
      {
      var myItem = (from c in db.ShoppingCarts where c.CartID == cartID && 
                              c.ProductID == productID select c).FirstOrDefault();
      if(myItem == null)
        {
        ShoppingCart cartadd = new ShoppingCart();
        cartadd.CartID = cartID;
        cartadd.Quantity = quantity;
        cartadd.ProductID = productID;
        cartadd.DateCreated = DateTime.Now;
        db.ShoppingCarts.AddObject(cartadd);
        }
      else
        {
        myItem.Quantity += quantity;
        }
      db.SaveChanges();
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Add Item to Cart - " + 
                                                          exp.Message.ToString(), exp);
      }
   }
}

We are using LINQ to Entities to see if the item is already in the cart. If so, we update the order quantity of the item, otherwise we create a new entry for the selected item

使用LINQ to Entities查看购物车是否存在此商品,如果存在则更新数量,不存在将创建新记录。

In order to call this method we will implement an AddToCart.aspx page that not only class this method but then displayed the current shopping a=cart after the item has been added.

为了调用此方法,创建 AddToCart.aspx页面,在添加商品后显示当前购物车的详情。

Right-Click on the solution name in the solution explorer and add and new page named AddToCart.aspx as we have done previously.

和之前一样的方法,添加AddToCart.aspx。

While we could use this page to display interim results like low stock issues, etc, in our implementation, the page will not actually render, but rather call the "Add" logic and redirect.

此页面并不会显示,而是调用”添加“逻辑,然后重定向了。

To accomplish this we'll add the following code to the Page_Load event.

为完成此功能,在Page_Load添加代码,如下:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using System.Diagnostics;

namespace TailspinSpyworks
{
    public partial class AddToCart : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            string rawId = Request.QueryString["ProductID"];
            int productId;
            if (!String.IsNullOrEmpty(rawId) && Int32.TryParse(rawId, out productId))
            {
                MyShoppingCart usersShoppingCart = new MyShoppingCart();
                String cartId = usersShoppingCart.GetShoppingCartId();
                usersShoppingCart.AddItem(cartId, productId, 1);
            }
            else
            {
                Debug.Fail("ERROR : We should never get to AddToCart.aspx 
                                                   without a ProductId.");
                throw new Exception("ERROR : It is illegal to load AddToCart.aspx 
                                                   without setting a ProductId.");
            }
            Response.Redirect("MyShoppingCart.aspx");
        }
    }
}

Note that we are retrieving the product to add to the shopping cart from a QueryString parameter and calling the AddItem method of our class.

注意,我们是通过QueryString参数传递要加入购物车的商品,然后调用AddItem方法。

Assuming no errors are encountered control is passed to the SHoppingCart.aspx page which we will fully implement next. If there should be an error we throw an exception.

如果没有错误则跳转到 SHoppingCart.aspx page 页面(此页面随后实现),如果有错则抛出异常。

Currently we have not yet implemented a global error handler so this exception would go unhandled by our application but we will remedy this shortly.

当前我们还没有实现全局的错误处理机制,此异常将直接抛出不会被处理,随后将修复这一问题。

Note also the use of the statement Debug.Fail() (available via using System.Diagnostics;)

注意Debug.Fail()的用法。

Is the application is running inside the debugger, this method will display a detailed dialog with information about the applications state along with the error message that we specify.

如果程序是处在调试运行状态,此方法将弹出对话框,显示程序的状态和指明的错误信息。

When running in production the Debug.Fail() statement is ignored.

当运行在发行版本时,此语句将被忽略。

You will note in the code above a call to a method in our shopping cart class names "GetShoppingCartId".

注意,语句中调用了另一个方法”GetShoppingCartId“。

Add the code to implement the method as follows.

添加如下代码实现此方法。

Note that we've also added update and checkout buttons and a label where we can display the cart "total".

注意,我们还添加了更新和结算按钮,还有一个现实总价的标签。

public const string CartId = "TailSpinSpyWorks_CartID";

//--------------------------------------------------------------------------------------+
public String GetShoppingCartId()
{
  if (Session[CartId] == null)
     {
     Session[CartId] = System.Web.HttpContext.Current.Request.IsAuthenticated ? 
                                        User.Identity.Name : Guid.NewGuid().ToString();
     }
  return Session[CartId].ToString();
}

We can now add items to our shopping cart but we have not implemented the logic to display the cart after a product has been added.

我们现在可以把商品添加到购物车了,但还没有实现显示购物车详情的逻辑。

So, in the MyShoppingCart.aspx page we'll add an EntityDataSource control and a GridVire control as follows.

因此,在MyShoppingCart.aspx页面添加EntityDataSource 控件和GridView控件,代码如下:

<div id="ShoppingCartTitle" runat="server" class="ContentHead">Shopping Cart</div>
<asp:GridView ID="MyList" runat="server" AutoGenerateColumns="False" ShowFooter="True" 
                          GridLines="Vertical" CellPadding="4"
                          DataSourceID="EDS_Cart"  
                          DataKeyNames="ProductID,UnitCost,Quantity" 
                          CssClass="CartListItem">              
  <AlternatingRowStyle CssClass="CartListItemAlt" />
  <Columns>
    <asp:BoundField DataField="ProductID" HeaderText="Product ID" ReadOnly="True" 
                                          SortExpression="ProductID"  />
    <asp:BoundField DataField="ModelNumber" HeaderText="Model Number" 
                                            SortExpression="ModelNumber" />
    <asp:BoundField DataField="ModelName" HeaderText="Model Name" 
                                          SortExpression="ModelName"  />
    <asp:BoundField DataField="UnitCost" HeaderText="Unit Cost" ReadOnly="True" 
                                         SortExpression="UnitCost" 
                                         DataFormatString="{0:c}" />         
    <asp:TemplateField> 
      <HeaderTemplate>Quantity</HeaderTemplate>
      <ItemTemplate>
         <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server" 
                      Text='<%# Bind("Quantity") %>'></asp:TextBox> 
      </ItemTemplate>
    </asp:TemplateField>           
    <asp:TemplateField> 
      <HeaderTemplate>Item Total</HeaderTemplate>
      <ItemTemplate>
        <%# (Convert.ToDouble(Eval("Quantity")) *  
             Convert.ToDouble(Eval("UnitCost")))%>
      </ItemTemplate>
    </asp:TemplateField>
    <asp:TemplateField> 
    <HeaderTemplate>Remove Item</HeaderTemplate>
      <ItemTemplate>
        <center>
          <asp:CheckBox id="Remove" runat="server" />
        </center>
      </ItemTemplate>
    </asp:TemplateField>
  </Columns>
  <FooterStyle CssClass="CartListFooter"/>
  <HeaderStyle  CssClass="CartListHead" />
</asp:GridView>

<div>
  <strong>
    <asp:Label ID="LabelTotalText" runat="server" Text="Order Total : ">  
    </asp:Label>
    <asp:Label CssClass="NormalBold" id="lblTotal" runat="server" 
                                                   EnableViewState="false">
    </asp:Label>
  </strong> 
</div>
<br />
<asp:imagebutton id="UpdateBtn" runat="server" ImageURL="Styles/Images/update_cart.gif" 
                                onclick="UpdateBtn_Click"></asp:imagebutton>
<asp:imagebutton id="CheckoutBtn" runat="server"  
                                  ImageURL="Styles/Images/final_checkout.gif"    
                                  PostBackUrl="~/CheckOut.aspx">
</asp:imagebutton>
<asp:EntityDataSource ID="EDS_Cart" runat="server" 
                      ConnectionString="name=CommerceEntities" 
                      DefaultContainerName="CommerceEntities" EnableFlattening="False" 
                      EnableUpdate="True" EntitySetName="ViewCarts" 
                      AutoGenerateWhereClause="True" EntityTypeFilter="" Select=""                         
                      Where="">
  <WhereParameters>
    <asp:SessionParameter Name="CartID" DefaultValue="0" 
                                        SessionField="TailSpinSpyWorks_CartID" />
  </WhereParameters>
</asp:EntityDataSource>

Call up the form in the designer so that you can double click on the Update Cart button and generate the click event handler that is specified in the declaration in the markup.

在设计视图双击UpdateCart按钮,生成已经在标记中定义的事件响应函数。

We'll implement the details later but doing this will let us build and run our application without errors.

随后再实现此函数的具体功能,这里先生成空函数避免运行程序出错。

When you run the application and add an item to the shopping cart you will see this.

运行程序,将产品添加到购物车后的效果如下:

Note that we have deviated from the "default" grid display by implementing three custom columns.

注意:我们实现了三个自定义列。

The first is an Editable, "Bound" field for the Quantity:

第一个是可编辑的”Bound“域,用来显示数量。

    <asp:TemplateField> 
      <HeaderTemplate>Quantity</HeaderTemplate>
      <ItemTemplate>
         <asp:TextBox ID="PurchaseQuantity" Width="40" runat="server" 
                      Text='<%# Bind("Quantity") %>'></asp:TextBox> 
      </ItemTemplate>
    </asp:TemplateField>

The next is a "calculated" column that displays the line item total (the item cost times the quantity to be ordered):

”calculated“列显示此商品的总价(单价乘以数量):

     <asp:TemplateField> 
      <HeaderTemplate>Item Total</HeaderTemplate>
      <ItemTemplate>
        <%# (Convert.ToDouble(Eval("Quantity")) *  
             Convert.ToDouble(Eval("UnitCost")))%>
      </ItemTemplate>
    </asp:TemplateField>

Lastly we have a custom column that contains a CheckBox control that the user will use to indicate that the item should be removed from the shopping chart.

最后一个自定义列包含选择框控件,用户通过选中此控件将不需要的商品从购物车移除。

    <asp:TemplateField> 
    <HeaderTemplate>Remove Item</HeaderTemplate>
      <ItemTemplate>
        <center>
          <asp:CheckBox id="Remove" runat="server" />
        </center>
      </ItemTemplate>
    </asp:TemplateField>

As you can see, the Order Total line is empty so let's add some logic to calculate the Order Total.

如你所见,总价现在还没有显示,添加一些逻辑来计算总价。

We'll first implement a "GetTotal" method to our MyShoppingCart Class.

首先在MyShoppingCart类实现”GetTotal“方法。

In the MyShoppingCart.cs file add the following code.

代码如下:

//--------------------------------------------------------------------------------------+
public decimal GetTotal(string cartID)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    decimal cartTotal = 0;
    try
      {
      var myCart = (from c in db.ViewCarts where c.CartID == cartID select c);
      if (myCart.Count() > 0)
         {
         cartTotal = myCart.Sum(od => (decimal)od.Quantity * (decimal)od.UnitCost);
         }
       }
     catch (Exception exp)
       {
       throw new Exception("ERROR: Unable to Calculate Order Total - " + 
exp.Message.ToString(), exp);
       }
     return (cartTotal);
     }
   }

Then in the Page_Load event handler we'll can call our GetTotal method. At the same time we'll add a test to see if the shopping cart is empty and adjust the display accordingly if it is.

然后在Page_Load事件处理函数中调用GetTotal方法。同时在其中测试购物车是否为空,如果为空时调整显示内容。

Now if the shopping cart is empty we get this:

如果为空时,显示如下:

And if not, we see our total.

如果不为空,可以看到总价。

However, this page is not yet complete.

此页面还没有做完。

We will need additional logic to recalculate the shopping cart by removing items marked for removal and by determining new quantity values as some may have been changed in the grid by the user.

添加新的逻辑实现用户移除商品或修改商品数量时重新计算购物车。

Lets add a "RemoveItem" method to our shopping cart class in MyShoppingCart.cs to handle the case when a user marks an item for removal.

 MyShoppingCart.cs添加”RemoveItem“方法,处理移除商品的操作。

//------------------------------------------------------------------------------------+
public void RemoveItem(string cartID, int  productID)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      var myItem = (from c in db.ShoppingCarts where c.CartID == cartID && 
                         c.ProductID == productID select c).FirstOrDefault();
      if (myItem != null)
         {
         db.DeleteObject(myItem);
         db.SaveChanges();
         }
      }
    catch (Exception exp)
      {
      throw new Exception("ERROR: Unable to Remove Cart Item - " + 
                                  exp.Message.ToString(), exp);
      }
    }
}

Now let's ad a method to handle the circumstance when a user simply changes the quality to be ordered in the GridView.

添加”UpdateItem“方法,处理修改数量的操作。

//--------------------------------------------------------------------------------------+
public void UpdateItem(string cartID, int productID, int quantity)
{
   using (CommerceEntities db = new CommerceEntities())
      {
      try
        {
        var myItem = (from c in db.ShoppingCarts where c.CartID == cartID && 
                                c.ProductID == productID select c).FirstOrDefault();
        if (myItem != null)
           {
           myItem.Quantity = quantity;
           db.SaveChanges();
           }
        }
     catch (Exception exp)
        {
        throw new Exception("ERROR: Unable to Update Cart Item - " +     
                                                        exp.Message.ToString(), exp);
        }
      }
}

With the basic Remove and Update features in place we can implement the logic that actually updates the shopping cart in the database. (In MyShoppingCart.cs)

在实现移除和更新功能的同时,完成对数据库的更新操作。

//-------------------------------------------------------------------------------------+
public void UpdateShoppingCartDatabase(String cartId, 
                                       ShoppingCartUpdates[] CartItemUpdates)
{
  using (CommerceEntities db = new CommerceEntities())
    {
    try
      {
      int CartItemCOunt = CartItemUpdates.Count();
      var myCart = (from c in db.ViewCarts where c.CartID == cartId select c);
      foreach (var cartItem in myCart)
        {
        // Iterate through all rows within shopping cart list
        for (int i = 0; i < CartItemCOunt; i++)
          {
          if (cartItem.ProductID == CartItemUpdates[i].ProductId)
             {
             if (CartItemUpdates[i].PurchaseQantity < 1 || 
   CartItemUpdates[i].RemoveItem == true)
                {
                RemoveItem(cartId, cartItem.ProductID);
                }
             else 
                {
                UpdateItem(cartId, cartItem.ProductID, 
                                   CartItemUpdates[i].PurchaseQantity);
                }
              }
            }
          }
        }
      catch (Exception exp)
        {
        throw new Exception("ERROR: Unable to Update Cart Database - " + 
                             exp.Message.ToString(), exp);
        }            
    }           
}

You'll note that this method expects two parameters. One is the shopping cart Id and the other is an array of objects of user defined type.

注意,此方法需要两个参数,一个是购物车Id,另一个是自定义类型的对象数组。

So as to minimize the dependency of our logic on user interface specifics, we've defined a data structure that we can use to pass the shopping cart items to our code without our method needing to directly access the GridView control.

为了最小化业务逻辑对用户接口的依赖,定义了一个数据结构,使用此结构可把购物车记录传递给代码,而无需在代码中直接访问GridView控件。

public struct ShoppingCartUpdates
{
   public int ProductId;
   public int PurchaseQantity;
   public bool RemoveItem;
}

In our MyShoppingCart.aspx.cs file we can use this structure in our Update Button Click Event handler as follows. Note that in addition to updating the cart we will update the cart total as well.

MyShoppingCart.aspx.cs文件的UpdateBtn_Click中使用此结构。注意除了更新购物车还要更新总价。

//--------------------------------------------------------------------------------------+
protected void UpdateBtn_Click(object sender, ImageClickEventArgs e)
{
  MyShoppingCart usersShoppingCart = new MyShoppingCart();
  String cartId = usersShoppingCart.GetShoppingCartId();

  ShoppingCartUpdates[] cartUpdates = new ShoppingCartUpdates[MyList.Rows.Count];
  for (int i = 0; i < MyList.Rows.Count; i++)
    {
    IOrderedDictionary rowValues = new OrderedDictionary();
    rowValues = GetValues(MyList.Rows[i]);
    cartUpdates[i].ProductId =  Convert.ToInt32(rowValues["ProductID"]);
    cartUpdates[i].PurchaseQantity = Convert.ToInt32(rowValues["Quantity"]); 

    CheckBox cbRemove = new CheckBox();
    cbRemove = (CheckBox)MyList.Rows[i].FindControl("Remove");
    cartUpdates[i].RemoveItem = cbRemove.Checked;
    }

   usersShoppingCart.UpdateShoppingCartDatabase(cartId, cartUpdates);
   MyList.DataBind();
   lblTotal.Text = String.Format("{0:c}", usersShoppingCart.GetTotal(cartId));
}

Note with particular interest this line of code:

特别要关注这一行代码:

rowValues = GetValues(MyList.Rows[i]);

GetValues() is a special helper function that we will implement in MyShoppingCart.aspx.cs as follows.

GetValues()函数在MyShoppingCart.aspx.cs中实现,代码如下:

//--------------------------------------------------------------------------------------+
public static IOrderedDictionary GetValues(GridViewRow row)
{
  IOrderedDictionary values = new OrderedDictionary();
  foreach (DataControlFieldCell cell in row.Cells)
    {
    if (cell.Visible)
      {
      // Extract values from the cell
      cell.ContainingField.ExtractValuesFromCell(values, cell, row.RowState, true);
      }
    }
    return values;
} 

This provides a clean way to access the values of the bound elements in our GridView control. Since our "Remove Item" CheckBox Control is not bound we'll access it via the FindControl() method.

提供了一个干净的方式访问GridView控件中绑定元素的值。”Remove Item“选择框控件不是绑定实现的,随后通过FindControl()方法来操作。

At this stage in your project's development we are getting ready to implement the checkout process.

我们已经为实现结算做好了准备。

Before doing so let's use Visual Studio to generate the membership database and add a user to the membership repository.

在处理结算之前,使用VS生成membership数据库,添加用户。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
东南亚位于我国倡导推进的“一带一路”海陆交汇地带,作为当今全球发展最为迅速的地区之一,近年来区域内生产总值实现了显著且稳定的增长。根据东盟主要经济体公布的最新数据,印度尼西亚2023年国内生产总值(GDP)增长5.05%;越南2023年经济增长5.05%;马来西亚2023年经济增速为3.7%;泰国2023年经济增长1.9%;新加坡2023年经济增长1.1%;柬埔寨2023年经济增速预计为5.6%。 东盟国家在“一带一路”沿线国家中的总体GDP经济规模、贸易总额与国外直接投资均为最大,因此有着举足轻重的地位和作用。当前,东盟与中国已互相成为双方最大的交易伙伴。中国-东盟贸易总额已从2013年的443亿元增长至 2023年合计超逾6.4万亿元,占中国外贸总值的15.4%。在过去20余年中,东盟国家不断在全球多变的格局里面临挑战并寻求机遇。2023东盟国家主要经济体受到国内消费、国外投资、货币政策、旅游业复苏、和大宗商品出口价企稳等方面的提振,经济显现出稳步增长态势和强韧性的潜能。 本调研报告旨在深度挖掘东南亚市场的增长潜力与发展机会,分析东南亚市场竞争态势、销售模式、客户偏好、整体市场营商环境,为国内企业出海开展业务提供客观参考意见。 本文核心内容: 市场空间:全球行业市场空间、东南亚市场发展空间。 竞争态势:全球份额,东南亚市场企业份额。 销售模式:东南亚市场销售模式、本地代理商 客户情况:东南亚本地客户及偏好分析 营商环境:东南亚营商环境分析 本文纳入的企业包括国外及印尼本土企业,以及相关上下游企业等,部分名单 QYResearch是全球知名的大型咨询公司,行业涵盖各高科技行业产业链细分市场,横跨如半导体产业链(半导体设备及零部件、半导体材料、集成电路、制造、封测、分立器件、传感器、光电器件)、光伏产业链(设备、硅料/硅片、电池片、组件、辅料支架、逆变器、电站终端)、新能源汽车产业链(动力电池及材料、电驱电控、汽车半导体/电子、整车、充电桩)、通信产业链(通信系统设备、终端设备、电子元器件、射频前端、光模块、4G/5G/6G、宽带、IoT、数字经济、AI)、先进材料产业链(金属材料、高分子材料、陶瓷材料、纳米材料等)、机械制造产业链(数控机床、工程机械、电气机械、3C自动化、工业机器人、激光、工控、无人机)、食品药品、医疗器械、农业等。邮箱:market@qyresearch.com

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值