极简实用的Asp.NetCore模块化框架新增CMS模块

  • A+
所属分类:.NET技术
摘要

关于这个框架的背景,在前面我已经交代过了。不清楚的可以查看这个链接  极简实用的Asp.NetCore模块化框架决定免费开源了


简介

关于这个框架的背景,在前面我已经交代过了。不清楚的可以查看这个链接 极简实用的Asp.NetCore模块化框架决定免费开源了

在最近一段时间内,对这个框架新增了以下功能:

1、新增了CMS模块,目前整体都比较简单,适合个人博客使用。

2、新增了AOP缓存,使用AspectCore,缓存可做到Memarycache和redis一件切换。

3、新增AOP事务,服务层和控制器都可以打上特性标签使用。

4、对多租户使用Filter,不管是添加还是更新、查询即可自动赋值。

5、新增七牛云图片上传功能。

6、对于单表的增删改查,在控制器内做了封装,有新的业务按约定建立对应的CRUD实体,一套API自动完成。

7、后台管理新增站群管理。

说了那么多,让我们上点代码和截图来瞧一瞧吧。

AOP缓存

  public class CacheInterceptorAttribute : AbstractInterceptorAttribute     {         private static readonly ConcurrentDictionary<Type, MethodInfo> TypeofTaskResultMethod = new ConcurrentDictionary<Type, MethodInfo>();         readonly int _expireSecond;         readonly string _cacheKey;          #region 拦截处理         /// <summary>         /// 过期时间,单位:分         /// </summary>         /// <param name="expireMin"></param>         public CacheInterceptorAttribute(string cacheKey = null, int expireMin = -1)         {             _expireSecond = expireMin * 60;             _cacheKey = cacheKey;         }          public async override Task Invoke(AspectContext context, AspectDelegate next)         {             try             {                 string key = string.Empty;                 //自定义的缓存key不存在,再获取类名+方法名或类名+方法名+参数名的组合式key                 if (!string.IsNullOrEmpty(_cacheKey))                 {                     key = _cacheKey;                 }                 else                 {                     key = GetKey(context.ServiceMethod, context.Parameters);                 }                  var returnType = GetReturnType(context);                 var cache = context.ServiceProvider.GetService<ICacheHelper>();                 if (!cache.Exists(key))                 {                     return;                 }                 var strResult = cache.Get<string>(key);                 var result = JsonConvert.DeserializeObject(strResult, returnType);                 if (result != null)                 {                     context.ReturnValue = ResultFactory(result, returnType, context.IsAsync());                 }                 else                 {                     result = await RunAndGetReturn(context, next);                     if (_expireSecond > 0)                     {                         cache.Set(key, result, TimeSpan.FromMinutes(_expireSecond));                     }                     else                     {                         cache.Set(key, result);                     }                 }             }             catch (Exception e)             {                 Console.WriteLine(e.Message);             }         }          private static string GetKey(MethodInfo method, object[] parameters)         {             return GetKey(method.DeclaringType.Name, method.Name, parameters);         }         private static string GetKey(string className, string methodName, object[] parameters)         {             var paramConcat = parameters.Length == 0 ? string.Empty : ":" + JsonConvert.SerializeObject(parameters);             return $"{className}:{methodName}{paramConcat}";         }           /// <summary>         /// 获取被拦截方法返回值类型         /// </summary>         /// <param name="context"></param>         /// <returns></returns>         private Type GetReturnType(AspectContext context)         {             return context.IsAsync()                 ? context.ServiceMethod.ReturnType.GetGenericArguments().First()                 : context.ServiceMethod.ReturnType;         }          /// <summary>         /// 执行被拦截方法         /// </summary>         /// <param name="context"></param>         /// <param name="next"></param>         /// <returns></returns>         private async Task<object> RunAndGetReturn(AspectContext context, AspectDelegate next)         {             await context.Invoke(next);             return context.IsAsync()             ? await context.UnwrapAsyncReturnValue()             : context.ReturnValue;         }          /// <summary>         /// 处理拦截器返回结果         /// </summary>         /// <param name="result"></param>         /// <param name="returnType"></param>         /// <param name="isAsync"></param>         /// <returns></returns>         private object ResultFactory(object result, Type returnType, bool isAsync)         {             return !isAsync                 ? result                 : TypeofTaskResultMethod                     .GetOrAdd(returnType, t => typeof(Task)                     .GetMethods()                     .First(p => p.Name == "FromResult" && p.ContainsGenericParameters)                     .MakeGenericMethod(returnType))                     .Invoke(null, new object[] { result });         }         #endregion

多租户

  public class MultiTenantAttribute : ActionFilterAttribute, IActionFilter     {         /// <summary>         /// 全局注册过滤器 ,自动为添加 更新方法赋值。也可自行手动打上特性标签         /// </summary>         /// <param name="context"></param>         //private string[] methods = new string[] { "add", "modify" };         public override void OnActionExecuting(ActionExecutingContext context)         {             var actionDescriptor = context.ActionDescriptor as ControllerActionDescriptor;             var actionName = actionDescriptor.ActionName.ToLower();             ICacheHelper cache = context.HttpContext.RequestServices.GetRequiredService(typeof(ICacheHelper)) as ICacheHelper;             var siteId = cache.Get<Site>(KeyHelper.Cms.CurrentSite)?.Id;             //如果是增加和修改方法  根据站群id             //if (methods.Any(o => actionName.Contains(o)))             //{                 foreach (var parameter in actionDescriptor.Parameters)                 {                     var parameterName = parameter.Name;//获取Action方法中参数的名字                     var parameterType = parameter.ParameterType;//获取Action方法中参数的类型                     //if (!typeof(int).IsAssignableFrom(parameterType))//如果不是ID类型                     //{                     //    continue;                     //}                     //自动添加租户id                     if (typeof(IGlobalSite).IsAssignableFrom(parameterType))                     {                         var model = context.ActionArguments[parameterName] as IGlobalSite;                         if (siteId != null)                         {                              model.SiteId = siteId.Value;                         }                     }                 }             //}         }     } }

控制器单表CRUD API

 /// <summary>     /// 适用于多租户模块使用     /// </summary>     /// <typeparam name="TEntity">实体</typeparam>     /// <typeparam name="TDetailQuery">详情查询参数实体</typeparam>     /// <typeparam name="TDeleteInput">删除实体</typeparam>     /// <typeparam name="TListQuery">列表分页查询参数实体</typeparam>     /// <typeparam name="TCreateInput">创建实体</typeparam>     /// <typeparam name="TUpdateInput">更新实体</typeparam>     [Route("api/[controller]/[action]")]     [ApiController]     [Authorize]     [MultiTenant]     public abstract class ApiTenantBaseController<TEntity, TDetailQuery, TDeleteInput, TListQuery, TCreateInput, TUpdateInput> : ControllerBase        where TEntity : BaseSiteEntity, new()        where TDetailQuery : DetailSiteQuery        where TDeleteInput : DeletesSiteInput        where TListQuery : ListSiteQuery        where TCreateInput : class        where TUpdateInput : class     {         private readonly IBaseServer<TEntity> _service;         private readonly IMapper _mapper;          public ApiTenantBaseController(IBaseServer<TEntity> service, IMapper mapper)         {             _service = service;             _mapper = mapper;         }         /// <summary>         /// 批量真实删除         /// </summary>         /// <param name="deleteInput"></param>         /// <returns></returns>         [HttpDelete]         public virtual async Task<ApiResult> Deletes([FromBody] TDeleteInput deleteInput)         {             var res = await _service.DeleteAsync(deleteInput.Ids);             if (res <= 0)             {                 throw new FriendlyException("删除失败了!");             }             return new ApiResult();         }         /// <summary>         /// 单个真实删除         /// </summary>         /// <param name="deleteInput"></param>         /// <returns></returns>         [HttpDelete]         public virtual async Task<ApiResult> Delete([FromBody] TDeleteInput deleteInput)         {             foreach (var item in deleteInput.Ids)             {                 var res = await _service.DeleteAsync(d => d.Id == item && d.SiteId == deleteInput.SiteId);                 if (res <= 0)                 {                     throw new FriendlyException("删除失败了!");                 }             }             return new ApiResult();         }         /// <summary>         /// 软删除         /// </summary>         /// <param name="deleteInput"></param>         /// <returns></returns>         [HttpDelete]         public virtual async Task<ApiResult> SoftDelete([FromBody] TDeleteInput deleteInput)         {             foreach (var item in deleteInput.Ids)             {                 var res = await _service.UpdateAsync(d => new TEntity() { Status = false }, d => d.Id == item && d.SiteId == deleteInput.SiteId&&d.Status==true);                 if (res <= 0)                 {                     throw new FriendlyException("删除失败了!");                 }             }             return new ApiResult();         }         /// <summary>         /// 列表分页         /// </summary>         /// <param name="listQuery">参数实体</param>         /// <returns></returns>         [HttpGet]         public virtual async Task<ApiResult> GetListPages([FromQuery] TListQuery listQuery)         {             var res = await _service.GetPagesAsync(listQuery.Page, listQuery.Limit, d => d.SiteId == listQuery.SiteId&&d.Status==true, d => d.Id, false);             return new ApiResult(data: new { count = res.TotalItems, items = res.Items });         }          /// <summary>         /// 详情         /// </summary>         /// <param name="detailQuery">参数实体</param>         /// <returns></returns>         [HttpGet]         public virtual async Task<ApiResult> Detail([FromQuery] TDetailQuery detailQuery)         {             var res = await _service.GetModelAsync(d => d.Id == detailQuery.Id && d.SiteId == detailQuery.SiteId&&d.Status==true);             return new ApiResult(data: res);         }         /// <summary>         /// 添加         /// </summary>         /// <param name="createInput">添加实体</param>         /// <returns></returns>         [HttpPost]         public virtual async Task<ApiResult> Add([FromBody] TCreateInput createInput)         {             var entity = _mapper.Map<TEntity>(createInput);             var res = await _service.AddAsync(entity);             if (res <= 0)             {                 throw new FriendlyException("添加失败了!");             }             return new ApiResult(data: res);         }         /// <summary>         /// 修改-默认忽略更新CreateTime字段         /// </summary>         /// <param name="updateInput">修改实体</param>         /// <returns></returns>         [HttpPut]         public virtual async Task<ApiResult> Modify([FromBody] TUpdateInput updateInput)         {             var entity = _mapper.Map<TEntity>(updateInput);             var res = await _service.UpdateAsync(entity, d => new { d.CreateTime });             if (res <= 0)             {                 throw new FriendlyException("修改失败了!");             }             return new ApiResult(data: res);         }     }

 效果图

极简实用的Asp.NetCore模块化框架新增CMS模块

极简实用的Asp.NetCore模块化框架新增CMS模块

极简实用的Asp.NetCore模块化框架新增CMS模块

总结

好了,又要到说再见的时候了,框架我只要有时间就会一直更新下去,不合理的地方欢迎浏览代码指导批评,我希望这个框架从简单的一点一滴做起,慢慢地把它做大做强。算是程序员阶段最后一次做框架了,什么时候不更新了,有可能就转行了。大家也可以不使用这个框架,只要里面地思路能帮助到一部分人,我认为这就足够了。

 

源码地址

码云:https://gitee.com/shenniu_code_group/shen-nius.-modularity

 

github:https://github.com/realyrare/ShenNiusFramework