您的位置:首页 > 其它

DiscuzNT商品交易插件设计之[信用机制]

2009-12-16 11:21 393 查看
在商品交易过程中,信用机制的引入是至关重要的,我们在这里参考的是discuz的做法(其实它最终是采

用类似TAOBAO的好评机制来实现的)。所以在每笔交易结束时,都会要求买卖双方进行互评,以便为信用机

制提供数据。而这里所使用的信用等级信息是参考discuz的数据进行相应级别设置的,其“信用等级”表

(dnt_goodscreditrules)结构参见下图所示:



其初始化数据设置在后台管理中显示如下:



好了,现在有了评分等级的依据,下面就开始介绍一下关于信用数据是如何操作的了。

首页打开源码包,在Discuz.Mall项目下的Pages文件夹中找到goodsrate.cs文件(这个就是买卖双方互
评的页面,因为之前的文章中已做过介绍,这里就不再介绍了),其相应的逻辑代码如下所示(评见注释):





//如果是提交


if (ispost)
{
//声明并初始化评价对象数据
Goodsrateinfo goodsrateinfo = new Goodsrateinfo();
goodsrateinfo.Ip = DNTRequest.GetIP();
goodsrateinfo.Postdatetime = DateTime.Now;
goodsrateinfo.Price = goodstradelog.Number * goodstradelog.Price + goodstradelog.Transportfee;
goodsrateinfo.Ratetype = DNTRequest.GetInt("ratetype", 0);
goodsrateinfo.Uid = userid;
goodsrateinfo.Username = username;
goodsrateinfo.Message = DNTRequest.GetString("message");
goodsrateinfo.Goodstradelogid = goodstradelog.Id;
goodsrateinfo.Goodstitle = goodstradelog.Subject;
goodsrateinfo.Goodsid = goodstradelog.Goodsid;
goodsrateinfo.Explain = "";

if (goodstradelog.Buyerid == userid) //买家
{
goodsrateinfo.Uidtype = 2;
goodsrateinfo.Ratetouid = goodstradelog.Sellerid;
goodsrateinfo.Ratetousername = goodstradelog.Seller;

goodstradelog.Ratestatus = 2;
}
else //卖家
{
goodsrateinfo.Uidtype = 1;
goodsrateinfo.Ratetouid = goodstradelog.Buyerid;
goodsrateinfo.Ratetousername = goodstradelog.Buyer;

goodstradelog.Ratestatus = 1;
}


.. //接下来的代码会在后面进行介绍
}





 上面代码段中的如下对象实例化语句中,Goodsrateinfo对象声明如下:




Code
 /// <summary>
/// 商品评价类
/// </summary>
public class Goodsrateinfo
{
private int _id;
/// <summary>
/// 评价id
/// </summary>
public int Id
{
get { return _id; }
set { _id = value; }
}

private int _goodstradelogid;//商品交易日志ID
/// <summary>
/// 商品交易日志ID
/// </summary>
public int Goodstradelogid
{
get { return _goodstradelogid; }
set { _goodstradelogid = value; }
}

private string _message;//评价内容
/// <summary>
/// 评价内容
/// </summary>
public string Message
{
get { return _message; }
set { _message = value; }
}

private string _explain;//解释内容
/// <summary>
/// 解释内容
/// </summary>
public string Explain
{
get { return _explain; }
set { _explain = value; }
}

private string _ip;//ip
/// <summary>
/// ip
/// </summary>
public string Ip
{
get { return _ip; }
set { _ip = value; }
}

private int _uid;//用户id
/// <summary>
/// 用户id
/// </summary>
public int Uid
{
get { return _uid; }
set { _uid = value; }
}

private int _uidtype;//用户id类型 1:卖家 2:买家
/// <summary>
/// 用户id类型 1:卖家 2:买家
/// </summary>
public int Uidtype
{
get { return _uidtype; }
set { _uidtype = value; }
}

private int _ratetouid;//被评价人的uid
/// <summary>
/// 被评价人的uid
/// </summary>
public int Ratetouid
{
get { return _ratetouid; }
set { _ratetouid = value; }
}

private string _ratetousername;//被评价人的用户名
/// <summary>
/// 被评价人的用户名
/// </summary>
public string Ratetousername
{
get { return _ratetousername; }
set { _ratetousername = value; }
}

private string _username;//用户名
/// <summary>
/// 用户名
/// </summary>
public string Username
{
get { return _username; }
set { _username = value; }
}

private DateTime _postdatetime;//评价日期
/// <summary>
/// 评价日期
/// </summary>
public DateTime Postdatetime
{
get { return _postdatetime; }
set { _postdatetime = value; }
}

private int _goodsid;//商品id
/// <summary>
/// 商品id
/// </summary>
public int Goodsid
{
get { return _goodsid; }
set { _goodsid = value; }
}

private string _goodstitle;//商品标题
/// <summary>
/// 商品标题
/// </summary>
public string Goodstitle
{
get { return _goodstitle; }
set { _goodstitle = value; }
}

private decimal _price;//交易价格
/// <summary>
/// 交易价格
/// </summary>
public decimal Price
{
get { return _price; }
set { _price = value; }
}

private int _ratetype;//评价类型 1:好评 2:中评 3:差评
/// <summary>
/// 评价类型 1:好评 2:中评 3:差评
/// </summary>
public int Ratetype
{
get { return _ratetype; }
set { _ratetype = value; }
}
}

