|
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281 |
- ![](./imgs/znzmsy.jpg)
-
- > 需求
-
- - **共性提炼**:因为从首页项目,到楼层、区域、电箱展示内容格式相同,视图模型需要提炼出基类。
- - **层级扩展**:设计图中给出的是"项目、楼层、区域、电箱",如果有更多的层级如何处理?这里不能针对每个层级硬编码,这样会写很多接口,应该保持尽量少的接口。
- - **分页**:如果列表项很多,需要分页。
- - **中介模式**:希望控制器方法内代码尽量简洁,把请求交给一套机制,该机制返回响应。这里采用中介者模式。
-
- > 视图模型设计
-
- 从前端发来的请求,服务端需要通过类来封装。所有的请求都涉及到分页、集团相关、项目相关、连接字符串相关,提炼出一个基类。
-
- 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`是返回给手机端用的,它代表如下红框中的部分:
-
- ![](./imgs/vm1.png)
-
- 对应代码:
-
- ```
- public class GeneralSumViewModel
- {
- //一旦有集合属性,一定要在构造函数内初始化,否则容易报null的错
- public GeneralSumViewModel()
- {
-
- Items = new List<GeneralSumItemViewModel>();
- }
-
- public List<GeneralSumItemViewmodel> Items{get;set;}
- }
- ```
- `GeneralSumItemViewmodel`代表这列表项,即如下红框中的部分:
- ![](./imgs/vm2.png)
-
- 代码如下:
-
- ```
- 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; }
- }
- ```
- 以上,包括接口所接受的模型和接口响应给前端的视图模型。
-
- 输入和输出定下来了,现在需要通过中介者模式让处理请求,输出响应。如下:
-
- ![](./imgs/mediator.png)
-
- > 处理请求和响应,中介者模式
-
- 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的实现细节略去。
|