欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

AngularJs篇:使用AngularJs打造一个简易权限系统的实现代码

程序员文章站 2022-04-25 20:41:42
一、引言 上一篇博文已经向大家介绍了angularjs核心的一些知识点,在这篇博文将介绍如何把angularjs应用到实际项目中。本篇博文将使用angularjs来打造一...

一、引言

上一篇博文已经向大家介绍了angularjs核心的一些知识点,在这篇博文将介绍如何把angularjs应用到实际项目中。本篇博文将使用angularjs来打造一个简易的权限管理系统。下面不多说,直接进入主题。

二、整体架构设计介绍

首先看下整个项目的架构设计图:

AngularJs篇:使用AngularJs打造一个简易权限系统的实现代码

从上图可以看出整个项目的一个整体结构,接下来,我来详细介绍了项目的整体架构:

采用asp.net web api来实现rest 服务。这样的实现方式,已达到后端服务的公用、分别部署和更好地扩展。web层依赖应用服务接口,并且使用castle windsor实现依赖注入。

1、显示层(用户ui)

显示层采用了angularjs来实现的spa页面。所有的页面数据都是异步加载和局部刷新,这样的实现将会有更好的用户体验。

2、应用层(application service)

angularjs通过http服务去请求web api来获得数据,而web api的实现则是调用应用层来请求数据。

3、基础架构层

基础架构层包括仓储的实现和一些公用方法的实现。

仓储层的实现采用ef code first的方式来实现的,并使用ef migration的方式来创建数据库和更新数据库。

lh.common层实现了一些公用的方法,如日志帮助类、表达式树扩展等类的实现。

4、领域层

领域层主要实现了该项目的所有领域模型,其中包括领域模型的实现和仓储接口的定义。

介绍完整体结构外,接下来将分别介绍该项目的后端服务实现和web前端的实现。

三、后端服务实现

后端服务主要采用asp.net web api来实现后端服务,并且采用castle windsor来完成依赖注入。

这里拿权限管理中的用户管理来介绍rest web api服务的实现。

提供用户数据的rest服务的实现:

public class usercontroller : apicontroller
  {
    private readonly iuserservice _userservice;

    public usercontroller(iuserservice userservice)
    {
      _userservice = userservice;
    }

    [httpget]
    [route("api/user/getusers")]
    public outputbase getusers([fromuri]pageinput input)
    {
      return _userservice.getusers(input);
    }

    [httpget]
    [route("api/user/userinfo")]
    public outputbase getuserinfo(int id)
    {
      return _userservice.getuser(id);
    }

    [httppost]
    [route("api/user/adduser")]
    public outputbase createuser([frombody] userdto userdto)
    {
      return _userservice.adduser(userdto);
    }

    [httppost]
    [route("api/user/updateuser")]
    public outputbase updateuser([frombody] userdto userdto)
    {
      return _userservice.updateuser(userdto);
    }

    [httppost]
    [route("api/user/updateroles")]
    public outputbase updateroles([frombody] userdto userdto)
    {
      return _userservice.updateroles(userdto);
    }

    [httppost]
    [route("api/user/deleteuser/{id}")]
    public outputbase deleteuser(int id)
    {
      return _userservice.deleteuser(id);
    }

    [httppost]
    [route("api/user/deleterole/{id}/{roleid}")]
    public outputbase deleterole(int id, int roleid)
    {
      return _userservice.deleterole(id, roleid);
    }
  }

从上面代码实现可以看出,user rest 服务依赖与iuserservice接口,并且也没有像传统的方式将所有的业务逻辑放在web api实现中,而是将具体的一些业务实现封装到对应的应用层中,rest api只负责调用对应的应用层中的服务。这样设计好处有:

1.rest 服务部依赖与应用层接口,使得职责分离,将应用层服务的实例化交给单独的依赖注入容器去完成,而rest服务只负责调用对应应用服务的方法来获取数据。采用依赖接口而不依赖与具体类的实现,使得类与类之间低耦合。

2.rest服务内不包括具体的业务逻辑实现。这样的设计可以使得服务更好地分离,如果你后期想用wcf来实现rest服务的,这样就不需要重复在wcf的rest服务类中重复写一篇web api中的逻辑了,这时候完全可以调用应用服务的接口方法来实现wcf rest服务。所以将业务逻辑实现抽到应用服务层去实现,这样的设计将使得rest 服务职责更加单一,rest服务实现更容易扩展。

用户应用服务的实现:

public class userservice : baseservice, iuserservice
  {
    private readonly iuserrepository _userrepository;
    private readonly iuserrolerepository _userrolerepository;
    public userservice(iuserrepository userrepository, iuserrolerepository userrolerepository)
    {
      _userrepository = userrepository;
      _userrolerepository = userrolerepository;
    }

    public getresults<userdto> getusers(pageinput input)
    {
      var result = getdefault<getresults<userdto>>();
      var filterexp = buildexpression(input);
      var query = _userrepository.find(filterexp, user => user.id, sortorder.descending, input.current, input.size);
      result.total = _userrepository.find(filterexp).count();
      result.data = query.select(user => new userdto()
      {
        id = user.id,
        createtime = user.creationtime,
        email = user.email,
        state = user.state,
        name = user.name,
        realname = user.realname,
        password = "*******",
        roles = user.userroles.take(4).select(z => new baseentitydto()
        {
          id = z.role.id,
          name = z.role.rolename
        }).tolist(),

        totalrole = user.userroles.count()
      }).tolist();

      return result;
    }

    public updateresult updateuser(userdto user)
    {
      var result = getdefault<updateresult>();
      var existuser = _userrepository.findsingle(u => u.id == user.id);
      if (existuser == null)
      {
        result.message = "user_not_exist";
        result.statecode = 0x00303;
        return result;
      }
      if (ishassamename(existuser.name, existuser.id))
      {
        result.message = "user_name_has_exist";
        result.statecode = 0x00302;
        return result;
      }

      existuser.realname = user.realname;
      existuser.name = user.name;
      existuser.state = user.state;
      existuser.email = user.email;
      _userrepository.update(existuser);
      _userrepository.commit();
      result.issaved = true;
      return result;
    }

    public createresult<int> adduser(userdto userdto)
    {
      var result = getdefault<createresult<int>>();
      if (ishassamename(userdto.name, userdto.id))
      {
        result.message = "user_name_has_exist";
        result.statecode = 0x00302;
        return result;
      }
      var user = new user()
      {
        creationtime = datetime.now,
        password = "",
        email = userdto.email,
        state = userdto.state,
        realname = userdto.realname,
        name = userdto.name
      };

      _userrepository.add(user);
      _userrepository.commit();
      result.id = user.id;
      result.iscreated = true;
      return result;
    }

    public deleteresult deleteuser(int userid)
    {
      var result = getdefault<deleteresult>();
      var user = _userrepository.findsingle(x => x.id == userid);
      if (user != null)
      {
        _userrepository.delete(user);
        _userrepository.commit();
      }
      result.isdeleted = true;
      return result;
    }

    public updateresult updatepwd(userdto user)
    {
      var result = getdefault<updateresult>();
      var userentity =_userrepository.findsingle(x => x.id == user.id);
      if (userentity == null)
      {
        result.message = string.format("当前编辑的用户“{0}”已经不存在", user.name);
        return result;
      }
      userentity.password = user.password;
      _userrepository.commit();
      result.issaved = true;
      return result;
    }

    public getresult<userdto> getuser(int userid)
    {
      var result = getdefault<getresult<userdto>>();
      var model = _userrepository.findsingle(x => x.id == userid);
      if (model == null)
      {
        result.message = "use_not_exist";
        result.statecode = 0x00402;
        return result;
      }
      result.data = new userdto()
      {
        createtime = model.creationtime,
        email = model.email,
        id = model.id,
        realname = model.realname,
        state = model.state,
        name = model.name,
        password = "*******"
      };
      return result;
    }

    public updateresult updateroles(userdto user)
    {
      var result = getdefault<updateresult>();
      var model = _userrepository.findsingle(x => x.id == user.id);
      if (model == null)
      {
        result.message = "use_not_exist";
        result.statecode = 0x00402;
        return result;
      }

      var list = model.userroles.tolist();
      if (user.roles != null)
      {
        foreach (var item in user.roles)
        {
          if (!list.exists(x => x.role.id == item.id))
          {
            _userrolerepository.add(new userrole { roleid = item.id, userid = model.id });
          }
        }

        foreach (var item in list)
        {
          if (!user.roles.exists(x => x.id == item.id))
          {
            _userrolerepository.delete(item);
          }
        }

        _userrolerepository.commit();
        _userrepository.commit();
      }

      result.issaved = true;
      return result;
    }

    public deleteresult deleterole(int userid, int roleid)
    {
      var result = getdefault<deleteresult>();
      var model = _userrolerepository.findsingle(x => x.userid == userid && x.roleid == roleid);
      if (model != null)
      {
        _userrolerepository.delete(model);
        _userrolerepository.commit();
      }

      result.isdeleted = true;
      return result;
    }

    public bool exist(string username, string password)
    {
      return _userrepository.findsingle(u => u.name == username && u.password == password) != null;
    }

    private bool ishassamename(string name, int userid)
    {
      return !string.isnullorwhitespace(name) && _userrepository.find(u=>u.name ==name && u.id != userid).any();
    }

    private expression<func<user, bool>> buildexpression(pageinput pageinput)
    {
      expression<func<user, bool>> filterexp = user => true;
      if (string.isnullorwhitespace(pageinput.name))
        return filterexp;
      
      switch (pageinput.type)
      {
        case 0:
          filterexp = user => user.name.contains(pageinput.name) || user.email.contains(pageinput.name);
          break;
        case 1:
          filterexp = user => user.name.contains(pageinput.name);
          break;
        case 2:
          filterexp = user => user.email.contains(pageinput.name);
          break;
      }

      return filterexp;
    }
  }

