FileZilla 425 Can't open data connection

mikel阅读(955)

WIN 2008 SERVER+FileZilla FTP Server,FTP端口:2013

防火墙中已允许FTP Server及端口访问,设置好后,FlashFxp客户端,被动模式,能连接,但是出错:425 Can’t open data connection,不能读出目录列表,服务端也提示:425 Can’t open data connectio。

解决方法:

1、首先尝试取消Flashfpx被动模式,还是本能列表;

2、更换FTP客户端工具为CuteFtp,仍然如此;

3、只能到服务器上找原因了。

FileZilla FTP Server->Edit->Settings->Passive mode settings,指定被动模式使用的端口范围,将Use custom port range前面打开,设置端口范围为2013到2019,然后在Windows防火墙中打开这些端口。

OK,问题解决,客户端连接正常,可以列表。
Keywords:FTP 错误425 Can’t open data connection 读取目录 列表失败 FileZilla 端口
************
  1. 第一步设置添加的目录是主目录 比如:f:\a\b\c
    如果一个用户仅只有一个共享的FTP目录,以下步骤可以省略了。
    如果想为一个用户设置多个盘符的共享目录,需要做以下的设置了:
  2. 第二步设置添加的其它目录必须设置 Aliases别名,否则client端连接后看不到其它的目录内容,只能看到主目录的内容
  3. 别名的设置是有技巧要求的:如增加目录:E:\myFTPDir 为Aliases别名目录,则需要如下设置:

    E:\myFTPDir  f:a\b\c\myFTPDirAliase  (重点是Aliases名称myFTPDirAliase前边要加主目录[f:\a\b\c]的完整的路径)

    这样设置完成后,客户端登陆后,即可看到主目录下面多了一个myFTPDirAliase的虚拟路径了

  4. 同一个虚拟目录也可以设置为多个别名 如:d:\FTP\softD|d:\FTP\mySoft 中间用|分隔即可
    这样client端即可以看到二个虚拟目录 /softD  和 /mySoft 二者的内容是相同的
  5. 设置完成 OK。结束。Enjoy

 

另:客户端支持多字符集,可以手动设置字符集

根据时间段计算工作日的天数(SqlServer),包含节假日的处理

mikel阅读(1669)

创建节假日表:

USE [XHManage]
GO

/****** Object:  Table [dbo].[Holiday]    Script Date: 02/20/2014 17:51:54 ******/
SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

CREATE TABLE [dbo].[Holiday](
[Id] [int] IDENTITY(1,1) NOT NULL,
[Name] [nvarchar](50) NOT NULL,
[BeginDate] [datetime] NOT NULL,
[EndDate] [datetime] NOT NULL,
[AddUser] [nvarchar](50) NOT NULL,
[AddTime] [datetime] NOT NULL,
[Exchange] [bit] NOT NULL,
CONSTRAINT [PK_Holiday] PRIMARY KEY CLUSTERED
(
[Id] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

ALTER TABLE [dbo].[Holiday] ADD  CONSTRAINT [DF_Holiday_AddTime]  DEFAULT (getdate()) FOR [AddTime]
GO

ALTER TABLE [dbo].[Holiday] ADD  CONSTRAINT [DF_Holiday_Exchange]  DEFAULT ((0)) FOR [Exchange]
GO

输入数据(2014年国家节假日设置数据):

INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘元旦’,’2014-1-1′,’2014-1-1′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘春节’,’2014-1-31′,’2014-2-6′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘清明节’,’2014-4-5′,’2014-4-7′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘劳动节’,’2014-5-1′,’2014-5-3′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘端午节’,’2014-6-2′,’2014-6-2′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘中秋节’,’2014-9-8′,’2014-9-8′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘国庆节’,’2014-10-1′,’2014-10-7′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘春节调休’,’2014-1-26′,’2014-1-26′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘春节调休’,’2014-2-8′,’2014-2-8′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘劳动节调休’,’2014-5-4′,’2014-5-4′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘国庆节调休 ‘,’2014-9-28′,’2014-9-28′,’admin’,’2014-2-20′,1)
INSERT INTO [XHManage].[dbo].[Holiday]([Name] ,[BeginDate] ,[EndDate] ,[AddUser] ,[AddTime] ,[Exchange])VALUES(‘国庆节调休 ‘,’2014-10-11′,’2014-10-11′,’admin’,’2014-2-20′,1)

 

 

 

go
create function [dbo].[WorkDay]
(
@beginday   datetime,
@endday   datetime
)
returns int
AS
begin
–set   datefirst   1
declare  @caldays   int
declare  @id   int
select   @caldays=0

while  DATEDIFF(d, @beginday,@endday)>=0
begin

if  datepart(dw,@beginday)>1 and datepart(dw,@beginday)<7
begin
SELECT @id=count(*) from Holiday
where @beginday between begindate and DATEADD(s,-1,DATEADD(day,1,enddate)) and [Exchange]=0
if(@id=0)
select   @caldays=@caldays+1
end
else
begin
SELECT @id=count(*) from Holiday
where @beginday between begindate and DATEADD(s,-1,DATEADD(day,1,enddate)) and [Exchange]=1
if(@id>0)
select   @caldays=@caldays+1
end
select   @beginday=dateadd(day,1,@beginday)
end
return   @caldays
end

测试:

go
select dbo.[WorkDay](‘2014-10-1′,’2014-10-31’)as ‘工作日’
select dbo.[WorkDay](‘2014-9-1′,’2014-9-30’)as ‘工作日’
select dbo.[WorkDay](‘2014-9-30′,’2014-9-30’)as ‘工作日’
select dbo.[WorkDay](‘2014-6-1′,’2014-6-30’)as ‘工作日’

sql server:自定義計算固定工作日,雙休日函數

mikel阅读(806)

---SQL server
declare @date datetime
set @date='2012-02-03'--getdate()
--本月第一天
SELECT DATEADD(mm, DATEDIFF(mm,0,@date), 0)
--本月最后一天
SELECT dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,@date)+1, 0))
---有個月多少天函數
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetAMonthHowNumber]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[GetAMonthHowNumber]
GO
CREATE   function  GetAMonthHowNumber
    @date datetime
)
returns int
as
begin
    declare @int int
    select @int=datediff(dd , @date , dateadd(mm, 1, @date))
    return @int
end
GO
--
select [dbo].[GetAMonthHowNumber] (getdate()) AS '月天數'
---計算當月周六,周日有多少天
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetAMonthStatSunNumber]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[GetAMonthStatSunNumber]
GO
CREATE   function  GetAMonthStatSunNumber
    @date datetime
)
returns int
as
begin
declare @Sdate datetime ,@Edate datetime
SELECT @Sdate=DATEADD(mm, DATEDIFF(mm,0,@date), 0)
SELECT @Edate=dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,@date)+1, 0))
--set @Sdate='2013-05-01'
--set @Edate='2013-05-31'
declare @aa table (strdate datetime) 
declare @i int
set @i=datediff(day,@Sdate,@Edate) 
while(@i>=0) 
begin
insert @aa  values (dateadd(day,@i,@Sdate)) 
set @i=@i-1 
end
select @i= count(*)  from @aa where   datepart(weekday,strdate) in (1,7)  --not in (1,7)
return @i
end
go
--
select [dbo].[GetAMonthStatSunNumber] (getdate()) AS '雙休日天數'
--計算當月除周六,周日有多少天,也是有多少工作日
if exists (select * from dbo.sysobjects where id = object_id(N'[dbo].[GetAMonthWorkDayNumber]') and xtype in (N'FN', N'IF', N'TF'))
drop function [dbo].[GetAMonthWorkDayNumber]
GO
CREATE   function  GetAMonthWorkDayNumber
    @date datetime
)
returns int
as
begin
declare @Sdate datetime ,@Edate datetime
SELECT @Sdate=DATEADD(mm, DATEDIFF(mm,0,@date), 0)
SELECT @Edate=dateadd(ms,-3,DATEADD(mm, DATEDIFF(m,0,@date)+1, 0))
--set @Sdate='2013-05-01'
--set @Edate='2013-05-31'
declare @aa table (strdate datetime) 
declare @i int
set @i=datediff(day,@Sdate,@Edate) 
while(@i>=0) 
begin
insert @aa  values (dateadd(day,@i,@Sdate)) 
set @i=@i-1 
end
select @i= count(*)  from @aa where   datepart(weekday,strdate) not in (1,7)  --not in (1,7)
return @i
end
go
---
select [dbo].[GetAMonthWorkDayNumber] (getdate()) as '工作日天數'

从ICLassFactory 为 CLSID的COM组建创建实例失败

mikel阅读(2054)

     首先针对这个问题,我注销服务器,然后再次进入服务器,就不会报错了,如果再次作业失败,进行编辑时,还是会报上图的错误,然后再次注销后登录、、、、
下面是网上的解决方案:
1、这个问题,写SQL 脚本执行自动备份时出现的错误,在SQL Server 代理,选中其中一个作业 ==》 步骤,点击编辑(前提是把一切步骤都执行完成之后),就出现这个问题了,其实这里面有提示是什么错误,看到其他信息没有,最后面显示 Microsoft.SQLServer.ManagedDTS,应该能想到,可能这个是某个软件,或者说在sql server 2008R2安装的时候,没有安装这个组件,没办法咯,只能现在下呗,

Microsoft.SQLServer.ManagedDTS是SSIS所用到的组件.

2、在命令行里执行下面的语句重新注册dts.dll文件,分别对应不同的平台:

(x86)
c:\windows\system32\regsvr32 “C:\Program Files\Microsoft SQL Server\100\DTS\Binn\dts.dll”
(x64)
c:\windows\syswow64\regsvr32 “C:\Program Files (x86)\Microsoft SQL Server\100\DTS\Binn\dts.dll”

 

3、

 

1.开始->运行,输入dcomcnfg.exe

2.打开组件服务器->计算机->我的电脑->DCOM配置;

