鼎鼎知识库
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

08.智能照明首页接口思路.md 8.6KB

需求

  • 共性提炼:因为从首页项目,到楼层、区域、电箱展示内容格式相同,视图模型需要提炼出基类。
  • 层级扩展:设计图中给出的是”项目、楼层、区域、电箱”,如果有更多的层级如何处理?这里不能针对每个层级硬编码,这样会写很多接口,应该保持尽量少的接口。
  • 分页:如果列表项很多,需要分页。
  • 中介模式:希望控制器方法内代码尽量简洁,把请求交给一套机制,该机制返回响应。这里采用中介者模式。

视图模型设计

从前端发来的请求,服务端需要通过类来封装。所有的请求都涉及到分页、集团相关、项目相关、连接字符串相关,提炼出一个基类。

BaseQuery.cs

public SieveModel PagingModel{get;set;}
public string GroupId{get;set;}
public string ProjectId{get;set;}
public string ConnKey{get;set;}

以上,SieveModel来自一个第三方的有关分页的库,叫做Sieve

考虑到首页和内页不同之处,在基类基础上再设计: GeneralSumQuery.cs

//IRequest<T>来自一个中介者模式的库MediatR
//意思是说,只要给我GeneralSumQuery,我就返回GeneralSumViewModel给外界
//GeneralSumViewModel这个模型是给手机端,手机端拿着这个模型去填充页面
public class GeneralSumQuery : BaseQuery, IRequest<GeneralSumViewModel>
{
    //首页的Project和内页的Location都需要用到
    //当Level=0,就代表首页Project,当Level=1比如代表楼层,以此类推
    public int Level{get;set;}

    //这个属性是给Location用的,就是根据不同的LocationId去加载其下的子级Location
    //子级Location有一个属性叫做ParentId,这个属性就指向父级的LocationId
    //所有,根据父级的主键,也就是这里的LocationId能获得其下的所有子级Location
    public string LocationId{get;set;}
}

GeneralSumViewModel是返回给手机端用的,它代表如下红框中的部分:

对应代码:

public class GeneralSumViewModel
{
    //一旦有集合属性,一定要在构造函数内初始化,否则容易报null的错
    public GeneralSumViewModel()
        {

            Items = new List<GeneralSumItemViewModel>();
        }

    public List<GeneralSumItemViewmodel> Items{get;set;}
}

GeneralSumItemViewmodel代表这列表项,即如下红框中的部分:

代码如下:

