受欢迎的博客标签

为nopcommerce自定义用户积分功能(1)

Published
由于此功能为定制功能,所以不方便把所有代码都发上来,而且功能或许还有BUG,有兴趣的同学可以一起研究,大家也可以访问中文站www.nopchina.com或者 www.nopcommerce.org或者加入QQ群101675096一起讨论。nopcommerce是开源的asp.net网店,从08年底出来第一个版本,截止本文发稿时最新版本为1.40,有兴趣的同学可以去http://www.nopcommerce.com了解详细情况。nopcommerce本身不带用户积分功能,至少1.4还没有。基本目前的用户需求,等不到官方发布这个功能了。我们来自己实现一个简单的用户积分,积分历史记录暂时通过SQL查询先来看看基本需求和大概流程。首先这个积分不像凡客那种买个东西就几千上万分,我们所定义的积分就是一种虚拟货币,而且和网店主要货币的汇率为1,我们的 目的很简单,用户在每次购物以后可以给用户的帐户里添加X元的货币,这个货币的名字用户可以自定义,比如这个网店我想取名叫A$,那个网店我想叫 Z$,或者直接叫RMB也行。为了简单起见我们规定在每次购物以后用户可以得到1%的货币(为了更好的区分,以下用“积分”代替),而且积分是扣掉打折以 后再开始计算的。例如用户购买了200元的东西,打折10%即20元,这样用户需要支付180元,所得积分为180*1%=1.8元。当然这个百分比是可 以随时更改的。要注意的是用户下单后不是马上得到积分而是在网店收到钱以后,由管理员在更新状态时同时给用户添加积分。管理员也可以选择不给此人增加积分,或者给此人增 加另一个数额的积分,而且在用户管理页面管理员也可以直接对用户的积分进行修改。为了把积分概念深入人心,用户下单以后也要加到邮件通知里。综上所述我们要的流程如下:1.用户下单,结帐,email通知用户下单成功并能获得X元的积分,此积分下一次可以在购物时使用。2.用户付款以后管理员给用户添加积分3.用户获得积分,下次可以使用。我们先来进行必要的设置,包括本地化、全局设置以及修改数据库。configuration->all settings->add new,添加两项设置“RewardPercent”和“RewardPoint”,分别为获得积分的百分比和积分的货币符号。content management->localization->add new,添加两项“Promotion.RewardMessageForGuest”和“Promotion.RewardPointName”分别为 guest user在结帐时的提示信息和本地化以后的货币符号,其实这个可以用上边的积分符号代替。然后,我们在数据库里添加一个新的表CREATE TABLE [dbo].[Nop_RewardAudit]([RewardID] [int] IDENTITY(1,1) NOT NULL,[OrderID] [int] NOT NULL,[RewardTime] [datetime] NOT NULL,[Status] [nchar](10) NOT NULL,[Amount] [money] NOT NULL,[RewardDetails] [nvarchar](2000) NULL,CONSTRAINT [PK_Nop_RewardAudit] PRIMARY KEY CLUSTERED([RewardID] ASC)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 80) ON [PRIMARY]) ON [PRIMARY]这个表用来做audit,目前还没有把audit的界面做出来,不过功能倒是设计出来了,有空加上。然后分别再向customer表和order表加一个money字段:ALTER TABLE dbo.Nop_Customer SET (LOCK_ESCALATION = TABLE)order表的类似,我把代码搞没了,大家自己加上应该没问题。接下来老丁我还得改几个存储过程,不过强烈建议放到后边改,是更新用户和插入订单的。CREATE PROCEDURE dbo.Nop_CustomerRewardPointCalculation@cID int,@orderID    int,@points money,@details nvarchar(2000)ASupdate nop_customerset rewardPoint = rewardPoint + @pointswhere CustomerID = @cIDinsert into Nop_RewardAudit(OrderID,RewardTime,Status,Amount,RewardDetails)values(@orderID,getdate(),””,@points,@details)还有:CREATE PROCEDURE dbo.Nop_CustomerSetRewardPoint@cID int,@orderID    int,@points money,@details nvarchar(2000)ASupdate nop_customerset rewardPoint = @pointswhere CustomerID = @cIDinsert into Nop_RewardAudit(OrderID,RewardTime,Status,Amount,RewardDetails)values(0,getdate(),””,@points,@details)RETURN更新用户信息:CREATE PROCEDURE [dbo].[Nop_CustomerUpdate](@CustomerId int,@CustomerGUID uniqueidentifier,@Email nvarchar(255),@PasswordHash nvarchar(255),@SaltKey nvarchar(255),@AffiliateID int,@BillingAddressID int,@ShippingAddressID int,@LastPaymentMethodID int,@LastAppliedCouponCode nvarchar(100),@LanguageID int,@CurrencyID int,@TaxDisplayTypeID int,@IsTaxExempt bit,@IsAdmin bit,@IsGuest bit,@IsForumModerator bit,@TotalForumPosts int,@Signature nvarchar(300),@AdminComment nvarchar(4000),@Active bit,@Deleted bit,@RegistrationDate datetime,@TimeZoneID nvarchar(200),@Username nvarchar(100),@AvatarID int,@RewardPoint money)ASBEGINUPDATE [Nop_Customer]SETCustomerGUID=@CustomerGUID,Email=@Email,PasswordHash=@PasswordHash,SaltKey=@SaltKey,AffiliateID=@AffiliateID,BillingAddressID=@BillingAddressID,ShippingAddressID=@ShippingAddressID,LastPaymentMethodID=@LastPaymentMethodID,LastAppliedCouponCode=@LastAppliedCouponCode,LanguageID=@LanguageID,CurrencyID=@CurrencyID,TaxDisplayTypeID=@TaxDisplayTypeID,IsTaxExempt=@IsTaxExempt,IsAdmin=@IsAdmin,IsGuest=@IsGuest,IsForumModerator=@IsForumModerator,TotalForumPosts=@TotalForumPosts,[Signature]=@Signature,AdminComment=@AdminComment,Active=@Active,Deleted=@Deleted,RegistrationDate=@RegistrationDate,TimeZoneID=@TimeZoneID,Username=@Username,AvatarID=@AvatarID,RewardPoint = @RewardPointWHERE[CustomerId] = @CustomerIdENDNop_OrderInsert的就不用我再放上来了吧,按上边这个SP来改问题不大。 nopcommerce的总体架构我们就不多说了,从WEB到逻辑再到数据库访问都有地方要修改,本文主要演示逻辑和数据库访问的修改,Web界面上的后边会提到如何调用。 首先我们有一个新的reward point provider,那么在web.config的sectionGroup里要加上:<section name=”RewardProvider” type=”NopSolutions.NopCommerce.DataAccess.DBProviderSection, Nop.DataAccess” requirePermission=”false”/> 在nopDataProviders的最后也要加上: <RewardProvider defaultProvider=”SQLRewardProvider”><providers><add name=”SQLRewardProvider” type=”NopSolutions.NopCommerce.DataAccess.Promo.SQLRewardProvider, Nop.DataAccess.SqlServer” connectionStringName=”NopSqlConnection”/></providers></RewardProvider> NopContext里也要加个字段用来保存当前session下的积分值: public decimal RewardPoint{get {if (HttpContext.Current.Session["RewardPoint"] == null)return decimal.Zero; decimal ret = decimal.Zero;if (decimal.TryParse(HttpContext.Current.Session["RewardPoint"].ToString(), out ret))return ret;elsereturn decimal.Zero;}set{HttpContext.Current.Session["RewardPoint"] = value;}} 现在再来看数据库抽象接口的代码,各个方法什么意思想必大家一看就懂: namespace NopSolutions.NopCommerce.DataAccess.Promo{[DBProviderSectionName("nopDataProviders/RewardProvider")]public abstract partial class DBRewardProvider : BaseDBProvider{public abstract bool RewardCalculation(decimal point, int customerID, int orderid, string details);public abstract DBCustomerCollection CustomerRewardList();public abstract DBRewardAuditCollection GetAllAuditList(DateTime from, DateTime to);public abstract DBRewardAuditCollection GetAuditByOrderID(DateTime from, DateTime to, int OrderID);public abstract bool RewardPointSetTo(decimal point, int customerID, string details);}} DBRewardAudit的定义: namespace NopSolutions.NopCommerce.DataAccess.Promo{public partial class DBRewardAudit : BaseDBEntity{public int RewardID { get; set; }public int OrderID { get; set; }public DateTime RewardTime { get; set; }public string Status { get; set; }public decimal Amount { get; set; }public string RewardDetails { get; set; }}} 按照标配来说还得要个DBRewardAuditCollection,我形式上加了这个文件,但没实质内容,如果管理员要求在登录网店后看到历史记录,可以用这个。 数据层的DBCustomer还要加上个RewardPoint属性: namespace NopSolutions.NopCommerce.DataAccess.CustomerManagement{public partial class DBCustomer : BaseDBEntity{public decimal RewardPoint { get; set; }}} 数据库层的UpdateCustomer方法的参数要修改,把reward point属性传进去,SQL里update的时候也能更新积分,在这儿就省略了。DBOrder也要加同样的public decimal RewardPoint { get; set; }属性。InsertOrder方法也要把RewardPoint包含进来。 具体的数据访问层,我这儿有个SQLRewardProvider.cs供参考。SQLRewardProvider 然后再看看业务逻辑层的RewardManager.cs,RewardManager,简单的代码,就不用再描述了。 OK到这儿大部分接口已经实现了,只等客户端代码调用了,所有的逻辑层,web层的代码都可以称为客户端代码,那么我们来试试下订单后消耗积分的情况。OrderManager.cs 中的PlaceOrder, …. Order order = InsertOrder(OrderGuid,    //这儿注意,用刚才修改过的方法,把积分传进去。 …. 等有了OrderID = order.OrderID;这句,订单也生成了,那么就要扣掉相应的积分了,直接在这句后边加上: //reward functionif (NopContext.Current.RewardPoint > customer.RewardPoint)//reward point cannot greater than customer’s point.NopContext.Current.RewardPoint = customer.RewardPoint; if (OrderID >= 0 && NopContext.Current.RewardPoint > decimal.Zero)//success, deduct reward pointsRewardManager.RewardCalculation(decimal.Zero – NopContext.Current.RewardPoint, customer.CustomerID, OrderID,”Order placed with ” + RewardManager.RewardName + NopContext.Current.RewardPoint.ToString()); 然后再接着往下走找到SendOrderPlacedStoreOwnerNotification和SendOrderPlacedCustomerNotification,里边的方法改改,让email可以支持reward point的消息: 首先要定义这些消息的token是什么,在GetListOfAllowedTokens里添加: //added comments and reward points – by dingsea:allowedTokens.Add(“%Order.RewardPointApplied%”);allowedTokens.Add(“%Customer.CurrentRewardPoint%”);allowedTokens.Add(“%Customer.WinRewardPoint%”);allowedTokens.Add(“%Settings.RewardName%”);//ends here. ReplaceMessageTemplateTokens里加上如下字段以方便在正则替换的时候找到对应的文字: //added: dingseatokens.Add(“Order.RewardPointApplied”, HttpUtility.HtmlEncode(order.RewardPoint.ToString(“N2″)));tokens.Add(“Customer.WinRewardPoint”,HttpUtility.HtmlEncode((order.OrderTotal * RewardManager.RewardPercentage).ToString(“N2″)));tokens.Add(“Customer.CurrentRewardPoint”,HttpUtility.HtmlEncode(NopContext.Current.User.RewardPoint.ToString(“N2″)));tokens.Add(“Settings.RewardName”,HttpUtility.HtmlEncode(RewardManager.RewardName)); 既然都到这里了,那也把nopcommerce的一个小BUG也补上: ProductListToHtmlTable方法中没有打折信息(在order details页面也有同样问题,同学们自行加上吧),所以我把打折和积分信息一起加上了: //fixed by dingsea – discount is missingstring CusDiscount = string.Empty;string CusRewardPoint = string.Empty; …… //fixed by dingsea – discount and reward pointCusDiscount = order.OrderDiscount == decimal.Zero ? string.Empty : PriceHelper.FormatPrice(order.OrderDiscount, true, order.CustomerCurrencyCode, language, false);CusRewardPoint = order.RewardPoint.ToString(“N2″); ……. //fixed by dingsea – discount and reward pointif(CusDiscount!=string.Empty)sb.AppendLine(“<tr><td style=\”text-align:right;\” colspan=\”3\”><strong>” + LocalizationManager.GetLocaleResourceString(“ShoppingCart.Sub-TotalDiscount”, LanguageID) + “</strong></td> <td style=\”text-align:right;\”><strong>” + CusDiscount + “</strong></td></tr>”);sb.AppendLine(“<tr><td style=\”text-align:right;\” colspan=\”3\”><strong>” + LocalizationManager.GetLocaleResourceString(“Promotion.RewardPointName”, LanguageID) + “</strong></td> <td style=\”text-align:right;\”><strong>” + CusRewardPoint + “</strong></td></tr>”);//fix ends 于是呼,在你的template->message template里订单生成时向用户和店主发的email里就可以支持积分的token了。 至于在web如何调用,那就比较容易了,在你想要的页面加上一个textbox,确保用户输入的是decimal,那就够了,这个数字会存到session里直到用户下单。而且在管理界面,利用rewardmanager的方法可以直接操作积分,方便快捷。  .