3.找到Microsoft Office Excel或者Microsoft Office Word点击右键->属性

4.选择安全,将启动和激活权限、访问权限、配置权限全部选择自定义,之后编辑,添加everyone用户,给它所有的权限

5.点击确定。

SQL SERVER 判断星期几

mikel阅读(1001)

 

  1. set datefirst 1
  2. if DATEPART(DW,GETDATE())=’1′
  3.         print ‘星期一’
  4.     ELSE IF
  5.         DATEPART(DW,GETDATE())=’2′
  6.         print ‘星期二’
  7.     ELSE IF
  8.         DATEPART(DW,GETDATE())=’3′
  9.         print ‘星期三’
  10.     ELSE IF
  11.         DATEPART(DW,GETDATE())=’4′
  12.         print ‘星期四’
  13.     ELSE IF
  14.         DATEPART(DW,GETDATE())=’5′
  15.         print ‘星期五’
  16.     ELSE IF
  17.         DATEPART(DW,GETDATE())=’6′
  18.         print ‘星期六’
  19.     ELSE IF
  20.         DATEPART(DW,GETDATE())=’7′
  21.         print ‘星期日’

这段代码是根据配置函数@@datefirst编写的。

第一步:

配置datefirst初始值为1;

第二步:

用IF语句判断当前返回值,并输出“星期几”的日期显示。

本文出自 “ItFans.520” 博客,请务必保留此出处http://51you.blog.51cto.com/3561752/1102057

EasyUI修改DateBox和DateTimeBox的默认日期格式

mikel阅读(2121)

        最近整理Easyui控件的时候,对Easyui的DateBox控件和DateTimeBox控件进行了梳理,而我之 所以将EasyUI的DateBox控件和DateTimeBox控件放在一起,归为一类,是因为这两个控件没有什么区别,如果你非得说这两个控件有区 别,也无非是DateTimeBox控件后面除了基本的年月日之外带上了小时或者分钟或者秒什么的,更何况,这两个控件在进行日期格式化时所采用的方法也 是一样的。

DateBox介绍:

Demo实例参看:

http://www.jeasyui.com/demo/main/index.php?plugin=DateBox&theme=default&dir=ltr&pitem=

属性方法介绍参看:

http://www.jeasyui.com/documentation/index.php#

DateTimeBox介绍:

Demo实例参看:

http://www.jeasyui.com/demo/main/index.php?plugin=DateTimeBox&theme=default&dir=ltr&pitem=  

属性方法介绍参看:

http://www.jeasyui.com/documentation/index.php#

控件的默认格式:

DateBox控件默认的日期格式是:                           DateTimeBox控件默认日期格式是:

                                             

这种显示的格式都是:mm/dd/yyyy,对于我们中国人来说,并不适合我们的习惯,同时,他们夹带了“Today”、“Ok”、“Close”等字 样,如果就这么给用户使用,虽然说当前这个社会,我们需要面向国际,但软件的使用者还是我们自己中国客户,所以说问题就来了~~~~~

问题一:英文改中文

        解决方法:引入easyui-lang-zh_CN.js

下载地址为:http://www.softhy.net/soft/33695.htm

        注意:

第一:Easyui引用js时,要先引入JQuery.min.js、其次是JQuery.easyui.min.js,最后是easyui-lang-zh_CN.js,存在顺序关系。

第二:引入easyui-lang-zh_CN.js后,相应的提示信息也会改变,同时改变了这两个控件的日期默认显示格式,为:yyyy-mm-dd,效果如下:

问题二:日期格式改正

        这两个控件在修改日期格式上都可以采用以下两种方法,我现在已以DateTimeBox为例,说明他们是如何实现的。

方法一:split函数+正则表达式

  1. <div style=“margin:20px 0;”></div>
  2.     定义日期格式:
  3.     <input class=“easyui-datetimebox” data-options=“formatter:ww4,parser:w4” style=“width:200px;”> yyyy年mm月dd日hh点</input>
  4.     <script type=“text/JavaScript>
  5.         function ww4(date){
  6.             var y = date.getFullYear();
  7.             var m = date.getMonth()+1;
  8.             var d = date.getDate();
  9.             var h = date.getHours();
  10.             return  y+‘年’+(m<10?(‘0’+m):m)+‘月’+(d<10?(‘0’+d):d)+‘日’+(h<10?(‘0’+h):h)+‘点’;
  11.         }
  12.         function w4(s){
  13.             var reg=/[\u4e00-\u9fa5]/  //利用正则表达式分隔
  14.             var ss = (s.split(reg));
  15.             var y = parseInt(ss[0],10);
  16.             var m = parseInt(ss[1],10);
  17.             var d = parseInt(ss[2],10);
  18.             var h = parseInt(ss[3],10);
  19.             if (!isNaN(y) && !isNaN(m) && !isNaN(d) && !isNaN(h)){
  20.                 return new Date(y,m-1,d,h);
  21.             } else {
  22.                 return new Date();
  23.             }
  24.         }
  25.     </script>

显示效果如下:

方法二:substring函数

  1. <div style=“margin:20px 0;”></div>
  2.     定义日期格式:
  3.     <input class=“easyui-datetimebox” data-options=“formatter:ww3,parser:w3” style=“width:200px;”> yyyy/mm/dd hh-mm-ss</input>
  4.     <script type=“text/JavaScript>
  5.         function ww3(date){
  6.             var y = date.getFullYear();
  7.             var m = date.getMonth()+1;
  8.             var d = date.getDate();
  9.             var h = date.getHours();
  10.             var min = date.getMinutes();
  11.             var sec = date.getSeconds();
  12.             var str = y+‘/’+(m<10?(‘0’+m):m)+‘/’+(d<10?(‘0’+d):d)+‘/’+‘ ‘+(h<10?(‘0’+h):h)+‘:’+(min<10?(‘0’+min):min)+‘:’+(sec<10?(‘0’+sec):sec);
  13.             return str;
  14.         }
  15.         function w3(s){
  16.             if (!s) return new Date();
  17.             var y = s.substring(0,4);
  18.             var m =s.substring(5,7);
  19.             var d = s.substring(8,10);
  20.             var h = s.substring(11,14);
  21.             var min = s.substring(15,17);
  22.             var sec = s.substring(18,20);
  23.             if (!isNaN(y) && !isNaN(m) && !isNaN(d) && !isNaN(h) && !isNaN(min) && !isNaN(sec)){
  24.                 return new Date(y,m-1,d,h,min,sec);
  25.             } else {
  26.                 return new Date();
  27.             }
  28.         }
  29.     </script>

显示效果如下:                 
注意:

方法二不适合将日期格式改为类似于yyyy-m-d h-m-s,理由:Substring函数是截取字符串,而在yyyy-m-d hh-mm-ss这种日期格式中m、d、h、m、s可能会存在一位数和二位数,整个日期的长度不固定,因此在截取的时候,选取的字符位置不能固定。

 

总结

       这两种方法的本质一样,不管是哪种解决方式,它的原理都是不变的,主要是借用Formatter和Parser函数,其中Formatter函数使得选择日期后将其格式化为我们需要的格式,Parser是分析字符串的函数,这个函以’date’为参数并返回一个日期。问题来了,不光是要知道它们的解决方式,还要知道它们是怎么产生的,应该怎么从根本上解决这个问题,这些解决方式背后的本质是什么。

MVC、MVP以及Model2[下篇] - Artech - 博客园

mikel阅读(1419)

来源: MVC、MVP以及Model2[下篇] – Artech – 博客园

[上篇] 通过采用MVC模式,我们可以将可视化UI元素的呈现、UI处理逻辑和业务逻辑分别定义在View、Controller和Model中,但是对于三者之 间的交互,MVC并没有进行严格的限制。最为典型的就是允许View和Model绕开Controller进行直接交互,View不仅仅可以通过调用 Model获取需要呈现给用户的数据,Model也可以直接通知View让其感知到状态的变化。当真正地将MVC应用于具体的项目开发中,不论是基于 GUI的桌面应用还是基于Web UI的Web应用,如果不对Model、View和Controller之间的交互进行更为严格的限制,我们编写的程序可以比自治视图更为难以维护。

今 天我们将MVC视为一种模式(Pattern),但是作为MVC最初提出者的Trygve M. H. Reenskau实际是将MVC视为一种范例(Paradigm),这可以从它在《Applications Programming in Smalltalk-80(TM): How to use Model-View-Controller (MVC)》中对MVC的描述可以看出来:In the MVC paradigm the user input, the modeling of the external world, and the visual feedback to the user are explicitly separated and handled by three types of object, each specialized for its task.

模式和范例的区别在于前者可以直接 应用到具体的应用上,而后者则仅仅提供一些基本的指导方针。在我看来MVC是一个很宽泛的概念,任何基于Model、View和Controller对 UI应用进行分解的设计都可以成为MVC。当我们采用MVC的思想来设计UI应用的时候,应该根据应用框架(比如Windows Forms、WPF和Web Forms)的特点对Model、View和Controller的界限以及相互之间的交互设置一个更为严格的规则。在软件设计的发展历程中出现了一些 MVC的变体(Varation),它们遵循定义在MVC中的基本原理。我们现在来简单地讨论一些一个常用的MVC变体。

目录
MVP
PV与SoC
View和Presenter交互的规则(针对SoC模式)
实例演示:SoC模式的应用
Model 2

ASP.NETMVC与Model2

一、 MVP

MVP 是一种广泛使用的基于架构模式,使用与基于事件驱动的应用框架,比如ASP.NET Web Forms和Windows Forms应用。MVP中的M和V对应中MVC的Model和View,而P(Presenter)则自然代替了MVC中的Controller。但是 MVP并非仅仅体现在从Controller到Presenter的转换,更对地体现在Model、View和Presenter之间的交互上。

