通过开发一个新闻功能来了解UrShop


第一步:在Urs.Data 创建Domain与Mapping

using System;
using Urs.Core;

namespace Urs.Data.Domain.News
{
    /// <summary>
    /// 资讯
    /// </summary>
    public partial class NewsItem : BaseEntity
    {
        /// <summary>
        /// 标题
        /// </summary>
        public virtual string Title { get; set; }
        /// <summary>
        /// 简介
        /// </summary>
        public virtual string Short { get; set; }

        /// <summary>
        /// 内容
        /// </summary>
        public virtual string Full { get; set; }

        /// <summary>
        /// 发布
        /// </summary>
        public virtual bool Published { get; set; }

        /// <summary>
        /// 创建时间
        /// </summary>
        public virtual DateTime CreateTime { get; set; }
    }
}
using Microsoft.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore.Metadata.Builders;
using Urs.Data.Domain.News;

namespace Urs.Data.Mapping.News
{
    public partial class NewsItemMap : UrsEntityTypeConfiguration<NewsItem>
    {
        public override void Configure(EntityTypeBuilder<NewsItem> builder)
        {
            builder.ToTable("news");
            builder.HasKey(bp => bp.Id);
            base.Configure(builder);
        }
    }
}


第二步:Urs.Services编写接口与接口实现

这里面用到了缓存;缓存接口为ICacheManager 封装了基础方法

缓存基础教程请看《Cache缓存基础教程》

using Urs.Core;
using Urs.Data.Domain.News;

namespace Urs.Services.News
{
    public partial interface INewsService
    {
        void DeleteNews(NewsItem newsItem);
        NewsItem GetNewsById(int newsId);
        IPagedList<NewsItem> GetAllNews(int pageIndex, int pageSize, bool showHidden = false);
        void InsertNews(NewsItem news);
        void UpdateNews(NewsItem news);
    }
}
using System;
using System.Linq;
using Urs.Core;
using Urs.Core.Caching;
using Urs.Core.Data;
using Urs.Data.Domain.News;

namespace Urs.Services.News
{
    public partial class NewsService : INewsService
    {
        #region Constants
        private const string NEWS_BY_ID_KEY = "Urs.news.id-{0}";
        private const string NEWS_PATTERN_KEY = "Urs.news.";
        #endregion
        #region Fields
        private readonly IRepository<NewsItem> _newsItemRepository;
        private readonly ICacheManager _cacheManager;
        #endregion
        #region Ctor
        public NewsService(IRepository<NewsItem> newsItemRepository,
            ICacheManager cacheManager)
        {
            _newsItemRepository = newsItemRepository;
            _cacheManager = cacheManager;
        }
        #endregion
        #region NewsItem
        public virtual void DeleteNews(NewsItem newsItem)
        {
            if (newsItem == null)
                throw new ArgumentNullException("newsItem");

            _newsItemRepository.Delete(newsItem);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);
        }
        public virtual NewsItem GetNewsById(int newsId)
        {
            if (newsId == 0)
                return null;

            string key = string.Format(NEWS_BY_ID_KEY, newsId);
            return _cacheManager.Get(key, () =>
            {
                var n = _newsItemRepository.GetById(newsId);
                return n;
            });
        }
        public virtual IPagedList<NewsItem> GetAllNews(int pageIndex, int pageSize, bool showHidden = false)
        {
            var query = _newsItemRepository.Table;
            if (!showHidden)
            {
                var utcNow = DateTime.Now;
                query = query.Where(n => n.Published);
            }
            query = query.OrderByDescending(b => b.CreateTime);
            
            var news = new PagedList<NewsItem>(query, pageIndex, pageSize);
            return news;
        }
        public virtual void InsertNews(NewsItem news)
        {
            if (news == null)
                throw new ArgumentNullException("news");

            _newsItemRepository.Insert(news);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);
        }
        public virtual void UpdateNews(NewsItem news)
        {
            if (news == null)
                throw new ArgumentNullException("news");

            _newsItemRepository.Update(news);

            _cacheManager.RemoveByPattern(NEWS_PATTERN_KEY);
        }
        #endregion
    }
}


第三步:后端功能开发,创建NewsController,并添加增删改查功能;

通过 _permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel) 方法判断是否有权限;如果要给新闻模块独立权限需要在StandardPermissionProvider做配置;权限不足返回:HttpUnauthorized();

如果需要用到DTO的话需要在Models创建一个Models,命名以[名称]+Model;DTO采用了AutoMapper;

AutoMapper基础教程请看《AutoMapper基础学习教程》

AdminAuthorize 请看《AdminAuthorize后端权限说明》

