浅谈C# WinForm中实现基于角色的权限菜单

浅谈C# WinForm中实现基于角色的权限菜单




作者:CodingMouse            日期:2008年12月14日


转载请注明文章来源:http://blog.csdn.net/CodingMouse/archive/2008/12/14/3515969.aspx




基于角色的权限菜单功能的实现在有开发经验的程序员看来仅仅是小菜一碟,然而却让许多初学者苦不堪言。为此,我将我近期花了几天时间写的权限菜单写成文字贴上博客给初学者参考。由于自己也是一个正在努力学习的菜鸟,对问题的分析和见解必然不够透彻,还望过路的老师们多多批评为谢!


一、建立 SQL Server 数据库模型


1、原始方案

一共设立了五个实体模型:

A、操作员(Operator):存储系统登录用户的名称、密码、启用状态等信息。
B、权限组(RightsGroup):存储系统权限分组(即:权限角色)的名称等信息。
C、权限关系(RightsRelation):用于将A项和B项之间的多对多关系拆解成两个一对多关系。
D、权限列表(RightsList):存储系统菜单项(即:权限项)的标题、内部名称、权限状态等信息。
E、权限组关系(RightGroupRelation):用于将B项和D项之间的多对多关系拆解成两个一对多关系。

通过上面的描述可以清楚看到,C项和E项仅仅是为了拆解多对多关系而设立,实体关系变得相对复杂了点。稍作考虑便知,既然是使用 C# WinForm + SQL Server 来完成这一功能,则可以考虑使用实体类来模拟数据库模型,并将实体类打包成泛型集合后存储到 SQL Server 数据库的 varBinary(max) 字段。这样便可以将原始方案的数据库模型缩减成三个实体模型,降低了关系的复杂程度。将原始方案稍作修改后即可得到如下改进方案。 

2、改进方案

如上所述,改进后的方案仅包含如下三个实体模型:

A、操作员(Operator):存储系统登录用户的名称、密码、启用状态、权限集合等信息。
B、权限组(RightsGroup):存储系统权限分组(即:权限角色)的名称、权限集合等信息。
C、权限关系(RightsRelation):用于将A项和B项之间的多对多关系拆解成两个一对多关系。

很容易看出,仅将原始方案的 E 项更改为 A项和 B 项的字段,即可将实体关系复杂度降低 40%。现在我们来看看改进方案的 SQL Server 数据库实现脚本代码:


-- 判断是否存在 操作员信息表(Operator),如果存在,则删除表 Operator
if exists(Select * From SysObjects Where Name = 'Operator')
Drop Table [Operator]
Go 


-- 创建 操作员信息表(Operator)
Create Table [Operator]
(
-- 主键列,自动增长 标识种子为 1 
[Id] int identity(1,1) Constraint [PK_OperatorId] Primary Key,


-- 操作员姓名
[OperatorName] nVarChar(50) Constraint [UQ_OperatorName] Unique(OperatorName) Not Null,


-- 密码
[Password] nVarChar(50) Constraint [CK_Password] Check(len([Password])>=6) Not Null,


-- 操作员权限列表
[RightsList] varBinary(max) Null,


-- 用户当前状态
[State] bit Constraint [DF_State] Default('true') Constraint [CK_State] Check([State] in ('true','false')) Not Null,
)
Go


-- 判断是否存在 权限组信息表(RightsGroup),如果存在,则删除表 RightsGroup
if exists(Select * From SysObjects Where Name = 'RightsGroup')
Drop Table [RightsGroup]
Go 


-- 创建 权限组信息表(RightsGroup)
Create Table [RightsGroup]
(
-- 主键列,自动增长 标识种子为 1 
[Id] int Identity(1,1) Constraint [PK_RightsGroupId] Primary Key,

-- 权限组名称
[GroupName] nVarChar(50) Constraint[UQ_GroupName] Unique (GroupName) Not Null,


-- 组权限列表
[GroupRightsList] varBinary(max) Null,
)
Go


-- 判断是否存在权限关系表(RightsRelation),如果存在,则删除表 RightsRelation
if exists(Select * From SysObjects Where Name = 'RightsRelation')
drop table [RightsRelation]
Go


-- 创建 权限关系表(RightsRelation)
Create Table [RightsRelation]
(
-- 主键列,自动增长 标识种子为 1 
[Id] int Identity(1, 1) Constraint [PK_RightsRelationId] Primary Key,


-- 操作员 Id
[OperatorId] int Constraint [FK_OperatorId] Foreign Key References Operator([Id]) Not Null,

-- 权限组 Id
[RightsGroupId] int Constraint [FK_RightsGroupId] Foreign Key References RightsGroup([Id]) Not Null
)
Go


二、建立实体类


建立了 SQL Server 数据库模型之后,我们开始建立实体类来作为权限管理的数据传输载体。

1、实体模型基类(ModelBase)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;


namespace CodingMouse.CMHotelManager.Model
{
/// <summary>
/// 实体模型基类
/// </summary>
[Serializable]
public class ModelBase
{
#region Private Members
int _id;
string _modelName; 
#endregion


#region Public Properties
/// <summary>
/// 实体模型 ID
/// </summary>
public int Id
{
get { return _id; }
set { _id = value; }
}
/// <summary>
/// 实体模型名称
/// </summary>
public string ModelName
{
get { return _modelName; }
set { _modelName = value; }

#endregion


#region Public Methods
/// <summary>
/// 无参构造
/// </summary>
public ModelBase() { }


/// <summary>
/// 带参构造
/// </summary>
/// <param name="id">实体模型 ID</param>
/// <param name="modelName">实体模型名称</param>
public ModelBase(int id, string modelName)
{
this.Id = id;
this.ModelName = modelName;

#endregion
}
}


2、操作员实体类(Operator)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;


namespace CodingMouse.CMHotelManager.Model
{
/// <summary>
/// 操作员实体类
/// </summary>
[Serializable]
public class Operator : ModelBase
{
#region Private Members
string _password;
Dictionary<string, Rights> _rightsCollection;
bool _state;
#endregion


#region Public Properties
/// <summary>
/// 操作员密码
/// </summary>
public string Password
{
get { return _password; }
set { _password = value; }
}
/// <summary>
/// 权限集合(键值用于存储菜单/工具栏项的 Name 属性)
/// </summary>
public Dictionary<string, Rights> RightsCollection
{
get { return _rightsCollection; }
set { _rightsCollection = value; }
}
/// <summary>
/// 操作员状态
/// </summary>
public bool State
{
get { return _state; }
set { _state = value; }
}
#endregion


#region Public Methods
/// <summary>
/// 无参构造(基类属性赋值说明:Id - 操作员 ID / ModelName - 操作员名称)
/// </summary>
public Operator() { }


/// <summary>
/// 带参构造
/// </summary>
/// <param name="operatorId">操作员 ID</param>
/// <param name="name">操作员名称</param>
/// <param name="password">操作员密码</param>
/// <param name="rightsCollection">权限集合(键值用于存储菜单/工具栏项的 Name 属性)</param>
/// <param name="state">操作员状态</param>
public Operator(
int operatorId,
string name,
string password,
Dictionary<string, Rights> rightsCollection,
bool state)
: base(operatorId, name)
{
this.Password = password;
this.RightsCollection = rightsCollection;
this.State = state;

#endregion
}
}

3、权限组实体类(RightsGroup)

C#源码清单:


using System;
using System.Collections.Generic;
using System.Text;


namespace CodingMouse.CMHotelManager.Model
{
/// <summary>
/// 权限组实体类
/// </summary>
[Serializable]
public class RightsGroup : ModelBase
{
#region Private Members
Dictionary<string, Rights> _groupRightsCollection; 
#endregion


#region Public Properties
/// <summary>
/// 组权限集合
/// </summary>
public Dictionary<string, Rights> GroupRightsCollection
{
get { return _groupRightsCollection; }
set { _groupRightsCollection = value; }

#endregion


#region Public Methods
/// <summary>
/// 无参构造(基类属性赋值说明:Id - 权限组 ID / ModelName - 权限组名称)
/// </summary>
public RightsGroup() { }
/// <summary>
/// 带参构造
/// </summary>
/// <param name="groupId">权限组 ID</param>
/// <param name="groupName">权限组名称</param>
/// <param name="groupRightsCollection">组权限集合</param>
public RightsGroup(
int groupId,
string groupName,
Dictionary<string, Rights> groupRightsCollection)
: base(groupId, groupName)
{
this.GroupRightsCollection = groupRightsCollection;

#endregion
}
}


4、权限关系实体类(RightsRelation)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;


namespace CodingMouse.CMHotelManager.Model
{
/// <summary>
/// 权限关系实体类
/// </summary>
[Serializable]
public class RightsRelation
{
#region Private Members
int _id;
int _operatorId;
string _operatorName;
int _rightsGroupId;
string rightsGroupName;
#endregion


#region Public Properties
/// <summary>
/// 权限关系 ID
/// </summary>
public int Id
{
get { return _id; }
set { _id = value; }
}
/// <summary>
/// 操作员 ID
/// </summary>
public int OperatorId
{
get { return _operatorId; }
set { _operatorId = value; }
}
/// <summary>
/// 操作员名称(便于在 WinForm 的 DataGridView 控件中实现数据绑定)
/// </summary>
public string OperatorName
{
get { return _operatorName; }
set { _operatorName = value; }
}
/// <summary>
/// 权限组 ID
/// </summary>
public int RightsGroupId
{
get { return _rightsGroupId; }
set { _rightsGroupId = value; }
}
/// <summary>
/// 权限组名称(便于在 WinForm 的 DataGridView 控件中实现数据绑定)
/// </summary>
public string RightsGroupName
{
get { return rightsGroupName; }
set { rightsGroupName = value; }
}
#endregion


#region Public Methods
/// <summary>
/// 无参构造
/// </summary>
public RightsRelation() { }
/// <summary>
/// 带参构造
/// </summary>
/// <param name="id">权限关系 ID</param>
/// <param name="operatorId">操作员 ID</param>
/// <param name="operatorName">操作员名称</param>
/// <param name="rightsGroupId">权限组 ID</param>
/// <param name="rightsGroupName">权限组名称</param>
public RightsRelation(
int id, 
int operatorId, 
string operatorName,
int rightsGroupId,
string rightsGroupName)
{
this.Id = id;
this.OperatorId = operatorId;
this.OperatorName = operatorName;
this.RightsGroupId = rightsGroupId;
this.RightsGroupName = rightsGroupName;

#endregion
}
}


三、具体代码实现


采用多层开发模式有助于降低耦合度,便于程序维护。所以,我们的本文的具体代码实现也使用了多层开发模式。限于篇幅,只列举出具体的代码实现类源码。同时,也是由于本文并不是讲解多层开发的文章,所以对于完成本文的主题功能所涉及的简单工厂、抽象工厂、接口定义、数据库访问等类的源码就不再一一罗列。

(一)数据访问层

1、操作员数据访问操作类(OperatorService)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Runtime.Serialization.Formatters.Binary;
using DBUtility = CodingMouse.CMHotelManager.DBUtility;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.DAL
{
/// <summary>
/// 操作员数据访问操作类
/// </summary>
public class OperatorService : IDAL.IOperatorService
{


#region IOperatorService 成员


/// <summary>
/// 根据操作员名称和密码获取操作员实体
/// </summary>
/// <param name="name">操作员名称</param>
/// <param name="pwd">操作员密码</param>
/// <returns>操作员实体</returns>
public Model.Operator GetOperatorInfoByName(string name, string pwd)
{
//SQL命令
string sqltxt = string.Format("Select Id, OperatorName, Password, RightsList, State From Operator Where OperatorName = '{0}' And Password = '{1}'", name, pwd);


//创建操作员实体
Model.Operator tmpOperator = new Model.Operator();


// 转换数据库存储的 二进制数据为 Byte[] 数组 以便进而转换为操作员权限集合
// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqltxt, conn);
conn.Open();


using (SqlDataReader myReader = cmd.ExecuteReader(
CommandBehavior.CloseConnection | 
CommandBehavior.SingleResult | 
CommandBehavior.SingleRow))
{
if (myReader.Read())
{
//将数据集转换成实体集合
tmpOperator.Id = Convert.ToInt32(myReader["Id"]);
tmpOperator.ModelName = Convert.ToString(myReader["OperatorName"]);
tmpOperator.Password = Convert.ToString(myReader["Password"]);
tmpOperator.State = Convert.ToBoolean(myReader["State"]);


// 读取权限集合
System.Data.SqlTypes.SqlBytes bytes = myReader.GetSqlBytes(3); // 只能指定列序号
// 将流反序列化为权限集合对象
BinaryFormatter bf = new BinaryFormatter();
if (!bytes.IsNull)
tmpOperator.RightsCollection = (bf.Deserialize(bytes.Stream) as Dictionary<string, Model.Rights>);
//else
//    throw new Exception(string.Format("操作员 [{0}] 没有任何权限,禁止登录!", tmpOperator.ModelName));
}
else
//如果没有读取到内容则抛出异常
throw new Exception("登录名称或用户密码不正确!");
}
}

// 如果操作员已经被禁用
if (!tmpOperator.State)
throw new Exception(string.Format("操作员 [{0}] 已被禁用,请与管理员联系!", tmpOperator.ModelName));
// 返回结果
return tmpOperator;
}


/// <summary>
/// 添加操作员
/// </summary>
/// <param name="addOperator">要添加的操作员实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddOperator(Model.Operator addOperator)
{
// 验证密码长度
if (addOperator.Password.Trim().Length < 6)
throw new Exception("用户密码长度不能小于六位!");
// 转换操作员权限集合为数据库可存取的 Byte[] 数组
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, addOperator.RightsCollection);
byte[] rigthsByteArray = new byte[(int)(ms.Length)];
ms.Position = 0;
ms.Read(rigthsByteArray, 0, (int)(ms.Length));
ms.Close();


// 拼接 SQL 命令
string sqlTxt = "Insert Into Operator (OperatorName, Password, RightsList, State) Values " +
"(@OperatorName, @Password, @RightsList, @State)";


// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqlTxt, conn);
SqlParameter prm1 = new SqlParameter("@OperatorName", SqlDbType.NVarChar, 50);
prm1.Value = addOperator.ModelName;
SqlParameter prm2 = new SqlParameter("@Password", SqlDbType.NVarChar, 50);
prm2.Value = addOperator.Password;
SqlParameter prm3 = new SqlParameter("@RightsList", SqlDbType.VarBinary, rigthsByteArray.Length,
ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, rigthsByteArray);
SqlParameter prm4 = new SqlParameter("@State", SqlDbType.Bit);
prm4.Value = addOperator.State;


cmd.Parameters.AddRange(new SqlParameter[] { prm1, prm2, prm3, prm4 });
conn.Open();


if (cmd.ExecuteNonQuery() >= 1)
return true;
else
return false;
}
}


/// <summary>
/// 删除操作员
/// </summary>
/// <param name="id">要删除的操作员 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteOperatorByID(int id)
{
// 删除单个信息 SQL 命令
string sqlTxt = string.Format("Delete From Operator Where Id = {0}", id);
// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 删除操作
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected >= 1)
return true;
else
return false;
}


/// <summary>
/// 修改操作员
/// </summary>
/// <param name="currentOperator">要修改的操作员实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyOperator(Model.Operator currentOperator)
{
// 验证密码长度
if (currentOperator.Password.Trim().Length < 6)
throw new Exception("用户密码长度不能小于六位!");
// 转换操作员权限集合为数据库可存取的 Byte[] 数组
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, currentOperator.RightsCollection);
byte[] rigthsByteArray = new byte[(int)(ms.Length)];
ms.Position = 0;
ms.Read(rigthsByteArray, 0, (int)(ms.Length));
ms.Close();


// 拼接 SQL 命令
string sqlTxt = "Update Operator Set OperatorName = @OperatorName, " +
"Password = @Password, RightsList = @RightsList, State = @State Where Id = @Id";


// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using(SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqlTxt, conn);
SqlParameter prm1 = new SqlParameter("@OperatorName", SqlDbType.NVarChar, 50);
prm1.Value = currentOperator.ModelName;
SqlParameter prm2 = new SqlParameter("@Password", SqlDbType.NVarChar, 50);
prm2.Value = currentOperator.Password;
SqlParameter prm3 = new SqlParameter("@RightsList", SqlDbType.VarBinary, rigthsByteArray.Length, 
ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, rigthsByteArray);
SqlParameter prm4 = new SqlParameter("@State", SqlDbType.Bit);
prm4.Value = currentOperator.State;
SqlParameter prm5 = new SqlParameter("@Id", SqlDbType.Int);
prm5.Value = currentOperator.Id;


cmd.Parameters.AddRange(new SqlParameter[] { prm1, prm2, prm3, prm4, prm5 });
conn.Open();


if (cmd.ExecuteNonQuery() >= 1)
return true;
else
return false;
}
}


/// <summary>
/// 获取所有操作员信息
/// </summary>
/// <returns>操作员实体集合</returns>
public Dictionary<string, Model.Operator> GetAllOperatorInfo()
{
//SQL命令
string sqltxt = "Select Id, OperatorName, Password, RightsList, State From Operator";
//创建操作员实体集合
Dictionary<string, Model.Operator> operatorCollection = new Dictionary<string, Model.Operator>();
//定义操作员实体
Model.Operator tmpOperator = null;


// 转换数据库存储的 二进制数据为 Byte[] 数组 以便进而转换为操作员权限集合
// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqltxt, conn);
conn.Open();


using (SqlDataReader myReader = cmd.ExecuteReader(
CommandBehavior.CloseConnection))
{
while (myReader.Read())
{
// 创建操作员实体
tmpOperator = new Model.Operator();
//将数据集转换成实体集合
tmpOperator.Id = Convert.ToInt32(myReader["Id"]);
tmpOperator.ModelName = Convert.ToString(myReader["OperatorName"]);
tmpOperator.Password = Convert.ToString(myReader["Password"]);
tmpOperator.State = Convert.ToBoolean(myReader["State"]);


// 读取权限集合
System.Data.SqlTypes.SqlBytes bytes = myReader.GetSqlBytes(3); // 只能指定列序号
// 将流反序列化为权限集合对象
BinaryFormatter bf = new BinaryFormatter();
if (!bytes.IsNull)
tmpOperator.RightsCollection = (bf.Deserialize(bytes.Stream) as Dictionary<string, Model.Rights>);


// 添加到操作员实体集合
operatorCollection.Add(tmpOperator.ModelName, tmpOperator);
}
}
}


// 返回结果
return operatorCollection;
}


/// <summary>
/// 根据操作员名称校验操作员是否存在
/// </summary>
/// <param name="operatorName">操作员名称</param>
/// <returns>True:存在/Flase:不存在</returns>
public bool CheckOperatorExist(string operatorName)
{
//创建查询信息的 SQL
string sqlTxt = string.Format(
"Select Count(*) From Operator Where OperatorName = '{0}'", 
operatorName);
//创建SQL执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
//执行查询操作
int result = Convert.ToInt32(dbProvider.RunCommand(sqlTxt));


if (result >= 1)
return true;
else
return false;
}


#endregion
}
}