MVC 模式中元素之间混乱的交互只要体现在允许View和Model绕开Controller进行单独“交流”,这在MVP模式中得到了彻底地解决。如下图所 示,能够与Model直接进行交互的仅限于Presenter,View只能间接地通过Presenter调用Model。Model的独立性在这里得到 了真正的体现,它不仅仅与可视化元素的呈现无关(View)和与UI处理逻辑(Presenter)无关。使用MVP的应用是用户驱动的而非Model驱 动的,所以Model不需要主动通知View以提醒状态发生了改变。

image

MVP 不仅仅避免了View和Model之间的耦合,更进一步地降低Presenter对View的依赖。如图1-2所示,Presenter依赖的是一个抽象 化的View,即View实现的接口IView。这带来的最直接的好处就是使定义在Presenter中的UI处理逻辑变得易于测试。由于 Presenter对View的依赖行为定义在接口IView中,我们只需要Mock一个实现了该接口的View就能对Presenter进行测试。

构 成MVP三要素之间的交互体现在两个方面,即View/Presenter和Presenter/Model。Presenter和Model之间的交互 很清晰,仅仅体现在Presenter对Model的单向调用。而View和Presenter之间该采用怎样的交互方式是整个MVP的核心,MVP针对 关注点分离的初衷能否体现在具体的应用中很大程度上取决于两者之间的交互方式是否正确。按照View和Presenter之间的交互方式以及View本身 的职责范围,Martin Folwer将MVP可分为PV(Passive View)和SoC(Supervising Controller)两种模式。

PV与SoC

解决View难以测试的最好的办法就是让它无须测试,如果 View不需要测试,其先决条件就是让它尽可能不涉及到UI处理逻辑,而这就是PV模式目的所在。顾名思义,PV(Passive View)是一个被动的View,针对包含其中的UI元素(比如控件)的操作不是由View自身来操作,而交给Presenter来操控。

如 果我们纯粹地采用PV模式来设计View,意味着我们需要将定义View中的UI元素通过属性的形式暴露出来。具体来说,当我们在为View定义接口的时 候,需要定义基于UI元素的属性以使Presenter可以对View进行细粒度地操作,但这并不是意味着我们直接将View上的控件暴露出来。举个简单 的例子,我们开发的HR系统 中具有如下图所示的Web页面用于根据部门获取员工列表。

image

现 在通过ASP.NET Web Form应用来涉及这个页面,我们来讨论一下如果采用PV模式View的接口该如何定义。对于Presenter来说,View供它操作的控件有两个,一 个是包含所有部门列表的DropDownList,另一个则是显示员工列表的GridView。在页面加载的时候,Presenter将部门列表绑定在 DropDownList上,与此同时包含所有员工的列表被绑定到GridView。当用户选择某个部门并点击“查询”按钮后,View将包含筛选部门在 内的查询请求转发给Presenter,后者筛选出相应的员工列表之后将其绑定到GridView。

如果我们为该View定义一个接口 IEmployeeSearchView,我们不能像如下的代码所示将上述这两个控件直接以属性的形式暴露出来。针对数据绑定对控件类型的选择属于 View的内部细节(比如说针对部门列表的显示,我们可以选择DropDownList也可以选择ListBox),不能体现在表示用于抽象View的接 口中。在另一方面,理想情况下定义在Presenter中的UI处理逻辑应该是与具体的技术平台无关的,如果在接口中涉及到了控件类型,这无疑将 Presenter也具体的技术平台绑定在了一起。

   1: public interface IEmployeeSearchView
   2: {
   3:     DropDownList Departments { get;}
   4:     GridView Employees { get; }
   5: }

正确的接口和实现该接口的View(一个Web页面)应该采用如下的定义方式。Presenter通过属性Departments和 Employees进行赋值进而实现对DropDownList和GridView进行绑定,通过属性SelectedDepartment得到用户选择 的筛选部门。为了尽可能让接口只暴露必须的信息,我们特意将对属性的读写作了控制。

   1: public interface IEmployeeSearchView
   2: {
   3:     IEnumerable<string> Departments { set; }
   4:     string SelectedDepartment { get; }
   5:     IEnumerable<Employee> Employees { set; }
   6: }
   7:
   8: public partial class EmployeeSearchView: Page, IEmployeeSearchView
   9: {
  10:     //其他成员
  11:     public IEnumerable<string> Departments
  12:     {
  13:         set
  14:         {
  15:             this.DropDownListDepartments.DataSource = value;
  16:             this.DropDownListDepartments.DataBind();
  17:         }
  18:     }
  19:     public string SelectedDepartment
  20:     {
  21:         get { return this.DropDownListDepartments.SelectedValue;}
  22:     }
  23:     public IEnumerable<Employee> Employees
  24:     {
  25:         set
  26:         {
  27:             this.GridViewEmployees.DataSource = value;
  28:             this.GridViewEmployees.DataBind();
  29:         }
  30:     }
  31: }

虽然从可测试性的角度来说PV模式是一种不错的选择,因为所有的UI处理逻辑全部定义在Presenter上,意味着所有的UI处理逻辑都可以被测 试。但是我们需要将View可供操作的UI元素定义在对应的接口中,对于一些复杂的富客户端(Rich Client)View来说,接口成员将会变得很多,这无疑会提升编程所需的代码量。从另一方讲,由于Presenter需要在控件级别对View进行细 粒度的控制,这无疑会提供Presenter本身的复杂度,往往会使原本简单的逻辑复杂化,在这种情况下我们往往采用SoC模式。

在SoC(Supervising Controller)模式下,为了降低Presenter的复杂度,我们将诸如数据绑定和格式化这样简单的UI处理逻辑逻辑转移到View中,这些处理 逻辑会体现在View实现的接口中。尽管View从Presenter中接管了部分UI处理逻辑,但是Presenter依然是整个三角关系的驱动 者,View被动的地位依然没有改变。对于用户作用在View上的交互操作,View本身并不进行响应,而是直接将交互请求转发给Presenter,后 者在独立完成相应的处理流程(可能涉及到针对Model的调用)之后会驱动View或者创建新的View作为对用户交互操作的响应。

View和Presenter交互的规则(针对SoC模式)

View和Presenter之间的交互是整个MVP的核心,能够正确地应用MVP模式来架构我们的应用极大地取决于能够正确地处理View和 Presenter两者之间的关系。在由Model、View和Presenter组成的三角关系的核心不是View而是 Presenter,Presenter不是View调用Model的中介,而是最终决定如何响应用户交互行为的决策者。

打个比方,View是Presenter委派到前端的客户代理,而作为客户的自然就是最终的用户。对于以鼠标/键盘操作体现的交互请求应该如何处 理,作为代理的View并没有决策权,所以它会将请求汇报给委托人Presenter。View向Presenter发送用户交互请求应该采用这样的口 吻:“我现在将用户交互请求发送给你,你看着办,需要我的时候我会协助你”,而不应该是这样:“我现在处理用户交互请求了,我知道该怎么办,但是我需要你 的支持,因为实现业务逻辑的Model只信任你”。

对于Presenter处理用户交互请求的流程,如果中间环节需要涉及到Model,它会直接发起对Model的调用。如果需要View的参与(比如需要将Model最新的状态反应在View上),Presenter会驱动View完成相应的工作。

对于绑定到View上的数据,不应该是View从Presenter上“拉”回来的,应该是Presenter主动“推”给View的。从消息流 (或者消息交换模式)的角度来讲,不论是View向Presenter完成针对用户交互请求的同志,还是Presenter在进行交互请求处理过程中驱动 View完成相应的UI操作,都是单向(One-Way)的。反应在 应用编程接口的定义上就意味着不论是定义在Presenter中被View调用的方法,还是定义在IView接口中被Presenter调用的方法最好都 是没有返回值得。如果不采用方法调用的形式,我们也可以通过事件注册的方式实现View和Presenter的交互,事件机制体现的消息流无疑是单向的。

View本身仅仅实现单纯的、独立的UI处理逻辑,它处理的数据应该是Presenter实时推送给它的,所以View尽可能不维护数据状态。定义 在IView的接口最好只包含方法,而避免属性的定义,Presenter所需的关于View的状态应该在接收到View发送的用户交互请求的时候一次得 到,而不需要通过View的属性去获取。

实例演示:SoC模式的应用

为了让读者对MVP模式,尤其是该模式下的View和Presenter之间的交互方式具有一个深刻的认识,我们现在来进行一个简单的实例演示。本 实例采用上面提及的关于员工查询的场景,并且采用ASP.NET Web Form来建立这个简单的应用,最终呈现出来的效果如上图所示。前面我们已经演示了采用PV模式下的IView应该如何定义,现在我们来看看SoC模式下 的IView有何不同。先来看看表示员工信息的数据类型如何定义,我们通过具有如下定义的数据类型Employee来表示一个员工。简单起见,我们仅仅定 义了表示员工基本信息(ID、姓名、性别、出生日期和部门)的5个属性。

   1: public class Employee
   2: {
   3:     public string     Id { get; private set; }
   4:     public string     Name { get; private set; }
   5:     public string     Gender { get; private set; }
   6:     public DateTime   BirthDate { get; private set; }
   7:     public string     Department { get; private set; }
   8:
   9: public Employee(string id, string name, string gender, DateTime birthDate, string department)
  10:     {
  11:         this.Id         = id;
  12:         this.Name       = name;
  13:         this.Gender     = gender;
  14:         this.BirthDate  = birthDate;
  15:         this.Department = department;
  16:     }
  17: }