上面的类声明对应数据库中的dnt_goodsrates表,而该类的结构如下图所示:



在完成了上面所说的类实例初始化设置之后,就要通过如下代码段来向数据表插入数据了:

//接上面的goodsrate.cs说明
if (GoodsRates.CreateGoodsRate(goodsrateinfo) > 0) //如果评价成功
{
if(GoodsRates.RateClosed(goodsrateinfo.Goodstradelogid,goodstradelog.Sellerid,goodstradelog.Buyerid))
{
goodstradelog.Ratestatus = 3;
TradeLogs.UpdateTradeLog(goodstradelog, oldstatus); //更新交易的评价状态
}

GoodsUserCredits.SetUserCredit(goodsrateinfo, goodsrateinfo.Uidtype == 1 ? goodstradelog.Buyerid :

goodstradelog.Sellerid);

SetUrl(base.ShowGoodsAspxRewrite(goodsrateinfo.Goodsid));
SetMetaRefresh();
AddMsgLine("您的评价已经成功<br />(<a href=""" + base.ShowGoodsAspxRewrite(goodsrateinfo.Goodsid) + """>点击这里返回商品页面</a>)<br />");
}

上面代码中的GoodsRates.CreateGoodsRate(goodsrateinfo)一行即是插入评价信息的代码,其函数声明如下所示:

/// <summary>
/// 创建商品评价信息
/// </summary>
/// <param name="goodsrateinfo">要创建的商品评价信息</param>
/// <returns></returns>
public static int CreateGoodsRate(Goodsrateinfo goodsrateinfo)
{
return DbProvider.GetInstance().CreateGoodsRate(goodsrateinfo);
}

这里因为只是简单的向数据库中插入记录,所以就不多做解释了。请接着看上面代码中的语句:

if(GoodsRates.RateClosed(goodsrateinfo.Goodstradelogid,goodstradelog.Sellerid,goodstradelog.Buyerid))
{
goodstradelog.Ratestatus = 3;
TradeLogs.UpdateTradeLog(goodstradelog, oldstatus); //更新交易的评价状态
}

这段代码主要是对评价状态进行确定,并通过该数据更新当前的交易评价状态,其RateClosed方法如下所示:

/// <summary>
/// 指定的商品交易双方是否已评
/// </summary>
/// <param name="goodstradelogid">当前商品交易id</param>
/// <param name="selleruid">卖家用户id</param>
/// <param name="buyeruid">买家</param>
/// <returns></returns>
public static bool RateClosed(int goodstradelogid, int selleruid, int buyeruid)
{
int ratecount = 0;
foreach (Goodsrateinfo goodsrateinfo in GetGoodsRatesByTradeLogID(goodstradelogid))
{
if (goodsrateinfo.Uid == selleruid || goodsrateinfo.Uid == buyeruid)
{
ratecount = ratecount + 1;
}
}
return ratecount >= 2 ? true : false;
}

到这里还有一个重要工作没有说,就是上面代码中的如下语句:

GoodsUserCredits.SetUserCredit(goodsrateinfo, goodsrateinfo.Uidtype == 1 ? goodstradelog.Buyerid :
goodstradelog.Sellerid);

在解释上面方法之前有必要先介绍一下关于信用机制显示数据的一些设计思想。请先看一下“信用评价”的页面截图:



当前用户(截图中是admin)的评价按日期分为:最近一周,最近一个月,最近六个月,六个月前。按类型又分为

好评,中评,差评。而这些数字如果在显示时进行实时统计的话(实时查询上面的dnt_goodsrates表),在页面响应

时间上会很慢,因为要做的查询“工作量”上实在是太大了,让人“无法接受”,因此这里我引入了一个数据表来记录要显

示的相应数据,也就是dnt_goodsusercredits,下面即是该数据表的字典截图:



   这样我们就可以通过相应的数据行来显示“信用评价页面”中的各行内容了,这样做一是优化查询(只要一次查询)即

可,二是数据与前台页面对应关系明确,一目了然。

当然,在这里我们需要一些当前用户的信用初始化数据,用于在日后有评价记录进入系统时来累加该表中的相应字段,
而这项工作也交给了上面所述的方法GoodsUserCredits.SetUserCredit。

通过上面的介绍之后,我们就来看一下实际的代码是如何实现这些功能的。请看下面的SetUserCredit方法声明(位于
App_Code文件夹下的GoodsUserCredits.cs文件):   

/// <summary>
/// 设置用户信用(该方法会在用户进行评价之后调用)
/// </summary>
/// <param name="goodsrateinfo">评价信息</param>
/// <param name="uid">被评价人的uid</param>
/// <returns></returns>
public static bool SetUserCredit(Goodsrateinfo goodsrateinfo, int uid)
{
//获取被评价人的信用信息
GoodsusercreditinfoCollection goodsusercreditinfocoll = GetUserCreditList(uid);

//如果信用表中不存在, 则创建被评价人的信息
if (goodsusercreditinfocoll.Count == 0)
{
//当初始化信息失败时则返回
if (DbProvider.GetInstance().InitGoodsUserCredit(uid) <= 0)
{
return false;
}
//再次获取被评价人的信用信息
goodsusercreditinfocoll = GetUserCreditList(uid);
}

//用于绑定要更新的用户信用
Goodsusercreditinfo cur_creditinfo = null;
foreach (Goodsusercreditinfo goodsusercreditinfo in goodsusercreditinfocoll)
{
//查找符合条件的用户信用

if (goodsrateinfo.Uidtype == goodsusercreditinfo.Ratefrom && goodsrateinfo.Ratetype ==

goodsusercreditinfo.Ratetype)

{
cur_creditinfo = goodsusercreditinfo; break;
}
}

//当不为空, 表示找到了要更新的用户信用信息, 则进行下面的绑定操作
if (cur_creditinfo != null)
{

IDataReader __idatareader = DbProvider.GetInstance().GetGoodsRateCount(uid,

goodsrateinfo.Uidtype, goodsrateinfo.Ratetype);

//绑定新的查询数据
if (__idatareader.Read())
{
cur_creditinfo.Ratefrom = goodsrateinfo.Uidtype;
cur_creditinfo.Ratetype = goodsrateinfo.Ratetype;
cur_creditinfo.Oneweek = Convert.ToInt32(__idatareader["oneweek"].ToString());
cur_creditinfo.Onemonth = Convert.ToInt32(__idatareader["onemonth"].ToString());
cur_creditinfo.Sixmonth = Convert.ToInt32(__idatareader["sixmonth"].ToString());
cur_creditinfo.Sixmonthago = Convert.ToInt32(__idatareader["sixmonthago"].ToString());
UpdateUserCredit(cur_creditinfo);
}
__idatareader.Close();
}

return true;
}

上面代码段中的第一行即是获取相应的用户信用记录的方法,其返回的是一个GoodsusercreditinfoCollection
类型,该类型是一个集合类型,而GetUserCreditList方法声明如下所示:

/// <summary>
/// 获取指定用户id的信用信息
/// </summary>
/// <param name="userid">用户id</param>
/// <returns></returns>
public static GoodsusercreditinfoCollection GetUserCreditList(int userid)
{
return DTO.GetGoodsUserCreditList(DbProvider.GetInstance().GetGoodsUserCreditByUid(userid));
}

该函数用于将数据表中查询的信用数据转换(DTO)成为集合类型,而其GetGoodsUserCreditByUid方法所执行

的查询语句如下所示(注:这里要返回的记录行数为6,即是上面信用页面截图中所列的6行数据):

/// <summary>
/// 获取指定用户id的评价信息
/// </summary>
/// <param name="uid">用户id</param>
/// <returns></returns>
public IDataReader GetGoodsUserCreditByUid(int uid)
{
DbParameter parm = DbHelper.MakeInParam("@uid", (DbType)SqlDbType.Int, 4, uid);

return DbHelper.ExecuteReader(CommandType.Text, string.Format("SELECT * FROM [{0}goodsusercredits]

WHERE [uid] = @uid ORDER BY [id] ASC", BaseConfigs.GetTablePrefix), parm);

}

而上面SetUserCredit方法中的如下代码,即是在尚无用户初始数据时来进行初始化操作的:

//如果信用表中不存在, 则创建被评价人的信息
if (goodsusercreditinfocoll.Count == 0)
{
//当初始化信息失败时则返回
if (DbProvider.GetInstance().InitGoodsUserCredit(uid) <= 0)
{
return false;
}
//再次获取被评价人的信用信息
goodsusercreditinfocoll = GetUserCreditList(uid);
}

其SQL语句如下所示(注:插入了6行数据):




Code
/// <summary>
/// 初始化用户评价信息
/// </summary>
/// <param name="userid">用户id</param>
/// <returns></returns>
public int InitGoodsUserCredit(int userid)
{
DbParameter[] parms =
{
DbHelper.MakeInParam("@uid", (DbType)SqlDbType.Int, 4,userid),
};
StringBuilder sb_sql = new StringBuilder();
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 2, 1);");
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 2, 2);");
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 2, 3);");
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 1, 1);");
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 1, 2);");
sb_sql.Append("INSERT INTO [" + BaseConfigs.GetTablePrefix + "goodsusercredits] ([uid],[ratefrom],[ratetype]) VALUES (@uid, 1, 3);SELECT SCOPE_IDENTITY() AS 'id';");

return Utils.StrToInt(DbHelper.ExecuteDataset(CommandType.Text, sb_sql.ToString(), parms).Tables[0].Rows[0][0].ToString(), -1);
}

在初始化相应数据后再次实例化goodsusercreditinfocoll对象,以便在接下来的代码中进行数据更新绑定,
如下所示:

 


..
  //用于绑定要更新的用户信用
Goodsusercreditinfo cur_creditinfo = null;
foreach (Goodsusercreditinfo goodsusercreditinfo in goodsusercreditinfocoll)
{
//查找符合条件的用户信用
if (goodsrateinfo.Uidtype == goodsusercreditinfo.Ratefrom && goodsrateinfo.Ratetype == goodsusercreditinfo.Ratetype)
{
cur_creditinfo = goodsusercreditinfo; break;
}
}

//当不为空, 表示找到了要更新的用户信用信息, 则进行下面的绑定操作
if (cur_creditinfo != null)
{

IDataReader __idatareader = DbProvider.GetInstance().GetGoodsRateCount(uid,

goodsrateinfo.Uidtype, goodsrateinfo.Ratetype);

//绑定新的查询数据
if (__idatareader.Read())
{
cur_creditinfo.Ratefrom = goodsrateinfo.Uidtype;
cur_creditinfo.Ratetype = goodsrateinfo.Ratetype;
cur_creditinfo.Oneweek = Convert.ToInt32(__idatareader["oneweek"].ToString());
cur_creditinfo.Onemonth = Convert.ToInt32(__idatareader["onemonth"].ToString());
cur_creditinfo.Sixmonth = Convert.ToInt32(__idatareader["sixmonth"].ToString());
cur_creditinfo.Sixmonthago = Convert.ToInt32(__idatareader["sixmonthago"].ToString());
UpdateUserCredit(cur_creditinfo);
}
__idatareader.Close();
}

这样,我们可在新创建信用数据之后用已存在的信用数据来更新已有的信用评价记录数了(参见信用页面截图)。

当然说到这里,还有一个问题没有解决,就是数据时效性的问题。因为从上面的代码中可以看出,只有在发生评

价行为时,才会更新相应的数据,而如果用户很长时间没有进行交易的话,数据是不会进行更新的,而该问题的解决

方案包括:

1. 可以在用户登陆时进行异步统计更新
2. 可在后台加入计划任务,以便在指定时间对全部用户的交易信用数据进行统计
3. 在上述方面中直接进行更新

当然目前还没有最终实现,因为还不排除存在“更优解决方案”的可能性。当然这里所造成的问题并不十分严重,

因为在好评率上不会因为这个问题而造成误差,同时评价信息也未受到影响,这会为将来解决方案出台后进行信息统

计提供可靠的数据支持。

  到这时,主要的业务逻辑和设计思路解释完了,而相应的页面数据显示基本上是使用了ajax +js进行开发的,相

关内容大家可以参见相应的模板(eccredit.htm)及js文件(template_eccredit.js)说明。这里就不多加说明了。

  当然,这里的信用机制还缺少一些分支流程的支持。比如因为买卖双方的“误操作”,造成评价信息的不准确。所

以应该提供“申诉(投诉)机制”来让管理员或双方介入修改相应的评价信息数据等。而这些都有待日后进一步完善设计。

  好了,今天的内容就先到这里了。

关于DiscuzNT交易插件介绍的系列文章到此就要告一段落了, 谢谢大家的支持和关注:)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: