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


    简介

    关于这个框架的背景,在前面我已经交代过了。不清楚的可以查看这个链接 极简实用的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);
            }
        }

     效果图

    总结

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

    源码地址

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

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

    喜欢交流的人进微信群

    作者:课间一起牛

    出处:https://www.cnblogs.com/mhg215/

    声援博主:如果您觉得文章对您有帮助,请点击文章末尾的【关注我】吧!

    别忘记点击文章右下角的【推荐】支持一波。~~~///(^v^)\~~~ .

    本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

    如果您有其他问题,也欢迎关注我下方的公众号,可以联系我一起交流切磋!

     B站: 课间一起牛的B站         知乎:课间一起牛的知乎

    码云:课间一起牛的码云      github:课间一起牛的github

  • 相关阅读:
    使用Eclipse调试Java程序的10个技巧
    向mysql中插入格式为"yyyyMMdd HH:mm:ss"的时间
    乐观锁和悲观锁
    js验证表单大全
    subclipse版本回滚功能
    (转)Linux(RedHat AS4)下架设subversion(svn)服务器
    Redhat5下安装Apache2与Subversion1.6.15
    (转)深入探讨 Java 类加载器
    (转)Java深度历险(一)——Java字节代码的操纵
    关于SVN 目录结构
  • 原文地址:https://www.cnblogs.com/mhg215/p/14622529.html
Copyright © 2020-2023  润新知