作为包含应用状态和状态操作行为的Model通过如下一个简单的EmployeeRepository类型还体现。如代码所示,表示所有员工列表的 数据通过一个静态字段来维护,而GetEmployees返回指定部门的员工列表。如果没有指定筛选部门或者指定的部门字符为空,则直接返回所有的员工列 表。

   1: public class EmployeeRepository
   2: {
   3:     private static IList<Employee> employees;
   4:     static EmployeeRepository()
   5:     {
   6:         employees = new List<Employee>();
   7:         employees.Add(new Employee("001", "张三", "男", new DateTime(1981, 8, 24), "销售部"));
   8:         employees.Add(new Employee("002", "李四", "女", new DateTime(1982, 7, 10), "人事部"));
   9:         employees.Add(new Employee("003", "王五", "男", new DateTime(1981, 9, 21), "人事部"));
  10:     }
  11:     public IEnumerable<Employee> GetEmployees(string department = "")
  12:     {
  13:         if (string.IsNullOrEmpty(department))
  14:         {
  15:             return employees;
  16:         }
  17:         return employees.Where(e => e.Department == department).ToArray();
  18:     }
  19: }

接下来我们来看作为View接口的IEmployeeSearchView的定义。如下面的代码片断所示,该接口定义了BindEmployees 和BindDepartments两个方法,分别用于绑定基于部门列表的DropDownList和基于员工列表的DataView。除此之 外,IEmployeeSearchView接口还定义了一个事件DepartmentSelected,该事件会在用户选择了筛选部门后点击“查询”按 钮时触发。DepartmentSelected事件参数类型为自定义的DepartmentSelectedEventArgs,属性 Department表示用户选择部门。

   1: public interface IEmployeeSearchView
   2: {
   3:     void BindEmployees(IEnumerable<Employee> employees);
   4:     void BindDepartments(IEnumerable<string> departments);
   5:     event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
   6: }
   7:
   8: public class DepartmentSelectedEventArgs : EventArgs
   9: {
  10:     public string Department { get; private set; }
  11:     public DepartmentSelectedEventArgs(string department)
  12:     {
  13:         Guard.ArgumentNotNullOrEmpty(department, "department");
  14:         this.Department = department;
  15:     }
  16: }

作为MVP三角关系核心的Presenter通过具有如下定义的EmployeeSearchPresenter表示。如下面的代码片断所示,表示 View的只读属性类型为IEmployeeSearchView接口,而另一个只读属性Repository则表示作为Model的 EmployeeRepository对象,两个属性均在构造函数中初始化。

   1: public class EmployeeSearchPresenter
   2: {
   3:     public IEmployeeSearchView View { get; private set; }
   4:     public EmployeeRepository Repository { get; private set; }
   5:
   6:     public EmployeeSearchPresenter(IEmployeeSearchView view)
   7:     {
   8:         this.View = view;
   9:         this.Repository = new EmployeeRepository();
  10:         this.View.DepartmentSelected += OnDepartmentSelected;
  11:     }
  12:     public void Initialize()
  13:     {
  14:         IEnumerable<Employee> employees = this.Repository.GetEmployees();
  15:         this.View.BindEmployees(employees);
  16:         string[] departments = new string[] { "销售部", "采购部", "人事部", "IT部" };
  17:         this.View.BindDepartments(departments);
  18:     }
  19:     protected void OnDepartmentSelected(object sender, DepartmentSelectedEventArgs args)
  20:     {
  21:         string department = args.Department;
  22:         var employees = this.Repository.GetEmployees(department);
  23:         this.View.BindEmployees(employees);
  24:     }
  25: }

在构造函数中我们注册了View的DepartmentSelected事件,作为事件处理器的OnDepartmentSelected方法通过 调用Repository(即Model)实现了针对所选部门的筛选,而返回的员工列表通过调用View的BindEmployees方法实现了在 View上的数据绑定。在Initialize方法中,我们通过调用Repository获取了表示所有员工的列表,并通过View的 BindEmployees方法显示在界面上;通过调用View的BindDepartments方法将作为筛选条件的部门列表绑定在View上。

最后我们来看看作为View的Web页面如何定义,如下所示的是作为页面主体部分的HTML,核心部分之包括一个用于绑定筛选部门列表的DropDownList和一个绑定员工列表的GridView。

   1: <html xmlns="http://www.w3.org/1999/xhtml">
   2:     <head runat="server">
   3:         ...
   4:     </head>
   5:     <body>
   6:         <form id="form1" runat="server">
   7:             <div id="page">
   8:                 <div class="top">
   9:                     选择查询部门:
  10:                     <asp:DropDownList ID="DropDownListDepartments" runat="server" />
  11:                     <asp:Button ID="ButtonSearch" runat="server" Text="查询" OnClick="ButtonSearch_Click" />
  12:                 </div>
  13:                 <asp:GridView ID="GridViewEmployees" runat="server" AutoGenerateColumns="false" Width="100%">
  14:                     <Columns>
  15:                         <asp:BoundField DataField="Name" HeaderText="姓名" />
  16:                         <asp:BoundField DataField="Gender" HeaderText="性别" />
  17:                         <asp:BoundField DataField="BirthDate" HeaderText="出生日期" DataFormatString="{0:dd/MM/yyyy}" />
  18:                         <asp:BoundField DataField="Department" HeaderText="部门"/>
  19:                     </Columns>
  20:                 </asp:GridView>
  21:             </div>
  22:         </form>
  23:     </body>
  24: </html>

如下所示的是该Web页面的后台代码的定义。它实现了定义在IEmployeeSearchView接口的两个方法(BindEmployees和 BindDepartments)和一个事件(DepartmentSelected)。表示Presenter的同名属性在构造函数中被初始化。在页面 加载的时候(Page_Load方法)Presenter的Initialize方法被调用,而在“查询”按钮被点击的时候 (ButtonSearch_Click)事件DepartmentSelected被触发。

   1: public partial class Default : Page, IEmployeeSearchView
   2: {
   3:     public EmployeeSearchPresenter Presenter { get; private set; }
   4:     public event EventHandler<DepartmentSelectedEventArgs> DepartmentSelected;
   5:     public Default()
   6:     {
   7:         this.Presenter = new EmployeeSearchPresenter(this);
   8:     }
   9:     protected void Page_Load(object sender, EventArgs e)
  10:     {
  11:         if (!this.IsPostBack)
  12:         {
  13:             this.Presenter.Initialize();
  14:         }
  15:     }
  16:     public void BindEmployees(IEnumerable<Employee> employees)
  17:     {
  18:         this.GridViewEmployees.DataSource = employees;
  19:         this.GridViewEmployees.DataBind();
  20:     }
  21:     public void BindDepartments(IEnumerable<string> departments)
  22:     {
  23:         this.DropDownListDepartments.DataSource = departments;
  24:         this.DropDownListDepartments.DataBind();
  25:     }
  26:     protected void ButtonSearch_Click(object sender, EventArgs e)
  27:     {
  28:         string department = this.DropDownListDepartments.SelectedValue;
  29:         DepartmentSelectedEventArgs eventArgs = new DepartmentSelectedEventArgs(department);
  30:         if (null != DepartmentSelected)
  31:         {
  32:             DepartmentSelected(this, eventArgs);
  33:         }
  34:     }
  35: }

二、Model2

Trygve M. H. Reenskau当初提出的MVC是作为基于GUI的桌面应用的架构模式,并不太适合Web本身的特性。虽然MVC/MVP也可以直接用于ASP.NET Web Form应用,但这是因为微软基于桌面应用的编程模式 来设计基于Web Form的ASP.NET应用框架的。Web应用不同于GUI桌面应用在于用户是通过浏览器与应用进行交互,交互请求和相应是通过HTTP请求和回复来完 成的。

为了让MVC能够Web应用提供原生的支持,另一个被称为Model2 的MVC变体被提出来,Model2来源于基于Java的Web应用架构模式。Java Web应用具有两种基本的架构模式,分别被称为Model1和Model2。Model1类似于我们前面提及的自治试图模式,它将数据的可视化呈现和用户 交互操作的处理逻辑合并在一起。Model1使用于那些比较简单的Web应用,对于相对复杂的应用应该采用Model 2。

为了让开发者采用相应的编程模式进行GUI桌面应用和Web应用的开发,微软通过ViewState和Postback对背后的HTTP请求和回复 机制进行了封装,使我们能够像编写Windows Forms应用一样采用事件驱动的方式进行ASP.NET Web Forms应用的编程。而Model 2采用完全不同的设计,它让开发者直接面向Web,让他们关注HTTP的请求和回复流程,所以Model 2提供对Web应用原生的支持。

对于Web应用来说,和用户直接交互的UI界面由浏览器来提供,接下来我们详细讨论作为MVC的三要素是如何相互协作对从浏览器发出的用户交互请求的响应的,下图所示的序列图体现了整个流程的全过程。

image

Model 2种一个HTTP请求的目标是Controller中的某个Action,后者体现为定义在Controller类型中的某个方法,所以对请求的处理最终 体现在对Controller对象的激活和对Action方法的执行。一般来说,Controller、Action以及作为Action方法的部分参数 (针对HTTP-GET)可以直接通过请求的URL解析出来。

如上图所示,我们通过一个拦截器(Interceptor)对抵达Web服务器的HTTP请求进行拦截。一般的Web应用框架都提供了针对这样一种 拦截机制,对于ASP.NET来说,我们可以以HttpModule的形式来定义这么一个拦截器。拦截器根据请求解析出目标Controller和对应的 Action,Controller被激活之后Action方法被执行。对于需要传入Action方法的输入参数,则来源于请求地址或/和Post的数 据。

在Controller的Action方法被执行过程中,它可以调用Model获取或者改变其状态。在Action方法执行的最后阶段会选择相应的 View,绑定在View上的数据来源Model或者基于显示要求进行得简单逻辑计算,我们有时候它们成为VM(View Model),即基于View的Model(MVC中的Model是与UI无关的)。生成的View最终写入HTTP回复并最终呈现在用户的浏览器中。

和MVP一样,Model 2完全隔断了View和Model之间的联系。Controller作为支配者地位在Model 2体现尤为明显,用户交互请求不再由View报告给Controller(Presenter),而是由拦截器直接转发给Controller。 Controller不仅仅决定着Model的调用,还决定了View的选择和生成。ASP.NET MVC就是基于Model 2模式设计的。

三、ASP.NETMVC与Model2