using Microsoft.AspNetCore.Mvc;
using System;
using System.Linq;
using Urs.Admin.Models.News;
using Urs.Data.Domain.News;
using Urs.Framework.Controllers;
using Urs.Framework.Extensions;
using Urs.Framework.Kendoui;
using Urs.Services.News;
using Urs.Services.Security;

namespace Urs.Admin.Controllers
{
    [AdminAuthorize]
    public partial class NewsController : BaseAdminController
    {
        #region Fields

        private readonly INewsService _newsService;
        private readonly IPermissionService _permissionService;

        #endregion

        #region Constructors
        public NewsController(INewsService newsService, IPermissionService permissionService)
        {
            this._newsService = newsService;
            this._permissionService = permissionService;
        }
        #endregion

        #region News items

        public IActionResult List()
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel))
                return HttpUnauthorized();

            return View();
        }
        [HttpPost]
        public IActionResult List(PageRequest command)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel))
                return HttpUnauthorized();

            var news = _newsService.GetAllNews(command.Page - 1, command.Limit, true);
            var gridModel = new ResponseResult
            {
                data = news.Select(x =>
                {
                    var m = x.ToModel<NewsItemModel>();
                    m.CreateTime = x.CreateTime;
                    return m;
                }),
                count = news.TotalCount
            };
            return Json(gridModel);
        }

        public IActionResult Edit(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel))
                return HttpUnauthorized();

            var newsItem = _newsService.GetNewsById(id);
            if (newsItem == null)
            {
                var model = new NewsItemModel();
                return View(model);
            }
            else
            {
                var model = newsItem.ToModel<NewsItemModel>();
                return View(model);
            }

        }

        [HttpPost]
        public IActionResult Edit(NewsItemModel model, bool continueEditing)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel))
                return HttpUnauthorized();
            var newsItem = _newsService.GetNewsById(model.Id);
            if (newsItem == null)
            {
                var newsItems = model.ToEntity<NewsItem>();
                newsItems.CreateTime = DateTime.Now;
                _newsService.InsertNews(newsItems);
            }
            else
            {
                if (ModelState.IsValid)
                {
                    newsItem = model.ToEntity(newsItem);
                    _newsService.UpdateNews(newsItem);
                }
            }

            return Json(new { success = 1 });
        }

        [HttpPost]
        public IActionResult Delete(int id)
        {
            if (!_permissionService.Authorize(StandardPermissionProvider.AccessAdminPanel))
                return HttpUnauthorized();

            var newsItem = _newsService.GetNewsById(id);
            if (newsItem == null)
                return Json(new { error = 1 });

            _newsService.DeleteNews(newsItem);

            return Json(new { success = 1 });
        }
        #endregion

    }
}

               

前端实现:

<div class="layui-fluid">
    <div class="layui-card">
        <div class="layui-card-header layuiadmin-card-header-auto">
            <button class="layui-btn  layui-btn-normal layuiadmin-btn-tags" data-type="add">添加</button>
        </div>
        <div class="layui-card-body">
            <table id="LAY-topic-list" lay-filter="LAY-topic-list"></table>
            <script type="text/html" id="layuiadmin-app-cont-tagsbar">
                <a class="layui-btn layui-btn-normal layui-btn-xs" lay-event="edit"><i class="layui-icon layui-icon-edit"></i>编辑</a>
                <a class="layui-btn layui-btn-danger layui-btn-xs" lay-event="del"><i class="layui-icon layui-icon-delete"></i>删除</a>
            </script>
        </div>
    </div>