public class GeneralSumItemViewModel : BaseViewModel
    {
        /// <summary>
        /// 名称:项目名称、楼层名称、区域名称、电箱名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 报警颜色:WarningColorEnum
        /// </summary>
        public short Color { get; set; }

        /// <summary>
        /// 是否可以进入到下一级
        /// </summary>
        public bool IsContinue { get; set; }

        /// <summary>
        /// 下一个位置层级
        /// </summary>
        public int NextLevel { get; set; }

        /// <summary>
        /// 本层Location主键,项目的LocationId=-1
        /// </summary>
        public string LocationId { get; set; }

        /// <summary>
        /// 报警
        /// </summary>
        public GeneralWarningItemViewModel WarningItem { get; set; }

        /// <summary>
        /// 负荷
        /// </summary>
        public GeneralFuHeItemViewModel FuHeItem { get; set; }

        /// <summary>
        /// 开关比例
        /// </summary>
        public GeneralKGBLItemViewModel KGBLItem { get; set; }

视图模型的基类:

public class BaseViewModel
    {
        public string ProjectId { get; set; }
        public string ConnKey { get; set; }
    }

有关报警的视图模型:

public class GeneralWarningItemViewModel : BaseViewModel
    {
        /// <summary>
        /// 报警描述
        /// </summary>
        public string Description { get; set; }
        /// <summary>
        /// 报警颜色:WarningColorEnum
        /// </summary>
        public short Color { get; set; }
        /// <summary>
        /// 报警数
        /// </summary>
        public string WarningNum { get; set; }
    }

有关负荷的视图模型:

public class GeneralFuHeItemViewModel : BaseViewModel
    {
        /// <summary>
        /// 负荷值
        /// </summary>
        public string FuHeValue { get; set; }
    }

有关开关比例的视图模型:

public class GeneralKGBLItemViewModel : BaseViewModel
    {
        public string OpenNum { get; set; }
        public string CloseNum { get; set; }
    }

以上,包括接口所接受的模型和接口响应给前端的视图模型。

输入和输出定下来了,现在需要通过中介者模式让处理请求,输出响应。如下:

处理请求和响应,中介者模式

MediatR是一个有关中介者模式的第三方库,它的作者就是大名鼎鼎的AutopMapper的作者,而AutoMapper是用来建立DTO和Entity Framework模型之间映射的第三方库。

HomeController.cs

 [Route("api/home")]
    public class HomeController : BaseController
    {

        private readonly IHostingEnvironment _env;

        public HomeController(IHostingEnvironment env)
        {
            _env = env;
        }


        /// <summary>
        /// 获取首页项目列表
        /// </summary>
        /// <returns></returns>
        /// <response code="200">返回首页项目列表</response>
        [Authorize]
        [Route("projects")]
        [HttpPost]
        [ProducesResponseType(200)]
        public async Task<IActionResult> GetProjects(BaseFlatQuery query)
        {

            var generalQuery = new GeneralSumQuery
            {
                Level = 0,
                LocationId = string.Empty,
                GroupId = User.FindFirst(GlobalSettings.Auth_GroupId).Value,
                ConnKey = string.Empty,
                ProjectId = string.Empty,
                PagingModel = new SieveModel
                {
                    Page=query.Page,
                    PageSize = query.PageSize
                }
            };

            return Ok(await Mediator.Send(generalQuery));
        }

        /// <summary>
        /// 获取楼层、区域等列表
        /// </summary>
        /// <returns></returns>
        /// <response code="200">返回楼层、区域等列表</response>
        [Authorize]
        [Route("locations")]
        [HttpPost]
        [ProducesResponseType(200)]
        public async Task<IActionResult> GetLocations(GeneralLocationQuery query)
        {
            var generalQuery = new GeneralSumQuery
            {
                Level = query.Level,
                LocationId = query.LocationId,
                GroupId=string.Empty,
                ConnKey=query.ConnKey,
                ProjectId = query.ProjectId,
                PagingModel = new SieveModel
                {
                    Page = query.Page,
                    PageSize = query.PageSize
                }
            };

            return Ok(await Mediator.Send(generalQuery));
        }


        /// <summary>
        /// 首页测试数据
        /// </summary>
        /// <returns></returns>
        /// <response code="200">返回首页测试数据</response>
        [Authorize]
        [HttpPost]
        [Route("test")]
        [ProducesResponseType(200)]
        public IActionResult Test()
        {           
            //return Ok(new { Id = 1, Name="这里是测试数据"});
            return Ok(User.FindFirst(GlobalSettings.Auth_GroupId).Value);
        }
    }

为什么需要BaseFlatQuery这个类?为什么不能用BaseQuery这个类?因为BaseQuery有些属性不是前端输入的,比如GroupId, ConnKey,这些属性是根据用户的相关信息赋值的,前端的请求不需要这个。而且在BaseQuery中有关分页的属性SieveModel中的Page和PageSize是可空的,在空的情况Sieve这个分页库会区找全局设置中的相关设置,即到appsettings.json中去找。总之,BaseFlatQuery是用来方便前端输入的,尽量让前端输入最简,并且没有嵌套类。同时,使用BaseFlatQuery这个类更安全,因为外界无从知晓服务端类的属性结构。

MediatR的工作原理:

  • 定义请求:
public class MyQuery : IRequest<MyViewMedel>
{}
  • 定义处理流程
public class MyQueryHandler : IRequestHandler<MyQuery,MyViewModel>
{

    public async Task<MyViewMedel> Handler(MyQuery request, CancellationToken cancellationToken)
    {


    }
}

MediatR的实现细节略去。