凭着读者对ASP.NET MVC的了解,通过上面对Model2模式的介绍,应该很清楚地认识到ASP.NET MVC就是根据Model2模式设计的。基于HTTP请求的拦截机制是通过一个自定义的HttpModule和一个自定义HttpHandler来实现 的,在本章的最后我们会通过一个例子来模拟ASP.NET MVC的工作原理。

在上面我们多次强调MVC的Model是维持应用状态提供业务功能的领域模型,或者是多层架构中进入业务层的入口或者业务服务的代理,但是 ASP.NET MVC中的Model还是这个Model吗?稍微了解ASP.NET MVC的读者都知道ASP.NET MVC的Model仅仅是绑定到View上的数据而已,它和MVC模式中的Model并不是一回事。由于ASP.NET MVC中的Model是基于View的,我们可以将其称为View Model。

由于ASP.NET MVC只有View Model,所以ASP.NET MVC应用框架本社实际上仅仅关于View和Controller,真正的Model以及Model和Controller之间的交互体现在我们如何来设 计Controller。我个人觉得将用于构建ASP.NET MVC的MVC模式成为M(Model)-V(View)-VM(View Model)-C(Controller)也许更为准确。

参考资料:
1、Dino Esposito,Andrea Saltarello《Micorsoft .NET Architecting Applications for Enterprise》
2、Adam Freeman, Steven Sanderson《Pro ASP.NET MVC 3 Framework》
3、Martin Fowler 《GUI Architectures》: http://martinfowler.com/eaaDev/uiArchs.html
4、Martin Fowler 《Passive View》:http://martinfowler.com/eaaDev/PassiveScreen.html
5、Martin Fowler 《Supervising Controller》:http://martinfowler.com/eaaDev/SupervisingPresenter.html
6、Mike Potel,VP & CTO,Taligent, Inc. 《MVP: Model-View-Presenter The Taligent Programming Model for C++ and Java》
7、Model View Controller- Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller
8、Model View Presenter- Wikipedia, the free encyclopedia:http://en.wikipedia.org/wiki/Model_View_Presenter
9、Steve Burbeck, Ph.D. 《Applications Programming in Smalltalk-80(TM):How to use Model-View-Controller (MVC)》:http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html

MVC、MVP以及Model2[上篇]
MVC、MVP以及Model2[下篇]

MVC、MVP以及Model2[上篇] - Artech - 博客园

mikel阅读(1145)

来源: MVC、MVP以及Model2[上篇] – Artech – 博客园

对于大部分面向最终用户的应用来说,它们都需要具有一个可视化的UI与用户进行交互,我 们将这个UI称为视图(View)。在早期,我们倾向于将所有与视图相关的逻辑糅合在一起,这些逻辑包括数据的呈现、用户操作的捕捉与相应以及和针对数据 存储(比如数据库)的操作。我们将这种设计模式称为自治视图(AV,Autonomous View)。

目录
一、自治视图
二、MVC模式
三、多层架构中的MVC

一、自治视图

说 到自治视图,可能很多人会感到模式,但是我想很多人(尤其是.NET开发人员)可能经常在采用这种模式来设计我们的应用。Windows Forms和ASP.NET Web Forms虽然分别属于GUI和Web开发框架,但是它们都采用了事件驱动的开发方式。所有与UI相关的逻辑都可以定义在针对视图(Windows Form或者Web Form)的后台代码(Code Behind)中,并最终注册到视图本身或者视图元素(控件)的相应事件上。

一个典型的人机交互应用具有三个主要的关注点,即数据在可视化界面上的呈现、UI处理逻辑(用于处理用户交互式操作的逻辑)和业务逻辑。对于自治视图模式来说,它实际上这三种混合在一起,势必会带来如下一些问题:

首先,业务逻辑是与UI无关的,应该最大限度地被重用。由于业务逻辑定义在自治视图中,相当于完全与视图本身绑定在一起。如果我们能够将UI的行为抽象出来,基于抽象化UI的处理逻辑也是可以被共享的,定义在自治视图的UI处理逻辑完全丧失了重用的可能。

其次,业务逻辑具有最强的稳定性,UI处理逻辑次之,而可视化界面上的呈现最差,比如我们经常会为了更好的呈现效果来调整HTML。将具有不同稳定性的元素融为一体,具有最差稳定性的元素决定了整体的稳定性,这是“短板理论”在软件设计中的体现。

再次,任何涉及到UI的组件都不易测试。UI是呈现给人看的,并且用于与人进行交互,用机器来模拟活生生的人来对组件实施自动化测试不是一件容易的事,自治视图严重损害了组件的可测试性。

为 了解决自治视图导致的这些问题,我们需要采用采用关注点分离(SoC, Seperation of Concerns)的方针将可视化界面呈现、UI处理逻辑和业务逻辑三者分离出来,并且采用采用合理的交互方式将它们之间的影响降到最低。由于将三者“分 而治之”,自然也使UI逻辑和业务逻辑编程的容易被测试的组件,使测试驱动设计与开发变成了可能。这里用于进行关注点分离的模式就是MVC。

二、MVC模式

MVC 的创建者是Trygve M. H. Reenskau,他是挪威的计算机专家,同时也是奥斯陆大学的名誉教授。MVC是他在1979年访问施乐帕克研究中心(Xerox PARC,Xerox Palo Alto Research Center)期间是提出一种主要针对GUI应用的软件架构模式。MVC最初用于SmallTalk,Trygve最初对MVC的描述记录在 《Applications Programming in Smalltalk-80(TM):
How to use Model-View-Controller (MVC)》这篇论文中,有兴趣的读者可以通过地址http://st-www.cs.illinois.edu/users/smarch/st-docs/mvc.html阅读这篇论文。MVC体现了关注点分离这一基本的设计方针,它将构成一个人机交互应用涉及到的功能分为Model、Controller和View三部分,三者各自具有的基本职责或者功能范围如下:

  • Model:是对应用状态和业务功能的封装,可以看成是同时包含数据和行为的领域模型(Domain Model)。Model接受Controller的请求执行相应的业务功能,并在状态改变的时候通知View。
  • View:实现可视化界面的呈现,捕捉最终用户的交互操作(比如鼠标和键盘操作)。
  • Controller:View 捕获到用户交互操作后会直接转发给Controller,后者完成相应的UI逻辑。如果需要涉及业务功能的调用,Controller会直接调用 Model。在完成UI处理之后,Controller会根据需要控制原View或者创建新的View对用户交互操作予以响应。

下 图揭示了MVC模式下Model、View和Controller之间的交互。对于传统的MVC模式,很多人认为Controller仅仅是View和 Model之间的中介,实则不然,View和Model存在直接的联系。View可以直接调用Model查询其状态信息。当Model状态发生改变的时 候,它也可以直接通知View。比如在一个提供股票实时价位的应用,维护股价信息的Model在股价变化的情况下可以直接通知相关的View改变其显示信 息。

image

从 消息交换模式的角度来讲,Model针对View的状态通知和View针对Controller的用户交互通知都是单向的,我们推荐采用事件机制来实现这 两种类型的通知。从设计模式的角度来讲就是采用观察者(Observer)模式通过注册/订阅的方式来实现它们,即View作为Model的观察者通过注 册相应的事件来检测状态的改变,而Controller作为View的观察者通过注册相应的事件来处理用户的交互操作。

三、多层架构中的MVC

我看到很多人将MVC和所谓的“三层架构”进行比较,其实两者并没有什么可比性,MVC更不是分别对应着UI、业务逻辑和数据存取三个层次。不过两者也不能说完全没有关系,我们现在就来讨论这个问题。

Trygve M. H. Reenskau当时提出MVC的时候实际上将其作为构建整个GUI应用的架构模式,而Model维护着整个应用的状态并实现了所有的业务逻辑,它更多地 体现为一个领域模型。而对于多层架构来说(比如我们经常提及的三层架构),MVC是被当成是UI呈现层(Presentation Layer)的设计模式,而Model则更多地体现为访问业务层的入口(Gateway)。如果采用面向服务的设计,将业务功能定义成相应服务并通过接口 (契约)的形式暴露出来,这里的Model甚至还可以表示成进行服务调用的代理。

MVC、MVP以及Model2[上篇]
MVC、MVP以及Model2[下篇]

通用权限管理 - xiyang_1990的专栏 - 博客频道 - CSDN.NET

mikel阅读(846)

来源: 通用权限管理 – xiyang_1990的专栏 – 博客频道 – CSDN.NET

一.引言

因为做过的一些系统的权限管理的功能虽然在逐步完善,但总有些不尽人意的地方,总想抽个时间来更好的思考一下权限系统的设计。

权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

二.设计目标

设计一个灵活、通用、方便的权限管理系统。

在这个系统中,我们需要对系统的所有资源进行权限控制,那么系统中的资源包括哪些呢?我们可以把这些资源简单概括为静态资源(功能操作、数据列)和动态资源(数据),也分别称为对象资源数据资源,后者是我们在系统设计与实现中的叫法。

系统的目标就是对应用系统的所有对象资源和数据资源进行权限控制,比如应用系统的功能菜单、各个界面的按钮、数据显示的列以及各种行级数据进行权限的操控。

三.相关对象及其关系

大概理清了一下权限系统的相关概念,如下所示:

1.       权限

系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子

系统管理

用户管理

查看用户

新增用户

修改用户

删除用户

对于上面的每个权限,又存在两种情况,一个是只是可访问,另一种是可授权,例如对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

2.       用户

应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

3.       角色

为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

4.       组

为了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有自己的角色信息,例如普通群、高级群等。

针对上面提出的四种类型的对象,让我们通过图来看看他们之间的关系。

有上图中可以看出,这四者的关系很复杂,而实际的情况比这个图还要复杂,权限、角色、组都具有上下级关系,权限管理是应用系统中比较棘手的问题,要设计一个通用的权限管理系统,工作量也着实不小。