</div>
<script src="../../../layuiadmin/layui/layui.js"></script>
<script type="text/html" id="publishedTpl">
    {{#  if(d.Published){ }}
    <button class="layui-btn layui-btn-xs">已上架</button>
    {{#  } else { }}
    <button class="layui-btn layui-btn-primary layui-btn-xs">已下架</button>
    {{#  } }}
</script>
<script>

    layui.config({
        base: '../../../layuiadmin/' //静态资源所在路径
    }).extend({
        index: 'lib/index' //主入口模块
    }).use(['index','table'], function () {
        var index = layer.load(2, { shade: false });
        var table = layui.table;
        //分类管理
        table.render({
            elem: '#LAY-topic-list'
            , url: '@Html.Raw(Url.Action("List", "News"))' //模拟接口
            ,method:'post'
            , page: true //开启分页
            , even: true
            , limit: 15
            , limits: [15,30,50,100]
            , cols: [[
                { type: 'numbers', fixed: 'left' }
                , { field: 'Id', width: 100, title: 'ID' }
                , { field: 'Title', title: '标题', minWidth: 100 }
                , { field: 'StartDate', title: '开始时间', minWidth: 150 }
                , { field: 'EndDate', title: '结束时间', minWidth: 150 }
                , { field: 'CreatedOn', title: '创建时间', minWidth: 150 }
                , { field: 'Published', title: '是否上架', templet: '#publishedTpl', minWidth: 80, align: 'center' }
                , { title: '操作', width: 150, align: 'center', fixed: 'right', toolbar: '#layuiadmin-app-cont-tagsbar' }
            ]]
            , text: { none: '一条数据也没有^_^' }
            , done: function () {
                layer.close(index);
            }
        });

        //监听工具条
        table.on('tool(LAY-topic-list)', function (obj) {
            var data = obj.data;
            if (obj.event === 'del') {
                layer.confirm('确定删除?', function (index) {
                    obj.del();
                    $.post('@Url.Action("Delete")', { id: obj.data.Id });
                    layer.close(index);
                });

            } else if (obj.event === 'edit') {
                var tr = $(obj.tr);
                window.location.href = '@Url.Action("Edit")?id=' + data.Id;
            }
        });

        var $ = layui.$, active = {
            add: function () {
                window.location.href = '@Url.Action("Edit")';
            }
        }
        $('.layui-btn.layuiadmin-btn-tags').on('click', function () {
            var type = $(this).data('type');
            active[type] ? active[type].call(this) : '';
        });
    });
</script>
@model Urs.Admin.Models.News.NewsItemModel

<div class="layui-fluid">
    <div class="layui-row layui-col-space15">
        <div class="layui-col-md12">
            <div class="layui-card">
                <div class="layui-card-header">新闻&nbsp;&nbsp;&nbsp;<a href="javascript:;" layadmin-event="back">返回</a></div>
                <div class="layui-card-body">
                    <div class="layui-form" wid100 lay-filter="">
                        <input asp-for="Id" type="hidden" />
                        <div class="layui-form-item">
                            <div class="col-md-2">
                                <nop-label asp-for="Title" />
                            </div>
                            <div class="layui-input-inline" style="width:800px;">
                                <nop-editor asp-for="Title" />
                                <span asp-validation-for="Title"></span>
                            </div>
                        </div>
                        <div class="layui-form-item">
                            <div class="col-md-2">
                                <nop-label asp-for="Short" />
                            </div>
                            <div class="layui-input-inline" style="width:800px;">
                                <nop-textarea asp-for="Short"></nop-textarea>
                                <span asp-validation-for="Short"></span>
                            </div>
                        </div>
                        <div class="layui-form-item">
                            <nop-label asp-for="Full" />
                            <div class="layui-input-inline" style="width:800px;height:420px">
                                <nop-editor asp-for="Full" asp-template="UEditor" />
                                <span asp-validation-for="Full"></span>
                            </div>
                        </div>
                        <div class="layui-form-item">
                            <div class="col-md-2">
                                <nop-label asp-for="Published" />
                            </div>
                            <div class="layui-input-inline">
                                <nop-bool asp-for="Published" />
                                <span asp-validation-for="Published"></span>
                            </div>
                        </div>
                        <div class="layui-form-item">
                            <div class="layui-input-block">
                                <button class="layui-btn" lay-submit lay-filter="set_system_email">确认保存</button>
                            </div>
                        </div>
                    </div>
                    <div style="padding-top: 30px;">
                        <a href="javascript:;" layadmin-event="back" class="layui-btn layui-btn-primary layui-btn-sm">返回上级</a>
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>
<script>
    layui.config({
        base: '../../../layuiadmin/' //静态资源所在路径
    }).extend({
        index: 'lib/index' //主入口模块
    }).use(['index', 'set'], function () {
        var $ = layui.$
            , form = layui.form
            ,active = {}
        form.on('submit(set_system_email)', function (obj) {
            var index = layer.load(2, { shade: false });
            var field = obj.field; //获取提交的字段
                field.Full = ue.getContent();
                //layer.msg(JSON.stringify(obj.field));
                //提交 Ajax 成功后,关闭当前弹层并重载表格
                $.post('@Url.Action("Edit")', field, function (rep) {
                    if (rep.success == 1) {
                        layer.close(index);
                        layer.msg("保存成功,即将自动关闭", { time: 2 * 1000 }, function () {
                            var iframe_index = parent.layer.getFrameIndex(window.name); //先得到当前iframe层的索引
                            console.log("当前iframe层的索引:" + iframe_index + "==window.name:" + window.name);
                            parent.layer.close(iframe_index); //再执行关闭
                            window.parent.location.reload();
                        });
                    }
                });
                return false;
        });
        $('.layui-btn.layuiadmin-btn-tags').on('click', function () {
            var type = $(this).data('type');
            active[type] ? active[type].call(this) : '';
        });
    });
</script>