2、权限组数据访问操作类(RightsGroupService)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using System.Data;
using System.Data.SqlClient;
using System.Configuration;
using System.Runtime.Serialization.Formatters.Binary;
using DBUtility = CodingMouse.CMHotelManager.DBUtility;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.DAL
{
/// <summary>
/// 权限组数据访问操作类
/// </summary>
public class RightsGroupService : IDAL.IRightsGroupService
{
#region IRightsGroupService 成员


/// <summary>
/// 获取所有权限组信息
/// </summary>
/// <returns>权限组实体集合</returns>
public Dictionary<string, Model.RightsGroup> GetAllRightsGroupInfo()
{
//SQL命令
string sqltxt = "Select Id, GroupName, GroupRightsList From RightsGroup";
//创建权限组实体集合
Dictionary<string, Model.RightsGroup> rightsGroupCollection = new Dictionary<string, Model.RightsGroup>();
//定义权限组实体
Model.RightsGroup tmpRightsGroup = null;


// 转换数据库存储的 二进制数据为 Byte[] 数组 以便进而转换为权限组权限集合
// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqltxt, conn);
conn.Open();


using (SqlDataReader myReader = cmd.ExecuteReader(
CommandBehavior.CloseConnection))
{
while (myReader.Read())
{
// 创建权限组实体
tmpRightsGroup = new Model.RightsGroup();
//将数据集转换成实体集合
tmpRightsGroup.Id = Convert.ToInt32(myReader["Id"]);
tmpRightsGroup.ModelName = Convert.ToString(myReader["GroupName"]);


// 读取权限集合
System.Data.SqlTypes.SqlBytes bytes = myReader.GetSqlBytes(2); // 只能指定列序号
// 将流反序列化为权限集合对象
BinaryFormatter bf = new BinaryFormatter();
if (!bytes.IsNull)
tmpRightsGroup.GroupRightsCollection = (bf.Deserialize(bytes.Stream) as Dictionary<string, Model.Rights>);


// 添加到权限组实体集合
rightsGroupCollection.Add(tmpRightsGroup.ModelName, tmpRightsGroup);
}
}
}


// 返回结果
return rightsGroupCollection;
}


/// <summary>
/// 添加权限组
/// </summary>
/// <param name="addRightsGroup">要添加的权限组实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddRightsGroup(Model.RightsGroup addRightsGroup)
{
// 转换权限组权限集合为数据库可存取的 Byte[] 数组
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, addRightsGroup.GroupRightsCollection);
byte[] rigthsByteArray = new byte[(int)(ms.Length)];
ms.Position = 0;
ms.Read(rigthsByteArray, 0, (int)(ms.Length));
ms.Close();


// 拼接 SQL 命令
string sqlTxt = "Insert Into RightsGroup (GroupName, GroupRightsList) Values " +
"(@GroupName, @GroupRightsList)";


// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqlTxt, conn);
SqlParameter prm1 = new SqlParameter("@GroupName", SqlDbType.NVarChar, 50);
prm1.Value = addRightsGroup.ModelName;
SqlParameter prm2 = new SqlParameter("@GroupRightsList", SqlDbType.VarBinary, rigthsByteArray.Length,
ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, rigthsByteArray);


cmd.Parameters.AddRange(new SqlParameter[] { prm1, prm2 });
conn.Open();


if (cmd.ExecuteNonQuery() >= 1)
return true;
else
return false;
}
}


/// <summary>
/// 删除权限组
/// </summary>
/// <param name="id">要删除的权限组 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsGroupByID(int id)
{
// 删除单个信息 SQL 命令
string sqlTxt = string.Format("Delete From RightsGroup Where Id = {0}", id);
// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 删除操作
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected >= 1)
return true;
else
return false;
}