当然对于有些项目,权限问题并不是那么复杂。有的只需要牵涉到权限和用户两种类型的对象,只需要给用户分配权限即可。

在另一些情况中,引入了角色对象,例如基于角色的权限系统, 只需要给角色分配权限,用户都隶属于角色,不需要单独为用户分配角色信息。

在下一篇中,我们将讲述权限管理的数据库设计等内容。
欢迎各位拍砖或给出宝贵意见。

理清了对象关系之后,让我们接着来进行数据库的设计。在数据库建模时,对于N对N的 关系,一般需要加入一个关联表来表示关联的两者的关系。初步估计一下,本系统至少需要十张表,分别为:权限表、用户表、角色表、组表、用户权限关联表、用 户角色关联表、角色权限关联表、组权限关联表、组角色关联表、用户属组关联表。当然还可能引出一些相关的表。下面让我们在PowerDesigner中画出各表吧。

       各表及其关系如下:


1.       用户表

用户表(TUser)

字段名称

字段

类型

备注

记录标识 tu_id bigint pk, not null
所属组织 to_id bigint fk, not null
登录帐号 login_name varchar(64) not null
用户密码 password varchar(64) not null
用户姓名 vsername varchar(64) not null
手机号 mobile varchar(20)
电子邮箱 email varchar(64)
创建时间 gen_time datetime not null
登录时间 login_time datetime
上次登录时间 last_login_time datetime
登录次数 count bigint not null

2.       角色表

角色表(TRole)

字段名称

字段

类型

备注

角色ID tr_id bigint pk, not null
父级角色ID parent_tr_id bigint not null
角色名称 role_name varchar(64) not null
创建时间 gen_time datetime not null
角色描述 description varchar(200)

3.       权限表

权限表(TRight)

字段名称

字段

类型

备注

权限ID tr_id bigint pk, not null
父权限 parent_tr_id bigint not null
权限名称 right_name varchar(64) not null
权限描述 description varchar(200)

4.       组表

组表(TGroup)

字段名称

字段

类型

备注

组ID tg_id bigint pk, not null
组名称 group_name varchar(64) not null
父组 parent_tg_id bigint not null
创建时间 gen_time datetime not null
组描述 description varchar(200)

5.       角色权限表

角色权限表(TRoleRightRelation)

字段名称

字段

类型

备注

记录标识 trr_id bigint pk, not null
角色 Role_id bigint fk, not null
权限 right_id bigint fk, not null
权限类型 right_type int not null(0:可访问,1:可授权)

6.       组权限表

组权限表(TGroupRightRelation)

字段名称

字段

类型

备注

记录标识 tgr_id bigint pk, not null
tg_id bigint fk, not null
权限 tr_id bigint fk, not null
权限类型 right_type int not null(0:可访问,1:可授权)

7.       组角色表

组角色表(TGroupRoleRelation)

字段名称

字段

类型

备注

记录标识 tgr_id bigint pk, not null
tg_id bigint fk, not null
角色 tr_id bigint pk, not null

8.       用户权限表

用户权限表(TUserRightRelation)

字段名称

字段

类型

备注

记录标识 tur_id bigint pk, not null
用户 tu_id bigint fk, not null
权限 tr_id bigint fk, not null
权限类型 right_type int not null(0:可访问,1:可授权)

9.       用户角色表

用户角色表(TUserRoleRelation)

字段名称

字段

类型

备注

记录标识 tur_id bigint pk, not null
用户 tu_id bigint fk, not null
角色 tr_id bigint fk, not null

10.   用户组表

用户组表(TUserGroupRelation)

字段名称

字段

类型

备注

记录标识 tug_id bigint pk, not null
用户 tu_id bigint fk, not null
tg_id bigint fk, not null

11.   组织表

组织表(TOrganization)

字段名称

字段

类型

备注

组织id to_id bigint pk, not null
父组 parent_to_id bigint not null
组织名称 org_name varchar(64) not null
创建时间 gen_time datetime not null
组织描述 description varchar(200)

12.   操作日志表

操作日志表(TLog)

字段名称

字段

类型

备注

日志ID log_id bigint pk, not null
操作类型 op_type int not null
操作内容 content varchar(200) not null
操作人 tu_id bigint fk, not null
操作时间 gen_time datetime not null

在前两篇文章中,不少朋友对我的设计提出了异议,认为过于复杂,当然在实际的各种系统的权限管 理模块中,并不像这里设计得那么复杂,我以前所做的系统中, 由只有用户和权限的,有只有用户、权限和角色的,还有一个系统用到了用户、权限、角色、组概念,这个系统是我在思考以前所做系统的权限管理部分中找到的一 些共性而想到的一个设计方案,当然还会有不少设计不到位的地方,在设计开发过程中会慢慢改进,这个系统权当学习只用,各位朋友的好的建议我都会考虑到设计 中,感谢各位朋友的支持。
今天抽时间整了一份概念设计出来,还有一些地方尚未考虑清楚,贴出1.0版,希望各位朋友提出宝贵建议。
大家也可以点击此处《通用权限管理概要设计说明书》自行下载,这是1.0版本,有些地方可能还会进行部分修改,有兴趣的朋友请关注我的blog。

1.      引言

1.1 编写目的

本文档对通用权限管理系统的总体设计、接口设计、界面总体设计、数据结构设计、系统出错处理设计以及系统安全数据进行了说明。

1.2 背景

a、 软件系统的名称:通用权限管理系统;

b、 任务提出者、开发者:谢星星;

c、 在J2EE的web系统中需要使用权限管理的系统。

1.3 术语

本系统:通用权限管理系统;

SSH:英文全称是Secure Shell。

1.4 预期读者与阅读建议

预期读者 阅读重点
开发人员 总体设计、接口设计、数据结构设计、界面总体设计、系统出错处理设计
设计人员 总体设计、接口设计、数据结构设计、系统安全设计

1.5 参考资料

《通用权限管理系统需求规格说明书》

《通用权限管理系统数据库设计说明书》

2.      总体设计

2.1 设计目标

权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

本系统的设计目标是对应用系统的所有资源进行权限控制,比如应用系统的功能菜单、各个界面的按钮控件等进行权限的操控。

2.2 运行环境

操作系统:Windows系统操作系统和Linux系列操作系统。

2.3 网络结构

通用权限管理系统可采用Java Swing实现,可以在桌面应用和Web应用系统中进行调用。如果需要要适应所有开发语言,可以将其API发布到WEB Service上。暂时用Java Swing实现。

2.4 总体设计思路和处理流程

在说明总体设计思路前,我们先说明本系统的相关概念:

1. 权限资源

系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子

系统管理

用户管理

查看用户

新增用户

修改用户

删除用户

对于上面的每个权限,又存在两种情况,一个是只是可访问,另一种是可授权,例如对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

2. 用户

应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

3. 角色

为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

4. 组

为 了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、 权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有 自己的角色信息,例如普通群、高级群等。

针对如上提出的四种对象,我们可以整理得出它们之间的关系图,如下所示:

总体设计思路是将系统分为组权限管理、角色权限管理、用户权限管理、组织管理和操作日志管理五部分。

其中组权限管理包括包含用户、所属角色、组权限资源和组总权限资源四部分,某个组的权限信息可用公式表示:组权限 = 所属角色的权限合集 + 组自身的权限。

角色权限管理包括包含用户、包含组和角色权限三部分,某个角色的权限的计算公式为:角色权限 = 角色自身权限。

用户权限管理包括所属角色、所属组、用户权限、用户总权限资源和组织管理五部分。某个用户总的权限信息存在如下计算公式:用户权限 = 所属角色权限合集 + 所属组权限合集 + 用户自身权限。

组织管理即对用户所属的组织进行管理,组织以树形结构展示,组织管理具有组织的增、删、改、查功能。

操作日志管理用于管理本系统的操作日志。

注意:因为组和角色都具有上下级关系,所以下级的组或角色的权限只能在自己的直属上级的权限中选择,下级的组或者角色的总的权限都不能大于直属上级的总权限。

2.5 模块结构设计

本系统的具有的功能模块结构如下图所示:

2.6 尚未解决的问题

无。

3.      接口设计(暂略)

3.1 用户接口(暂略)

3.2 外部接口(暂略)

3.3 内部接口(暂略)

4.      界面总体设计

本节将阐述用户界面的实现,在此之前对页面元素做如下约定:

序号

页面元素

约定

1

按钮

未选中时:[按钮名称]

选中时:[按钮名称]

2

单选框

○ 选项

3

复选框

□ 选项

4

下拉框

 [选项,…,] ▽

5

文本框

 |________|

6

TextArea

 |…………|

7

页签

未选中时:选项名称

 选中时:选项名称

8

未选中链接

链接文字

9

选中链接

链接文字

10

说明信息

说明信息

 

4.1 组权限管理

4.1.1包含用户

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该组所包含的用户。

4.1.2所属角色

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

 

当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该组所属的角色。

4.1.3组权限

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]


[保存] [取消]

4.1.4总权限

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改组的权限信息,点击“保存”按钮保存修改信息。

4.1.5组管理

在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

4.2 角色权限管理

4.2.1包含用户

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的用户。

4.2.2包含组

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

组ID   组名称     组描述

1      xxx1       —

2       xxx2        —

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的组。

4.2.3角色权限

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改角色的权限信息,点击“保存”按钮保存修改信息。

4.2.4管理角色

在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

4.3 用户权限管理

4.3.1所属角色

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的角色。

4.3.2所属组

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

组ID   组名称     组描述

1       组1         —

2       组2         —

当用户选择“修改”按钮时,弹出组的树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的组。

4.3.3用户权限

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

4.3.4总权限

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

4.3.5用户管理

当选择了某用户时,点击右键,弹出菜单列表:修改、删除、取消,点击修改和删除按钮可以实现用户的删除和修改功能。

选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加用户按钮可以实现用户的添加功能。

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

4.3.6组织管理

