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服务的实现:

rush:csharp;"> 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 <a href="https://www.jb51.cc/tag/createuser/" target="_blank" class="keywords">createuser</a>([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服务实现更容易扩展。

用户应用服务的实现:

rush:csharp;"> 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.<a href="https://www.jb51.cc/tag/Now/" target="_blank" class="keywords">Now</a>,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("当前编辑的<a href="https://www.jb51.cc/tag/yonghu/" target="_blank" class="keywords">用户</a>“{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中去实现。这样一些增删改操作实现公用,之所以可以将这里操作实现公用,是因为这些操作都是非常类似的,无非是操作的实体不一样罢了。

仓储层的实现:

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

rush:csharp;"> public class BaseRepository : IRepository where TEntity :class,IEntity { private readonly ThreadLocal _localCtx = new ThreadLocal(() => new UserManagerDBContext());
public UserManagerDBContext DbContext { get { return _localCtx.Value; } }

public TEntity FindSingle(Expression<Func<TEntity,bool>> exp = null)
{
  return DbContext.Set<TEntity>().AsNoTracking().F<a href="https://www.jb51.cc/tag/irs/" target="_blank" class="keywords">irs</a>tOrDefault(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<a href="https://www.jb51.cc/tag/so/" target="_blank" class="keywords">.so</a>rtBy(sortPredicate).Skip(skip).Take(take);

      return pagedAscending;
    case SortOrder.Descending:
      var pagedDescending = query<a href="https://www.jb51.cc/tag/so/" target="_blank" class="keywords">.so</a>rtByDescending(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.F<a href="https://www.jb51.cc/tag/irs/" target="_blank" class="keywords">irs</a>t());
  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,IUserRepository
{

}

四、AngularJS前端实现

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

rush:plain;"> 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类的配置:

rush:js;"> 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

rush:js;"> 简易权限管理系统Demo @Styles.Render("~/js/base/style") @Scripts.Render("~/js/base/lib")
@Scripts.Render("~/js/angularjs/app")

五、运行效果

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

六、总结

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

本文所有源码下载地址:

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

angularjsangularjs管理系统做管理系统权限管理

相关文章

前言 做过web项目开发的人对layer弹层组件肯定不陌生,作为l...
前言 前端表单校验是过滤无效数据、假数据、有毒数据的第一步...
前言 图片上传是web项目常见的需求,我基于之前的博客的代码...
前言 导出Excel文件这个功能,通常都是在后端实现返回前端一...
前言 众所周知,js是单线程的,从上往下,从左往右依次执行,...
前言 项目开发中,我们可能会碰到这样的需求:select标签,禁...