/// <summary>
/// 修改权限组
/// </summary>
/// <param name="currentRightsGroup">要修改的权限组实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyRightsGroup(Model.RightsGroup currentRightsGroup)
{
// 转换权限组权限集合为数据库可存取的 Byte[] 数组
MemoryStream ms = new MemoryStream();
BinaryFormatter bf = new BinaryFormatter();
bf.Serialize(ms, currentRightsGroup.GroupRightsCollection);
byte[] rigthsByteArray = new byte[(int)(ms.Length)];
ms.Position = 0;
ms.Read(rigthsByteArray, 0, (int)(ms.Length));
ms.Close();


// 拼接 SQL 命令
string sqlTxt = "Update RightsGroup Set GroupName = @GroupName, GroupRightsList = @GroupRightsList Where Id = @Id";


// 从配置文件读取连接字符串
string connectionString = ConfigurationManager.ConnectionStrings["SQLSERVER"].ConnectionString;
// 执行 SQL 命令
using (SqlConnection conn = new SqlConnection(connectionString))
{
SqlCommand cmd = new SqlCommand(sqlTxt, conn);
SqlParameter prm1 = new SqlParameter("@GroupName", SqlDbType.NVarChar, 50);
prm1.Value = currentRightsGroup.ModelName;
SqlParameter prm2 = new SqlParameter("@GroupRightsList", SqlDbType.VarBinary, rigthsByteArray.Length,
ParameterDirection.Input, false, 0, 0, null, DataRowVersion.Current, rigthsByteArray);
SqlParameter prm3 = new SqlParameter("@Id", SqlDbType.Int);
prm3.Value = currentRightsGroup.Id;


cmd.Parameters.AddRange(new SqlParameter[] { prm1, prm2, prm3 });
conn.Open();


if (cmd.ExecuteNonQuery() >= 1)
return true;
else
return false;
}
}


/// <summary>
/// 根据权限组名称校验权限组是否已经存在
/// </summary>
/// <param name="rightsGroupName">权限组名称</param>
/// <returns>True:存在/False:不存在</returns>
public bool CheckRightsGroupExist(string rightsGroupName)
{
//SQL命令
string sqlTxt = string.Format("Select Count(*) From RightsGroup Where GroupName = '{0}'", rightsGroupName);


//创建SQL执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
//执行查询操作
int result = Convert.ToInt32(dbProvider.RunCommand(sqlTxt));


if (result >= 1)
return true;
else
return false;
}


#endregion
}
}


3、权限关系数据访问操作类(RightsRelationService)


C#源码清单:


using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.Data.SqlClient;
using DBUtility = CodingMouse.CMHotelManager.DBUtility;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.DAL
{
/// <summary>
/// 权限关系数据访问操作类
/// </summary>
public class RightsRelationService : IDAL.IRightsRelationService
{
#region IRightsRelationService 成员


/// <summary>
/// 添加单个权限关系
/// </summary>
/// <param name="rightsRelation">权限关系实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddRightsRelation(Model.RightsRelation rightsRelation)
{
// 拼接 SQL 命令
string sqlTxt = string.Format(
"Insert Into RightsRelation (OperatorId, RightsGroupId) " +
"Values ({0}, {1})",
rightsRelation.OperatorId, rightsRelation.RightsGroupId);


// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 SQL
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected == 1)
{
return true;
}
else
{
return false;
}
}


/// <summary>
/// 根据权限关系 ID 删除权限关系
/// </summary>
/// <param name="id">权限关系 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsRelationById(int id)
{
bool isOk = false;


// 删除单个信息 SQL 命令
string sqlTxt = string.Format("Delete From RightsRelation Where Id = {0}", id);
// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 删除操作
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected >= 1)
{
isOk = true;
}
else
{
isOk = false;
}


return isOk;
}


/// <summary>
/// 根据操作员 ID 删除对应的所有权限关系
/// </summary>
/// <param name="operatorId">操作员 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsRelationByOperatorId(int operatorId)
{
bool isOk = false;


// 删除单个信息 SQL 命令
string sqlTxt = string.Format("Delete From RightsRelation Where OperatorId = {0}", operatorId);
// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 删除操作
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected >= 1)
{
isOk = true;
}
else
{
isOk = false;
}


return isOk;
}


/// <summary>
/// 修改单个权限关系
/// </summary>
/// <param name="rightsRelation">权限关系实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyRightsRelation(Model.RightsRelation rightsRelation)
{
// 拼接 SQL 命令
string sqlTxt = string.Format(
"Update RightsRelation Set OperatorId = {0}, RightsGroupId = {1} Where Id = {2}",
rightsRelation.OperatorId, rightsRelation.RightsGroupId, rightsRelation.Id);


// 创建 SQL 执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行 SQL
int rowsAffected;
dbProvider.RunCommand(sqlTxt, out rowsAffected);


if (rowsAffected == 1)
{
return true;
}
else
{
return false;
}
}


/// <summary>
/// 获取所有的权限关系集合
/// </summary>
/// <returns>权限关系集合</returns>
public List<Model.RightsRelation> GetAllRightsRelation()
{
//创建数据集
DataSet dsRightsRelation = new DataSet("RightsRelation");
//创建权限关系集合
List<Model.RightsRelation> rightsRelationList = new List<Model.RightsRelation>();
//创建查询权限关系信息的 SQL
string sqlTxt = "Select R.Id, R.OperatorId, O.OperatorName, R.RightsGroupId, " +
"G.GroupName As [RightsGroupName] From RightsRelation As R Join Operator As O " +
"On R.OperatorId = O.Id Join RightsGroup As G On R.RightsGroupId = G.Id";
//创建SQL执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
//执行查询操作
dsRightsRelation = dbProvider.RunCommand(sqlTxt, "RightsRelation");
//将数据集转换成实体集合
foreach (DataRow row in dsRightsRelation.Tables["RightsRelation"].Rows)
{
Model.RightsRelation tmpRightsRelation = new Model.RightsRelation();
tmpRightsRelation.Id = Convert.ToInt32(row["Id"]);
tmpRightsRelation.OperatorId = Convert.ToInt32(row["OperatorId"]);
tmpRightsRelation.OperatorName = Convert.ToString(row["OperatorName"]);
tmpRightsRelation.RightsGroupId = Convert.ToInt32(row["RightsGroupId"]);
tmpRightsRelation.RightsGroupName = Convert.ToString(row["RightsGroupName"]);


rightsRelationList.Add(tmpRightsRelation);
}


//返回所有权限关系集合
return rightsRelationList;
}


/// <summary>
/// 根据操作员 ID 获取对应的所有权限关系
/// </summary>
/// <param name="id">操作员 ID</param>
/// <returns>权限关系集合</returns>
public List<Model.RightsRelation> GetRightsRelationByOperatorId(int id)
{
//创建数据集
DataSet dsRightsRelation = new DataSet("RightsRelation");
//创建权限关系集合
List<Model.RightsRelation> rightsRelationList = new List<Model.RightsRelation>();
//创建查询权限关系信息的 SQL
string sqlTxt = string.Format("Select R.Id, R.OperatorId, O.OperatorName, R.RightsGroupId, " +
"G.GroupName As [RightsGroupName] From RightsRelation As R Join Operator As O " +
"On R.OperatorId = O.Id Join RightsGroup As G On R.RightsGroupId = G.Id " +
"Where OperatorId = {0}", id);
//创建SQL执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
//执行查询操作
dsRightsRelation = dbProvider.RunCommand(sqlTxt, "RightsRelation");
//将数据集转换成实体集合
foreach (DataRow row in dsRightsRelation.Tables["RightsRelation"].Rows)
{
Model.RightsRelation tmpRightsRelation = new Model.RightsRelation();
tmpRightsRelation.Id = Convert.ToInt32(row["Id"]);
tmpRightsRelation.OperatorId = Convert.ToInt32(row["OperatorId"]);
tmpRightsRelation.OperatorName = Convert.ToString(row["OperatorName"]);
tmpRightsRelation.RightsGroupId = Convert.ToInt32(row["RightsGroupId"]);
tmpRightsRelation.RightsGroupName = Convert.ToString(row["RightsGroupName"]);


rightsRelationList.Add(tmpRightsRelation);
}


//返回所有权限关系集合
return rightsRelationList;
}


/// <summary>
/// 根据权限组 ID 获取与此权限组相关的权限关系数量
/// </summary>
/// <param name="id">权限组 ID</param>
/// <returns>权限关系数量</returns>
public int GetRightsRelationCountByRightsGroupId(int id)
{
// SQL命令
string sqlTxt = string.Format("Select Count(*) From RightsRelation Where RightsGroupId = {0}", id);


// 创建SQL执行对象
DBUtility.AbstractDBProvider dbProvider = DBUtility.AbstractDBProvider.Instance();
// 执行查询操作
int result = Convert.ToInt32(dbProvider.RunCommand(sqlTxt));


// 返回结果
return result;
}