选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加子组织、删除组织、修改组织按钮可以实现组织的添加、删除和修改功能。

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

4.4 操作日志管理

4.4.1查询操作日志

操作名称:|________|  操作人:|________|

操作时间从 |________| 到 |________| [查询] [重置] [删除]

编号    操作名称    操作内容    操作人    操作时间

1        xx1         —        Amigo    2007-10-8

2        xx2         —        xxyy     2007-10-8

输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。

4.4.2删除操作日志

操作名称:|________| 操作人:|________|

操作时间从 |________| 到 |________| [查询] [重置] [删除]

编号    操作名称    操作内容    操作人    操作时间

1        xx1       —           Amigo      2007-10-8

2        xx2       —           xxyy       2007-10-8

输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。而后点击“删除”按钮,可删除符合查询条件的操作日志。

5.      数据结构设计

数据库设计的模型请参见《通用权限管理系统_数据库模型.pdm》。表的说明请参见《通用权限管理系统数据库设计说明书》。

5.1 设计原则

5.1.1命名的规范

数据库中表、主键、外键、索引的命名都以统一的规则,采用大小写敏感的形式,各种对象命名长度不要超过30个字符,这样便于应用系统适应不同的数据库平台。

5.1.2数据的一致性和完整性

为了保证数据库的一致性和完整性,往往通过表间关联的方式来尽可能的降低数据的冗余。表间关联是一种强制性措施,建立后,对父表(Parent Table)和子表(Child Table)的插入、更新、删除操作均要占用系统的开销。如果数据冗余低,数据的完整性容易得到保证,但增加了表间连接查询的操作,为了提高系统的响应时间,合理的数据冗余也是必要的。使用规则(Rule)和约束(Check)来防止系统操作人员误输入造成数据的错误是设计人员的另一种常用手段,但是,不必要的规则和约束也会占用系统的不必要开销,需要注意的是,约束对数据的有效性验证要比规则快。所有这些,需要在设计阶段应根据系统操作的类型、频度加以均衡考虑。

5.2 数据库环境说明

数据库:MySQL5.0

设计库建模工具:PowerDesigner12.0

5.3 数据库命名规则

表名以T开头,外键以FK开头,索引以INDEX开头。

5.4 逻辑结构

pdm文件的名称为:《通用权限管理系统_数据库模型》。

5.5 物理存储

通过数据库建模工具PowerDesigner12可以将pdm导出为文本文件,将数据库脚本放入文本文件中保存。

5.6 数据备份和恢复

数据库需定期备份(每天备份一次),备份文件格式为backup_yyyyMMdd,数据库被破坏时,利用最新的备份文件进行恢复。

6.      系统出错处理设计

6.1 出错信息

错误分类 子项及其编码 错误名称 错误代码 备注
数据库错误 连接 连接超时 100001001
连接断开 100001002
数据库本身错误代码 数据库本身错误代码 100002+数据库错误代码
TCP连接错误 连接 连接超时 101001001
连接断开 101001002
其它TCP连接错误(socket自身错误代码) 101002+ socket错误代码
配置信息错误 未配置输入参数 102001
未配置输出参数 102002
组管理部分自定义错误 103001——103999
角色管理部分自定义错误 104001——104999
用户管理部分自定义错误 105001——105999
操作日志管理 106001——106999

6.2 补救措施

为了当某些故障发生时,对系统进行及时的补救,提供如下补救措施:

a.后备技术   定期对数据库信息进行备份(每天一次),当数据库因某种原因被破坏时,以最新的数据库脚本进行恢复;。

7.      系统安全设计

7.1 数据传输安全性设计

SSH可以通过将联机的封包加密的技术进行资料的传递; 使用SSH可以把传输的所有数据进行加密,即使有人截获到数据也无法得到有用的信息。同时数据经过压缩,大大地加快了传输的速度。通过SSH的使用,可以确保资料传输比较安全并且传输效率较高。

7.2 应用系统安全性设计

操作人的操作信息需要提供操作记录。对系统的异常信息需进行记录,已备以后查看。只有授权用户才能登录系统,对于某个操作,需要具有相应权限才能进行操作。

7.3 数据存储安全性设计

对于用户的密码等敏感信息采用MD5进行加密。

在前两篇文章中,不少朋友对我的设计提出了异议,认为过于复杂,当然在实际的各种系统的权限 管理模块中,并不像这里设计得那么复杂,我以前所做的系统中, 由只有用户和权限的,有只有用户、权限和角色的,还有一个系统用到了用户、权限、角色、组概念,这个系统是我在思考以前所做系统的权限管理部分中找到的一 些共性而想到的一个设计方案,当然还会有不少设计不到位的地方,在设计开发过程中会慢慢改进,这个系统权当学习只用,各位朋友的好的建议我都会考虑到设计 中,感谢各位朋友的支持。
今天抽时间整了一份概念设计出来,还有一些地方尚未考虑清楚,贴出1.0版,希望各位朋友提出宝贵建议。
大家也可以点击此处《通用权限管理概要设计说明书》自行下载,这是1.0版本,有些地方可能还会进行部分修改,有兴趣的朋友请关注我的blog。

1.      引言

1.1 编写目的

本文档对通用权限管理系统的总体设计、接口设计、界面总体设计、数据结构设计、系统出错处理设计以及系统安全数据进行了说明。

1.2 背景

a、 软件系统的名称:通用权限管理系统;

b、 任务提出者、开发者:谢星星;

c、 在J2EE的web系统中需要使用权限管理的系统。

1.3 术语

本系统:通用权限管理系统;

SSH:英文全称是Secure Shell。

1.4 预期读者与阅读建议

预期读者 阅读重点
开发人员 总体设计、接口设计、数据结构设计、界面总体设计、系统出错处理设计
设计人员 总体设计、接口设计、数据结构设计、系统安全设计

1.5 参考资料

《通用权限管理系统需求规格说明书》

《通用权限管理系统数据库设计说明书》

2.      总体设计

2.1 设计目标

权限系统一直以来是我们应用系统不可缺少的一个部分,若每个应用系统都重新对系统的权限进行设计,以满足不同系统用户的需求,将会浪费我们不少宝贵时间,所以花时间来设计一个相对通用的权限系统是很有意义的。

本系统的设计目标是对应用系统的所有资源进行权限控制,比如应用系统的功能菜单、各个界面的按钮控件等进行权限的操控。

2.2 运行环境

操作系统:Windows系统操作系统和Linux系列操作系统。

2.3 网络结构

通用权限管理系统可采用Java Swing实现,可以在桌面应用和Web应用系统中进行调用。如果需要要适应所有开发语言,可以将其API发布到WEB Service上。暂时用Java Swing实现。

2.4 总体设计思路和处理流程

在说明总体设计思路前,我们先说明本系统的相关概念:

1. 权限资源

系统的所有权限信息。权限具有上下级关系,是一个树状的结构。下面来看一个例子

系统管理

用户管理

查看用户

新增用户

修改用户

删除用户

对于上面的每个权限,又存在两种情况,一个是只是可访问,另一种是可授权,例如对于“查看用户”这个权限,如果用户只被授予“可访问”,那么他就不能将他所具有的这个权限分配给其他人。

2. 用户

应用系统的具体操作者,用户可以自己拥有权限信息,可以归属于0~n个角色,可属于0~n个组。他的权限集是自身具有的权限、所属的各角色具有的权限、所属的各组具有的权限的合集。它与权限、角色、组之间的关系都是n对n的关系。

3. 角色

为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,例如系统管理员、管理员、用户、访客等角色。角色具有上下级关系,可以形成树状视图,父级角色的权限是自身及它的所有子角色的权限的综合。父级角色的用户、父级角色的组同理可推。

4. 组

为 了更好地管理用户,对用户进行分组归类,简称为用户分组。组也具有上下级关系,可以形成树状视图。在实际情况中,我们知道,组也可以具有自己的角色信息、 权限信息。这让我想到我们的QQ用户群,一个群可以有多个用户,一个用户也可以加入多个群。每个群具有自己的权限信息。例如查看群共享。QQ群也可以具有 自己的角色信息,例如普通群、高级群等。

针对如上提出的四种对象,我们可以整理得出它们之间的关系图,如下所示:

总体设计思路是将系统分为组权限管理、角色权限管理、用户权限管理、组织管理和操作日志管理五部分。

其中组权限管理包括包含用户、所属角色、组权限资源和组总权限资源四部分,某个组的权限信息可用公式表示:组权限 = 所属角色的权限合集 + 组自身的权限。

角色权限管理包括包含用户、包含组和角色权限三部分,某个角色的权限的计算公式为:角色权限 = 角色自身权限。

用户权限管理包括所属角色、所属组、用户权限、用户总权限资源和组织管理五部分。某个用户总的权限信息存在如下计算公式:用户权限 = 所属角色权限合集 + 所属组权限合集 + 用户自身权限。

组织管理即对用户所属的组织进行管理,组织以树形结构展示,组织管理具有组织的增、删、改、查功能。

操作日志管理用于管理本系统的操作日志。

注意:因为组和角色都具有上下级关系,所以下级的组或角色的权限只能在自己的直属上级的权限中选择,下级的组或者角色的总的权限都不能大于直属上级的总权限。

2.5 模块结构设计

本系统的具有的功能模块结构如下图所示:

2.6 尚未解决的问题

无。

3.      接口设计(暂略)

3.1 用户接口(暂略)

3.2 外部接口(暂略)

3.3 内部接口(暂略)

4.      界面总体设计

本节将阐述用户界面的实现,在此之前对页面元素做如下约定:

序号

页面元素

约定

1

按钮

未选中时:[按钮名称]

选中时:[按钮名称]

2

单选框

○ 选项

3

复选框

□ 选项

4

下拉框

 [选项,…,] ▽

5

文本框

 |________|

6

TextArea

 |…………|

7

页签

未选中时:选项名称

 选中时:选项名称

8

未选中链接

链接文字

9

选中链接

链接文字