这里应用服务层其实还可以进一步的优化,实现代码层级的读写分离,定义ireadonlyservice接口和iwriteservie接口,并且把写操作可以采用泛型方法的方式抽象到baseservice中去实现。这样一些增删改操作实现公用,之所以可以将这里操作实现公用,是因为这些操作都是非常类似的,无非是操作的实体不一样罢了。

仓储层的实现:

用户应用服务也没有直接依赖与具体的仓储类,同样也是依赖其接口。对应的用户仓储类的实现如下:

public class baserepository<tentity> : irepository<tentity>
    where tentity :class , ientity
  {
    private readonly threadlocal<usermanagerdbcontext> _localctx = new threadlocal<usermanagerdbcontext>(() => new usermanagerdbcontext());

    public usermanagerdbcontext dbcontext { get { return _localctx.value; } }

    public tentity findsingle(expression<func<tentity, bool>> exp = null)
    {
      return dbcontext.set<tentity>().asnotracking().firstordefault(exp);
    }

    public iqueryable<tentity> find(expression<func<tentity, bool>> exp = null)
    {
      return filter(exp);
    }

    public iqueryable<tentity> find(expression<func<tentity, bool>> expression, expression<func<tentity, dynamic>> sortpredicate, sortorder sortorder, int pagenumber, int pagesize)
    {
      if (pagenumber <= 0)
        throw new argumentoutofrangeexception("pagenumber", pagenumber, "pagenumber must great than or equal to 1.");
      if (pagesize <= 0)
        throw new argumentoutofrangeexception("pagesize", pagesize, "pagesize must great than or equal to 1.");

      var query = dbcontext.set<tentity>().where(expression);
      var skip = (pagenumber - 1) * pagesize;
      var take = pagesize;
      if (sortpredicate == null)
        throw new invalidoperationexception("based on the paging query must specify sorting fields and sort order.");

      switch (sortorder)
      {
        case sortorder.ascending:
          var pagedascending = query.sortby(sortpredicate).skip(skip).take(take);

          return pagedascending;
        case sortorder.descending:
          var pageddescending = query.sortbydescending(sortpredicate).skip(skip).take(take);
          return pageddescending;
      }

      throw new invalidoperationexception("based on the paging query must specify sorting fields and sort order.");
    }

    public int getcount(expression<func<tentity, bool>> exp = null)
    {
      return filter(exp).count();
    }

    public void add(tentity entity)
    {
      dbcontext.set<tentity>().add(entity);
    }

    public void update(tentity entity)
    {
      dbcontext.entry(entity).state = entitystate.modified;
    }

    public void delete(tentity entity)
    {
      dbcontext.entry(entity).state = entitystate.deleted;
      dbcontext.set<tentity>().remove(entity);
    }

    public void delete(icollection<tentity> entitycollection)
    {
      if(entitycollection.count ==0)
        return;

      dbcontext.set<tentity>().attach(entitycollection.first());
      dbcontext.set<tentity>().removerange(entitycollection);
    }

    private iqueryable<tentity> filter(expression<func<tentity, bool>> exp)
    {
      var dbset = dbcontext.set<tentity>().asqueryable();
      if (exp != null)
        dbset = dbset.where(exp);
      return dbset;
    }

    public void commit()
    {
      dbcontext.savechanges();
    }
  }

public class userrepository :baserepository<user>, iuserrepository
  {
     
  }

 四、angularjs前端实现