#endregion
}
}


(二)业务逻辑层(在本文中仅仅起到一个数据传递者的作用)

1、操作员数据访问操作类(OperatorManager)

C#源代码清单

using System;
using System.Collections.Generic;
using System.Text;
using DALFactory = CodingMouse.CMHotelManager.DALFactory;
using IBLL = CodingMouse.CMHotelManager.IBLL;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.BLL
{
/// <summary>
/// 操作员数据访问操作类
/// </summary>
public class OperatorManager : IBLL.IOperatorManager
{


#region IOperatorManager 成员


/// <summary>
/// 根据操作员名称和密码获取操作员实体
/// </summary>
/// <param name="name">操作员名称</param>
/// <param name="pwd">操作员密码</param>
/// <returns>操作员实体</returns>
public Model.Operator GetOperatorInfoByName(string name, string pwd)
{
// 超级后门管理员账户(测试用,千万别用于商业用途,要不然被抓去坐监可别怪我!)
if (name == "CodingMouse" && pwd == "20071117")
{
Model.Operator adminOperator = new Model.Operator();
adminOperator.Id = 0;
adminOperator.ModelName = name;
adminOperator.Password = pwd;
adminOperator.RightsCollection = new Dictionary<string, Model.Rights>();
adminOperator.State = true;


return adminOperator;
}


//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.GetOperatorInfoByName(name, pwd);
}


/// <summary>
/// 添加操作员
/// </summary>
/// <param name="addOperator">要添加的操作员实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddOperator(Model.Operator addOperator)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.AddOperator(addOperator);
}


/// <summary>
/// 删除操作员
/// </summary>
/// <param name="id">要删除的操作员 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteOperatorByID(int id)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.DeleteOperatorByID(id);
}


/// <summary>
/// 修改操作员
/// </summary>
/// <param name="currentOperator">要修改的操作员实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyOperator(Model.Operator currentOperator)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.ModifyOperator(currentOperator);
}


/// <summary>
/// 获取所有操作员信息
/// </summary>
/// <returns>操作员实体集合</returns>
public Dictionary<string, Model.Operator> GetAllOperatorInfo()
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.GetAllOperatorInfo();
}


/// <summary>
/// 根据操作员名称校验操作员是否存在
/// </summary>
/// <param name="operatorName">操作员名称</param>
/// <returns>True:存在/Flase:不存在</returns>
public bool CheckOperatorExist(string operatorName)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IOperatorService operatorService = absDALFactory.BuildOperatorService();
//调用实例方法
return operatorService.CheckOperatorExist(operatorName);
}


#endregion
}
}


2、权限组数据访问操作类(RightsGroupManager)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using DALFactory = CodingMouse.CMHotelManager.DALFactory;
using IBLL = CodingMouse.CMHotelManager.IBLL;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.BLL
{
/// <summary>
/// 权限组数据访问操作类
/// </summary>
public class RightsGroupManager : IBLL.IRightsGroupManager
{
#region IRightsGroupManager 成员


/// <summary>
/// 获取所有权限组信息
/// </summary>
/// <returns>权限组实体集合</returns>
public Dictionary<string, Model.RightsGroup> GetAllRightsGroupInfo()
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsGroupService rightsGroupService = absDALFactory.BuildRightsGroupService();
//调用实例方法
return rightsGroupService.GetAllRightsGroupInfo();
}


/// <summary>
/// 添加权限组
/// </summary>
/// <param name="addRightsGroup">要添加的权限组实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddRightsGroup(Model.RightsGroup addRightsGroup)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsGroupService rightsGroupService = absDALFactory.BuildRightsGroupService();
//调用实例方法
return rightsGroupService.AddRightsGroup(addRightsGroup);
}


/// <summary>
/// 删除权限组
/// </summary>
/// <param name="id">要删除的权限组 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsGroupByID(int id)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsGroupService rightsGroupService = absDALFactory.BuildRightsGroupService();
//调用实例方法
return rightsGroupService.DeleteRightsGroupByID(id);
}


/// <summary>
/// 修改权限组
/// </summary>
/// <param name="currentRightsGroup">要修改的权限组实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyRightsGroup(Model.RightsGroup currentRightsGroup)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsGroupService rightsGroupService = absDALFactory.BuildRightsGroupService();
//调用实例方法
return rightsGroupService.ModifyRightsGroup(currentRightsGroup);
}


/// <summary>
/// 根据权限组名称校验权限组是否已经存在
/// </summary>
/// <param name="rightsGroupName">权限组名称</param>
/// <returns>True:存在/False:不存在</returns>
public bool CheckRightsGroupExist(string rightsGroupName)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsGroupService rightsGroupService = absDALFactory.BuildRightsGroupService();
//调用实例方法
return rightsGroupService.CheckRightsGroupExist(rightsGroupName);
}


#endregion
}
}


3、权限关系数据访问操作类(RightsRelationManager)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using DALFactory = CodingMouse.CMHotelManager.DALFactory;
using IBLL = CodingMouse.CMHotelManager.IBLL;
using IDAL = CodingMouse.CMHotelManager.IDAL;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.BLL
{
/// <summary>
/// 权限关系数据访问操作类
/// </summary>
public class RightsRelationManager : IBLL.IRightsRelationManager
{
#region IRightsRelationManager 成员


/// <summary>
/// 添加单个权限关系
/// </summary>
/// <param name="rightsRelation">权限关系实体</param>
/// <returns>True:成功/False:失败</returns>
public bool AddRightsRelation(Model.RightsRelation rightsRelation)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.AddRightsRelation(rightsRelation);
}


/// <summary>
/// 根据权限关系 ID 删除权限关系
/// </summary>
/// <param name="id">权限关系 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsRelationById(int id)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.DeleteRightsRelationById(id);
}


/// <summary>
/// 根据操作员 ID 删除对应的所有权限关系
/// </summary>
/// <param name="operatorId">操作员 ID</param>
/// <returns>True:成功/False:失败</returns>
public bool DeleteRightsRelationByOperatorId(int operatorId)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.DeleteRightsRelationByOperatorId(operatorId);
}


/// <summary>
/// 修改单个权限关系
/// </summary>
/// <param name="rightsRelation">权限关系实体</param>
/// <returns>True:成功/False:失败</returns>
public bool ModifyRightsRelation(Model.RightsRelation rightsRelation)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.ModifyRightsRelation(rightsRelation);
}


/// <summary>
/// 获取所有的权限关系集合
/// </summary>
/// <returns>权限关系集合</returns>
public List<Model.RightsRelation> GetAllRightsRelation()
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.GetAllRightsRelation();
}


/// <summary>
/// 根据操作员 ID 获取对应的所有权限关系
/// </summary>
/// <param name="id">操作员 ID</param>
/// <returns>权限关系集合</returns>
public List<Model.RightsRelation> GetRightsRelationByOperatorId(int id)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.GetRightsRelationByOperatorId(id);
}


/// <summary>
/// 根据权限组 ID 获取与此权限组相关的权限关系数量
/// </summary>
/// <param name="id">权限组 ID</param>
/// <returns>权限关系数量</returns>
public int GetRightsRelationCountByRightsGroupId(int id)
{
//定义并实例化抽象工厂类
DALFactory.AbstractDALFactory absDALFactory = DALFactory.AbstractDALFactory.Instance();
//调用工厂方法生成实例
IDAL.IRightsRelationService rightsRelationService = absDALFactory.BuildRightsRelationService();
//调用实例方法
return rightsRelationService.GetRightsRelationCountByRightsGroupId(id);
}


#endregion
}
}


(三)界面表示层(由于界面操作逻辑的复杂度较高,在此仅列出部分难点代码)