10

说明信息

说明信息

 

4.1 组权限管理

4.1.1包含用户

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该组所包含的用户。

4.1.2所属角色

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

 

当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该组所属的角色。

4.1.3组权限

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]


[保存] [取消]

4.1.4总权限

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改组的权限信息,点击“保存”按钮保存修改信息。

4.1.5组管理

在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

组信息

组1

组11

组12

组…

组2

组21

组22

组…

 

所选择组:组1

[包含用户] [所属角色] [组权限] [总权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

4.2 角色权限管理

4.2.1包含用户

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的用户。

4.2.2包含组

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

组ID   组名称     组描述

1      xxx1       —

2       xxx2        —

……

当用户选择“修改”按钮时,弹出用户列表,操作人可以通过勾选或取消勾选来修改该角色所包含的组。

4.2.3角色权限

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改角色的权限信息,点击“保存”按钮保存修改信息。

4.2.4管理角色

在下图中,选中组1的时候,右键点击可弹出组的操作列表,包括添加、删除和修改按钮,从而完成在该组下添加子组,删除该组以及修改该组的功能。

角色信息

角色1

角色11

角色12

角色…

角色2

角色21

角色22

角色…

 

所选择角色:角色1

[包含用户] [包含组] [角色权限]

[修改]

用户名   姓名     手机号   最近登录时间 登录次数

阿蜜果 谢星星 13666666666 2007-10-8    66

sterning xxx    13555555555 2007-10-8    10

……

4.3 用户权限管理

4.3.1所属角色

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

当用户选择“修改”按钮时,弹出角色树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的角色。

4.3.2所属组

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

组ID   组名称     组描述

1       组1         —

2       组2         —

当用户选择“修改”按钮时,弹出组的树形结构,操作人可以通过勾选或取消勾选来修改该用户所属的组。

4.3.3用户权限

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

4.3.4总权限

用户信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]


[保存] [取消]

通过对已具有的权限取消勾选,或为某权限添加勾选,来修改用户的权限信息,点击“保存”按钮保存修改信息。

4.3.5用户管理

当选择了某用户时,点击右键,弹出菜单列表:修改、删除、取消,点击修改和删除按钮可以实现用户的删除和修改功能。

选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加用户按钮可以实现用户的添加功能。

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

4.3.6组织管理

选择某个组织,例如下表中的“广州分公司”,弹出菜单列表:添加子组织、删除组织、修改组织、添加用户、取消,点击添加子组织、删除组织、修改组织按钮可以实现组织的添加、删除和修改功能。

用户权限信息

xx公司

广州分公司

阿蜜果

肖xx

yy…

北京分公司

zz1

zz2

zz3…

 

所选择用户:阿蜜果

[所属角色] [所属组] [用户权限] [总权限]

[修改]

角色ID   角色名称   角色描述

1          访客       —

2         初级用户    —

4.4 操作日志管理

4.4.1查询操作日志

操作名称:|________|  操作人:|________|

操作时间从 |________| 到 |________| [查询] [重置] [删除]

编号    操作名称    操作内容    操作人    操作时间

1        xx1         —        Amigo    2007-10-8

2        xx2         —        xxyy     2007-10-8

输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。

4.4.2删除操作日志

操作名称:|________| 操作人:|________|

操作时间从 |________| 到 |________| [查询] [重置] [删除]

编号    操作名称    操作内容    操作人    操作时间

1        xx1       —           Amigo      2007-10-8

2        xx2       —           xxyy       2007-10-8

输入上图表单中的查询信息后,点击“查询”按钮,可查询出符合条件的信息。而后点击“删除”按钮,可删除符合查询条件的操作日志。

5.      数据结构设计

数据库设计的模型请参见《通用权限管理系统_数据库模型.pdm》。表的说明请参见《通用权限管理系统数据库设计说明书》。

5.1 设计原则

5.1.1命名的规范

数据库中表、主键、外键、索引的命名都以统一的规则,采用大小写敏感的形式,各种对象命名长度不要超过30个字符,这样便于应用系统适应不同的数据库平台。

5.1.2数据的一致性和完整性

为了保证数据库的一致性和完整性,往往通过表间关联的方式来尽可能的降低数据的冗余。表间关联是一种强制性措施,建立后,对父表(Parent Table)和子表(Child Table)的插入、更新、删除操作均要占用系统的开销。如果数据冗余低,数据的完整性容易得到保证,但增加了表间连接查询的操作,为了提高系统的响应时间,合理的数据冗余也是必要的。使用规则(Rule)和约束(Check)来防止系统操作人员误输入造成数据的错误是设计人员的另一种常用手段,但是,不必要的规则和约束也会占用系统的不必要开销,需要注意的是,约束对数据的有效性验证要比规则快。所有这些,需要在设计阶段应根据系统操作的类型、频度加以均衡考虑。

5.2 数据库环境说明

数据库:MySQL5.0

设计库建模工具:PowerDesigner12.0

5.3 数据库命名规则

表名以T开头,外键以FK开头,索引以INDEX开头。

5.4 逻辑结构

pdm文件的名称为:《通用权限管理系统_数据库模型》。

5.5 物理存储

通过数据库建模工具PowerDesigner12可以将pdm导出为文本文件,将数据库脚本放入文本文件中保存。

5.6 数据备份和恢复

数据库需定期备份(每天备份一次),备份文件格式为backup_yyyyMMdd,数据库被破坏时,利用最新的备份文件进行恢复。

6.      系统出错处理设计

6.1 出错信息

错误分类 子项及其编码 错误名称 错误代码 备注
数据库错误 连接 连接超时 100001001
连接断开 100001002
数据库本身错误代码 数据库本身错误代码 100002+数据库错误代码
TCP连接错误 连接 连接超时 101001001
连接断开 101001002
其它TCP连接错误(socket自身错误代码) 101002+ socket错误代码
配置信息错误 未配置输入参数 102001
未配置输出参数 102002
组管理部分自定义错误 103001——103999
角色管理部分自定义错误 104001——104999
用户管理部分自定义错误 105001——105999
操作日志管理 106001——106999

6.2 补救措施

为了当某些故障发生时,对系统进行及时的补救,提供如下补救措施:

a.后备技术   定期对数据库信息进行备份(每天一次),当数据库因某种原因被破坏时,以最新的数据库脚本进行恢复;。

7.      系统安全设计

7.1 数据传输安全性设计

SSH可以通过将联机的封包加密的技术进行资料的传递; 使用SSH可以把传输的所有数据进行加密,即使有人截获到数据也无法得到有用的信息。同时数据经过压缩,大大地加快了传输的速度。通过SSH的使用,可以确保资料传输比较安全并且传输效率较高。

7.2 应用系统安全性设计

操作人的操作信息需要提供操作记录。对系统的异常信息需进行记录,已备以后查看。只有授权用户才能登录系统,对于某个操作,需要具有相应权限才能进行操作。

7.3 数据存储安全性设计

对于用户的密码等敏感信息采用MD5进行加密。

如何开发高性能低成本的网站之技术选择 - AIの海雅 - 博客园

mikel阅读(859)

来源: 如何开发高性能低成本的网站之技术选择 – AIの海雅 – 博客园

每个企业都是慢慢发展起来的,在起步阶段成本是一个不得不考虑的重大问题 。直接入正题:

 

前台框架:  ASP.NET MVC + JQuery + Json + Flash , ASP.NET MVC 高性能速度快,JQuery 简洁成熟的Js基础框架 , Json 数据格式体积小 ,传输快。Flash 用于开发复杂的页面交互应用。

 

缓存方案

Memcached , 基于Key-Value的传统Cache储存方式 , 高性能 , 而且它内置LRU(Least Recently Used)机制自动维护缓存数据,从而 提高缓存的性能和负载能力。

MongoDb , 数据库级别的缓存解决方案 , 适合海量的数据缓存 , 支持查询

 

权限模型:

基于ASP.NET MVC 的RBAC , 控制对象粒度到Action , 控制操作粒度 是否能访问。权限基于Cookie/缓存记录认证信息 , 在用户登录时就计算出改用户的所有权限并缓存。

(优点:直接通过AOP做横切面控制,不需要设置权限点 ;缺点:无法控制到同一个Action有增、删、改、查等更细的操作粒度,不同的操作需要制作不同的Action , 表面上要多几个Action , 其实这样做职责更加分离,更加符合OO的观点)

 

多语言解决方案:

服务端, 基于资源文件,完美配合ASP.NET MVC 前段框架 ,进行各项数据验证及提示等

客户端, 同样基于资源文件, 对Page页面采用script 导入序列化的资源文件 ,按名词空间引用 ,如Resources.Book.AreYouSure 的Js变量. 对于flash等可以通过Json 传递。

 

数据通信:

服务端,WCF , WebService

客户端,  HttpRequest 数据类型Json

 

数据访问层:标准接口化,不对数据实现依赖。

Entity Framwork , 适合只使用SQL Server 的解决方案, 开发效率最高

NHibernate , 支持多数据平台 ,开发效率较高 , 性能一般

ADO.NET, 完全靠开发实现,开发效率低 , 性能较高

         性能和效率按正常水平评估

 

解耦办法:

         IOC , 依赖注入 ,

AOP , 横切面拦截 ,权限中的推荐做法

 

负载均衡:

Nginx , Web前端的负载均衡解决方案 , Nginx 开源免费,高性能 .

 

页面提速:

实时性要求不高的页面可以做静态化 ,页面的部分动态内容可以通过SSI处理 ,然后数据更新就主动生成页面。页面静态化,通过XSLT的CMS生成机制可以对生成的页面内容进行压缩。

静态资源文件拆分出去做独立站点,加上服务端的GZIP/Deflate压缩等操作,最好配上二级域名,已加快客户端HTTP下载.更加方便以后做CDN.

 

SSO:

如果有多个站点,统一认证可以降低开发维护等成本.

 

数据库:

      MySQL 成熟,开源.