web前端的实现就是采用angularjs来实现,并且采用模块化开发模式。具体web前端的代码结构如下图所示:

AngularJs篇:使用AngularJs打造一个简易权限系统的实现代码

app/images // 存放web前端使用的图片资源

app/styles // 存放样式文件

app/scripts // 整个web前端用到的脚本文件
        / controllers // angularjs控制器模块存放目录
        / directives // angularjs指令模块存放目录
       /  filters // 过滤器模块存放目录
       /  services // 服务模块存放目录
      / app.js // web前端程序配置模块(路由配置)
app/modules // 项目依赖库,angular、bootstrap、jquery库

app/views // angularjs视图模板存放目录

使用angularjs开发的web应用程序的代码之间的调用层次和后端基本一致,也是视图页面——》控制器模块——》服务模块——》web api服务。

并且web前端css和js资源的加载采用了bundle的方式来减少请求资源的次数,从而加快页面加载时间。具体bundle类的配置:

public class bundleconfig
  {
    // for more information on bundling, visit http://go.microsoft.com/fwlink/?linkid=301862
    public static void registerbundles(bundlecollection bundles)
    {
      //类库依赖文件
      bundles.add(new scriptbundle("~/js/base/lib").include(
          "~/app/modules/jquery-1.11.2.min.js",
          "~/app/modules/angular/angular.min.js",
          "~/app/modules/angular/angular-route.min.js",
          "~/app/modules/bootstrap/js/ui-bootstrap-tpls-0.13.0.min.js",
          "~/app/modules/bootstrap-notify/bootstrap-notify.min.js"
          ));
      //angularjs 项目文件
      bundles.add(new scriptbundle("~/js/angularjs/app").include(
          "~/app/scripts/services/*.js",
          "~/app/scripts/controllers/*.js",
          "~/app/scripts/directives/*.js",
          "~/app/scripts/filters/*.js",
          "~/app/scripts/app.js"));
      //样式
      bundles.add(new stylebundle("~/js/base/style").include(
          "~/app/modules/bootstrap/css/bootstrap.min.css",
          "~/app/styles/dashboard.css",
          "~/app/styles/console.css"
          ));
    }
  }

首页 index.cshtml

<!doctype html>
<html ng-app="lh">
<head>
  <meta name="viewport" content="width=device-width" />
  <title>简易权限管理系统demo</title>
  @styles.render("~/js/base/style")
  @scripts.render("~/js/base/lib")
</head>
<body ng-controller="navigation">
  <nav class="navbar navbar-inverse navbar-fixed-top">
    <div class="container-fluid">
      <div class="navbar-header">
        <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar" aria-expanded="false" aria-controls="navbar">
          <span class="sr-only">toggle navigation</span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
          <span class="icon-bar"></span>
        </button>
        <a class="navbar-brand" href="/">简易权限管理系统demo</a>
      </div>
      <div class="navbar-collapse collapse">
        <ul class="nav navbar-nav navbar-left">
          <li class="{{item.isactive?'active':''}}" ng-repeat="item in ls">
            <a href="#{{item.urls[0].link}}">{{item.name}}</a>
          </li>
        </ul>
        <div class="navbar-form navbar-right">
          <a href="@url.action("unlogin", "home", null)" class="btn btn-danger">
            {{lang.exit}}
          </a>
        </div>
      </div>
    </div>
  </nav>
  <div class="container-fluid">
    <div class="row">
      <div class="col-sm-3 col-md-2 sidebar">
        <ul class="nav nav-sidebar">
          <li class="{{item.isactive?'active':''}}" ng-repeat="item in urls"><a href="#{{item.link}}">{{item.title}}</a></li>
        </ul>
      </div>
      <div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
        <div ng-view></div>
      </div>
    </div>
  </div>
  @scripts.render("~/js/angularjs/app")
</body>
</html>

五、运行效果

介绍完前后端的实现之后,接下来让我们看下整个项目的运行效果:

AngularJs篇:使用AngularJs打造一个简易权限系统的实现代码

六、总结

到此,本文的所有内容都介绍完了,尽管本文的angularjs的应用项目还有很多完善的地方,例如没有缓冲的支持、没有实现读写分离,没有对一些api进行压力测试等。但angularjs在实际项目中的应用基本是这样的,大家如果在项目中有需要用到angularjs,正好你们公司的后台又是.net的话,相信本文的分享可以是一个很好的参考。

本文所有源码下载地址:privilegemanagement

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。