1、权限菜单数据管理类(RightsMenuDataManager)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Forms;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.UI.Common
{
/// <summary>
/// 权限菜单数据管理类
/// </summary>
public class RightsMenuDataManager
{
#region Private Members
/// <summary>
/// 保存当前被管理的菜单对象
/// </summary>
MenuStrip _msMain = null;
/// <summary>
/// 保存当前权限项 ID
/// </summary>
int _rightsId = 0; 
#endregion


#region Internal Properties
/// <summary>
/// 被管理的菜单对象
/// </summary>
internal MenuStrip MsMain
{
get { return _msMain; }
set { _msMain = value; }

#endregion


#region Private Methods
/// <summary>
/// 使用递归法读取菜单权限子项
/// </summary>
/// <param name="rightCollection">要保存到的菜单根项权限集合</param>
/// <param name="tsmiRootLevel">当前菜单权限相对根项</param>
private Model.Rights GetMenuRightsChildrenItem(Dictionary<string, Model.Rights> rightCollection, ToolStripMenuItem tsmiRootLevel)
{
// 显示下级菜单项元素 Text
Model.Rights secondRights = null;


// 使用 ToolStripItem 基类遍历获取下级菜单项
foreach (ToolStripItem tsmiNextLevel in tsmiRootLevel.DropDownItems)
{
// ID 号累加
_rightsId++;
// 显示下级菜单项元素 Text
secondRights = new Model.Rights();
secondRights.Id = _rightsId;
secondRights.ModelName = tsmiNextLevel.Name;
secondRights.RightsState = false;
secondRights.ParentLevelRightsName = rightCollection[tsmiRootLevel.Name].ModelName;


// 如果是菜单项而不是其它菜单项类型
if (tsmiNextLevel is ToolStripMenuItem)
{
secondRights.RightsCaption = tsmiNextLevel.Text;
// 添加当前项到集合
rightCollection.Add(secondRights.ModelName, secondRights);
// 使用递归添加所有次级子项到集合
GetMenuRightsChildrenItem(rightCollection, tsmiNextLevel as ToolStripMenuItem);
}
// 如果是分隔项而不是其它菜单项类型
else if (tsmiNextLevel is ToolStripSeparator)
{
secondRights.RightsCaption = "━━━━";
// 添加此菜单分隔项到集合
rightCollection.Add((tsmiNextLevel as ToolStripSeparator).Name, secondRights);
}
}


return secondRights;
}
/// <summary>
/// 使用递归法加载菜单权限子项
/// </summary>
/// <param name="rightCollection">权限集合</param>
/// <param name="tsmiRootLevel">当前菜单权限相对根项</param>
private void LoadMenuRightsChildrenItem(Dictionary<string, Model.Rights> rightCollection, ToolStripMenuItem tsmiRootLevel)
{
// 使用 ToolStripItem 基类遍历获取下级菜单项
foreach (ToolStripItem tsmiNextLevel in tsmiRootLevel.DropDownItems)
{
foreach (Model.Rights tmpRights in rightCollection.Values)
{
// 如果是菜单项而不是其它菜单项类型
if (tsmiNextLevel is ToolStripMenuItem)
{
// 如果内部名称相同
if (tsmiNextLevel.Name == tmpRights.ModelName)
{
// 设置名称和显隐状态
tsmiNextLevel.Text = tmpRights.RightsCaption;
tsmiNextLevel.Visible = tmpRights.RightsState;


// 使用递归加载所有次级子项
LoadMenuRightsChildrenItem(rightCollection, tsmiNextLevel as ToolStripMenuItem);
break;
}
}
// 如果是分隔项而不是其它菜单项类型
else if (tsmiNextLevel is ToolStripSeparator)
{
// 如果内部名称相同
if (tsmiNextLevel.Name == tmpRights.ModelName)
{
// 设置显隐状态
tsmiNextLevel.Visible = tmpRights.RightsState;
break;
}
}
}
}

#endregion


#region Internal Methods
/// <summary>
/// 使用递归法读取菜单权限项
/// </summary>
/// <param name="rightCollection">要保存到的目标权限集合</param>
/// <returns>权限集合</returns>
internal Dictionary<string, Model.Rights> ReadMenuRightsItem(Dictionary<string, Model.Rights> rightCollection)
{
if (!(this.MsMain is MenuStrip))
throw new Exception("未指定被管理的菜单对象!请使用本方法的另一重载指定菜单对象!");


// 调用另一重载
return this.ReadMenuRightsItem(this._msMain, rightCollection);
}
/// <summary>
/// 使用递归法读取菜单权限项
/// </summary>
/// <param name="msCurrentMenu">当前被管理的菜单对象</param>
/// <param name="rightCollection">要保存到的目标权限集合</param>
/// <returns>权限集合</returns>
internal Dictionary<string, Model.Rights> ReadMenuRightsItem(MenuStrip msCurrentMenu, Dictionary<string, Model.Rights> rightCollection)
{
// 遍历获取菜单根项
foreach (ToolStripMenuItem tsmiRootLevel in msCurrentMenu.Items)
{
if (tsmiRootLevel is ToolStripMenuItem)
{
// ID 号累加
_rightsId++;
// 显示菜单根项元素 Text
Model.Rights rootRights = new Model.Rights();
rootRights.Id = _rightsId;
rootRights.ModelName = tsmiRootLevel.Name;
rootRights.RightsCaption = tsmiRootLevel.Text;
rootRights.RightsState = false;
rootRights.ParentLevelRightsName = msCurrentMenu.Name;


// 如果未添加此权限则添加此权限到菜单根项的权限集合
bool isExist = false;
foreach (Model.Rights tmpRights in rightCollection.Values)
{
if (tmpRights.ModelName == rootRights.ModelName)
isExist = true;
}
if (!isExist)
rightCollection.Add(rootRights.ModelName, rootRights);


// 使用递归添加所有子项
GetMenuRightsChildrenItem(rightCollection, tsmiRootLevel);
}
}


return rightCollection;
}
/// <summary>
/// 使用递归法加载菜单权限项
/// </summary>
/// <param name="rightCollection">要保存到的目标权限集合</param>
/// <returns>权限集合</returns>
internal void LoadMenuRightsItem(Dictionary<string, Model.Rights> rightCollection)
{
if (!(this.MsMain is MenuStrip))
throw new Exception("未指定被管理的菜单对象!请使用本方法的另一重载指定菜单对象!");


// 调用另一重载
this.LoadMenuRightsItem(this._msMain, rightCollection);
}
/// <summary>
/// 使用递归法加载菜单权限项
/// </summary>
/// <param name="msCurrentMenu">当前被管理的菜单对象</param>
/// <param name="rightCollection">权限集合</param>
internal void LoadMenuRightsItem(MenuStrip msCurrentMenu, Dictionary<string, Model.Rights> rightCollection)
{
foreach (Model.Rights tmpRights in rightCollection.Values)
{
// 遍历获取菜单根项
foreach (ToolStripMenuItem tsmiRootLevel in msCurrentMenu.Items)
{
if (tsmiRootLevel is ToolStripMenuItem)
{
// 如果内部名称相同
if (tsmiRootLevel.Name == tmpRights.ModelName)
{
// 设置名称和显隐状态
tsmiRootLevel.Text = tmpRights.RightsCaption;
tsmiRootLevel.Visible = tmpRights.RightsState;
// 使用递归加载所有子项
LoadMenuRightsChildrenItem(rightCollection, tsmiRootLevel);
break;
}
}
}
}

#endregion
}
}


2、权限菜单界面管理类(RightsMenuUIManager)

C#源码清单:

using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using System.Windows.Forms;
using IBLL = CodingMouse.CMHotelManager.IBLL;
using BLLFactory = CodingMouse.CMHotelManager.BLLFactory;
using Model = CodingMouse.CMHotelManager.Model;


namespace CodingMouse.CMHotelManager.UI.Common
{
/// <summary>
/// 权限菜单界面管理类
/// </summary>
public class RightsMenuUIManager
{
#region Private Members
DataGridView _dgvOperatorList = null;
DataGridView _dgvRightsList = null;
TreeView _tvRightsView = null;
MenuStrip _msMain = null;
Forms.frmRightsManager _rightsManagerUI = null;
#endregion


#region Internal Properties
/// <summary>
/// 要操作的操作员列表 DataGridView 对象
/// </summary>
internal DataGridView DgvOperatorList
{
get { return _dgvOperatorList; }
set { _dgvOperatorList = value; }
}
/// <summary>
/// 要操作的权限列表 DataGridView 对象
/// </summary>
internal DataGridView DgvRightsList
{
get { return _dgvRightsList; }
set { _dgvRightsList = value; }
}
/// <summary>
/// 要操作的权限视图 TreeView 对象
/// </summary>
internal TreeView TvRightsView
{
get { return _tvRightsView; }
set { _tvRightsView = value; }
}
/// <summary>
/// 被管理的菜单对象
/// </summary>
internal MenuStrip MsMain
{
get { return _msMain; }
set { _msMain = value; }
}
/// <summary>
/// 当前被管理的操作界面
/// </summary>
internal Forms.frmRightsManager RightsManagerUI
{
get { return _rightsManagerUI; }
set { _rightsManagerUI = value; }
}
#endregion


#region Private Methods
/// <summary>
/// 加载所有树子节点
/// </summary>
/// <param name="currentChildTreeNode">当前子节点</param>
/// <param name="rightsCollection">所有权限集合</param>
/// <returns>加载了所有次级子节点的当前子节点</returns>
private TreeNode LoadAllChildTreeNode(TreeNode currentChildTreeNode, Dictionary<string, Model.Rights> rightsCollection)
{
// 如果是菜单分隔则设置突出显示
if (currentChildTreeNode.Text == "━━━━")
{
currentChildTreeNode.ForeColor = Color.Red;
currentChildTreeNode.ToolTipText = "<-- 菜单分隔 -->";
}
// 遍历同父权限项集合
foreach (Model.Rights tmpRights in rightsCollection.Values)
{
// 如果当前父级权限项的权限名称与当前节点相同
if (tmpRights.ParentLevelRightsName == currentChildTreeNode.Tag.ToString())
{
// 为当前节点创建新的子节点
TreeNode newChildTreeNode = new TreeNode(tmpRights.RightsCaption);
newChildTreeNode.Tag = tmpRights.ModelName;
newChildTreeNode.Checked = tmpRights.RightsState;
// 创建同父权限项集合
List<Model.Rights> sameNessParentRightsList = new List<Model.Rights>();
// 获取所有与当前权限项具有相同父权限项的权限项
foreach (Model.Rights sameNessParentRights in rightsCollection.Values)
{
if (sameNessParentRights.ParentLevelRightsName == tmpRights.ParentLevelRightsName)
sameNessParentRightsList.Add(sameNessParentRights);
}
// 递归添加到当前节点及其所有子节点
currentChildTreeNode.Nodes.Add(LoadAllChildTreeNode(newChildTreeNode, rightsCollection));
}
}


// 返回当前处理的节点
return currentChildTreeNode;

#endregion


#region Internal Methods
/// <summary>
/// 将操作员集合数据绑定显示到数据视图
/// </summary>
/// <param name="operatorCollection">操作员集合</param>
internal void BindOperatorInfoToDataGridView(Dictionary<string, Model.Operator> operatorCollection)
{
try
{
// 如果包含操作员信息
if (operatorCollection.Count > 0)
{
// 将权限集合绑定显示在数据视图中
BindingSource source = new BindingSource();
source.DataSource = operatorCollection.Values;
this._dgvOperatorList.DataSource = source;
// 设置中文列名及可写状态
this._dgvOperatorList.Columns["Id"].HeaderText = "编号";
this._dgvOperatorList.Columns["Id"].ToolTipText = "[只读列]";
this._dgvOperatorList.Columns["Id"].DisplayIndex = 0;
this._dgvOperatorList.Columns["Id"].ReadOnly = true;
this._dgvOperatorList.Columns["ModelName"].HeaderText = "操作员名称";
this._dgvOperatorList.Columns["ModelName"].ToolTipText = "[只读列]";
this._dgvOperatorList.Columns["ModelName"].DisplayIndex = 1;
this._dgvOperatorList.Columns["ModelName"].ReadOnly = true;
this._dgvOperatorList.Columns["Password"].HeaderText = "密码";
this._dgvOperatorList.Columns["Password"].DisplayIndex = 2;
this._dgvOperatorList.Columns["State"].HeaderText = "状态";
this._dgvOperatorList.Columns["State"].DisplayIndex = 3;
this._dgvOperatorList.Columns["RightsCollection"].HeaderText = "权限列表";
this._dgvOperatorList.Columns["RightsCollection"].DisplayIndex = 4;
this._dgvOperatorList.Columns["RightsCollection"].ReadOnly = true;
this._dgvOperatorList.Columns["RightsCollection"].Visible = false;


// 设置菜单分隔项的权限标题列为只读
foreach (DataGridViewRow dgvRow in this._dgvOperatorList.Rows)
{
// 设置单元格工具栏提示
foreach (DataGridViewCell dgvCell in dgvRow.Cells)
{
if (dgvCell.ReadOnly)
dgvCell.ToolTipText = "[只读格]";
else
dgvCell.ToolTipText = "[可写格]";
}
}
}
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"加载失败",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}


/// <summary>
/// 将权限集合数据绑定显示到数据视图
/// </summary>
/// <param name="rightsCollection">权限集合</param>
internal void BindDataToDataGridView(Dictionary<string, Model.Rights> rightsCollection)
{
try
{
// 保存所有选择单元格
List<string> selectedCellValueList = new List<string>();
foreach (DataGridViewCell dgvCell in _dgvRightsList.SelectedCells)
{
selectedCellValueList.Add(dgvCell.Value.ToString().Trim());
}


// 将权限集合绑定显示在数据视图中
BindingSource source = new BindingSource();
source.DataSource = rightsCollection.Values;
this._dgvRightsList.DataSource = source;
// 设置中文列名及可写状态
this._dgvRightsList.Columns["Id"].HeaderText = "编号";
this._dgvRightsList.Columns["Id"].ToolTipText = "[只读列]";
this._dgvRightsList.Columns["Id"].ReadOnly = true;
this._dgvRightsList.Columns["Id"].DisplayIndex = 0;
this._dgvRightsList.Columns["RightsCaption"].HeaderText = "权限标题";
this._dgvRightsList.Columns["RightsCaption"].DisplayIndex = 1;
this._dgvRightsList.Columns["ModelName"].HeaderText = "内部名称";
this._dgvRightsList.Columns["ModelName"].ToolTipText = "[只读列]";
this._dgvRightsList.Columns["ModelName"].ReadOnly = true;
this._dgvRightsList.Columns["ModelName"].DisplayIndex = 2;
this._dgvRightsList.Columns["RightsState"].HeaderText = "权限状态";
this._dgvRightsList.Columns["RightsState"].DisplayIndex = 3;
this._dgvRightsList.Columns["ParentLevelRightsName"].HeaderText = "父级权限";
this._dgvRightsList.Columns["ParentLevelRightsName"].ToolTipText = "[只读列]";
this._dgvRightsList.Columns["ParentLevelRightsName"].ReadOnly = true;
this._dgvRightsList.Columns["ParentLevelRightsName"].DisplayIndex = 4;


// 设置菜单分隔项的权限标题列为只读
foreach (DataGridViewRow dgvRow in this._dgvRightsList.Rows)
{
// 如果是菜单分隔项则设置为只读
if (dgvRow.Cells["RightsCaption"].Value.ToString().Trim() == "━━━━")
dgvRow.Cells["RightsCaption"].ReadOnly = true;
// 设置单元格工具栏提示
foreach (DataGridViewCell dgvCell in dgvRow.Cells)
{
if (dgvCell.ReadOnly)
{
dgvCell.ToolTipText = "[只读格]";
if (dgvCell.Value.ToString().Trim() == "━━━━")
dgvCell.ToolTipText += " | <-- 菜单分隔 -->";
}
else
dgvCell.ToolTipText = "[可写格]";
}
}


// 禁用关联选择
RightsManagerUI.IsRelatingChooseCells = false;
// 清除所有单元格选择
_dgvRightsList.ClearSelection();
// 还原原有选择
foreach (string dgvSelectedCellValue in selectedCellValueList)
{
foreach (DataGridViewRow dgvRow in _dgvRightsList.Rows)
{
foreach (DataGridViewCell dgvCell in dgvRow.Cells)
{
if (dgvCell.Value.ToString().Trim() == dgvSelectedCellValue)
dgvCell.Selected = true;
}
}
}
// 启用关联选择
RightsManagerUI.IsRelatingChooseCells = true;
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"加载失败",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}


/// <summary>
/// 将数据绑定加载到树形视图
/// </summary>
/// <param name="rightsCollection">权限集合</param>
internal void BindDataToTreeView(Dictionary<string, Model.Rights> rightsCollection)
{
// 禁用树视图的重绘
_tvRightsView.BeginUpdate();
// 清除原有节点
_tvRightsView.Nodes.Clear();
// 遍历权限集合以加载数据
foreach (Model.Rights tmpRights in rightsCollection.Values)
{
// 定义权限根项
TreeNode rootTreeNode = null;
// 如果是权限根项
if (tmpRights.ParentLevelRightsName == _msMain.Name)
{
rootTreeNode = new TreeNode(tmpRights.RightsCaption);
rootTreeNode.Tag = tmpRights.ModelName;
rootTreeNode.Checked = tmpRights.RightsState;
_tvRightsView.Nodes.Add(rootTreeNode);
}
// 如果是权限子项
else
{
// 创建权限子项
TreeNode childTreeNode = new TreeNode(tmpRights.RightsCaption);
childTreeNode.Tag = tmpRights.ModelName;
childTreeNode.Checked = tmpRights.RightsState;
// 将子项添加到对应的父项中
foreach (TreeNode tmpTreeNode in _tvRightsView.Nodes)
{
// 如果与现存的节点父级权限相同
if (tmpTreeNode.Tag.ToString() == tmpRights.ParentLevelRightsName)
{
// 递归添加所有层级子节点
tmpTreeNode.Nodes.Add(LoadAllChildTreeNode(childTreeNode, rightsCollection));
}
}
}
}


// 展开所有树节点
_tvRightsView.ExpandAll();


// 启用树视图的重绘
_tvRightsView.EndUpdate();
}


/// <summary>
/// 选中/取消树节点及其子节点勾选状态
/// </summary>
/// <param name="currentTreeNode">当前操作的节点</param>
internal void CheckOrUnCheckTreeNode(TreeNode currentTreeNode)
{
// 如果有选中单元格
if (_dgvOperatorList.SelectedCells.Count > 0)
{
// 保存当前选中行的操作员名称
string operatorName = _dgvOperatorList.Rows[_dgvOperatorList.SelectedCells[0].RowIndex].Cells["ModelName"].Value.ToString().Trim();
// 同步权限状态
RightsManagerUI.OperatorCollection[operatorName].RightsCollection[currentTreeNode.Tag.ToString()].RightsState = currentTreeNode.Checked;


// 同时选中/取消子节点的勾选
foreach (TreeNode childTreeNode in currentTreeNode.Nodes)
{
// 同步子节点勾选状态
childTreeNode.Checked = currentTreeNode.Checked;
// 递归勾选下层子节点
CheckOrUnCheckTreeNode(childTreeNode);
}
}
}


/// <summary>
/// 加载操作员列表
/// </summary>
internal void LoadOperatorList()
{
BLLFactory.BLLFactory bllFactory = new BLLFactory.BLLFactory();
IBLL.IOperatorManager operatorManager = bllFactory.BuildOperatorManager();


// 加载操作员列表
try
{
RightsManagerUI.OperatorCollection = operatorManager.GetAllOperatorInfo();
// 检查所有操作员的权限列表
foreach (Model.Operator tmpOperator in RightsManagerUI.OperatorCollection.Values)
{
// 如果权限为空就创建一个新的空权限集合
if (!(tmpOperator.RightsCollection is Dictionary<string, Model.Rights>))
{
tmpOperator.RightsCollection = new Dictionary<string, Model.Rights>();
// 创建权限管理类实例
Common.RightsMenuDataManager rmdManager = new Common.RightsMenuDataManager();
// 创建权限集合空结构
tmpOperator.RightsCollection = rmdManager.ReadMenuRightsItem(_msMain, tmpOperator.RightsCollection);
}
}


// 如果包含操作员信息
if (RightsManagerUI.OperatorCollection.Count > 0)
{
// 将操作员集合数据绑定显示到数据视图
BindOperatorInfoToDataGridView(RightsManagerUI.OperatorCollection);
// 重新指定当前登录操作员对象
foreach (Model.Operator tmpOperator in RightsManagerUI.OperatorCollection.Values)
{
if (tmpOperator.ModelName == RightsManagerUI.CurrentOperator.ModelName)
{
RightsManagerUI.CurrentOperator = RightsManagerUI.OperatorCollection[RightsManagerUI.CurrentOperator.ModelName];
// 将数据绑定显示到数据视图
BindDataToDataGridView(RightsManagerUI.CurrentOperator.RightsCollection);
// 将数据绑定加载到树形视图
BindDataToTreeView(RightsManagerUI.CurrentOperator.RightsCollection);


// 在操作员列表中选中当前操作员
foreach (DataGridViewRow dgvRow in _dgvOperatorList.Rows)
{
if (dgvRow.Cells["ModelName"].Value.ToString().Trim() == RightsManagerUI.CurrentOperator.ModelName)
{
dgvRow.Selected = true;
break;
}
}


break;
}
}
}
else
{
// 清空操作员列表的数据行
DgvOperatorList.Rows.Clear();
}
}
catch (Exception ex)
{
MessageBox.Show(
ex.Message,
"加载失败",
MessageBoxButtons.OK,
MessageBoxIcon.Error);
}
}
#endregion
}
}


四、文章结语


以上内容并未提供完整的源代码演示,仅仅提供一种解决此类问题的思路和方法。

(一)技术难点

1、将集合对象存储到数据库中(先使用序列化将集合对象流化,再将其转换为 Byte[] 数组);
2、使用递归法解析菜单对象及应用权限菜单项的权限设置(注意 MenuStrip 对象的第一层级的子项集合为 Items ,其余均为 DropDownItems ,所以在递归解析或应用设置时只能从第二层级的子项开始进行递归调用);
3、BindingSource 界面数据绑定对象 与 DataGridView 数据视图控件的配合使用(注意在绑定 Dictionary<Key, T> 泛型集合内容时,应直接将 BindingSource 的 DataSource 属性设置为该集合的 .Values 才能绑定到实际的内容);
4、TreeView 树形视图控件的使用(注意在增删树节点时应适当使用 BeginUpdate() 和 EndUpdate() 方法来防止闪屏问题);
5、隶属于多个权限组的操作员权限应为其所从属的多个权限组权限的并集(不算难,遍历一下集合即可)。


(二)扩展思考

此外,我所阐述的这种解决方案还可以再扩展一些人性化的功能。譬如本文没提及的一些人性化功能:

1、让管理员用户完全自定义各个操作员的权限菜单标题(我已在自己的项目扩展此功能);
2、让权限菜单项在禁用时同时将该项的 Enabled 属性设置为 False ,以防止被隐藏的菜单项被快捷键呼出(太简单了,读者自己在上面的代码中添加少量代码即可实现)。
3、让管理员自由选择使用基于用户或基于角色的权限管理方案(由于在操作员和权限组两个实体模型中都各自存储了自身的权限集合,所以,在这种相对松散的权限关系下,很容易实现此功能,此功能我也已在自己的项目中实现)。



2008年12月14日
By CodingMouse
  • 1
    点赞
  • 12
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值