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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281
  1. ![](./imgs/znzmsy.jpg)
  2. > 需求
  3. - **共性提炼**:因为从首页项目,到楼层、区域、电箱展示内容格式相同,视图模型需要提炼出基类。
  4. - **层级扩展**:设计图中给出的是"项目、楼层、区域、电箱",如果有更多的层级如何处理?这里不能针对每个层级硬编码,这样会写很多接口,应该保持尽量少的接口。
  5. - **分页**:如果列表项很多,需要分页。
  6. - **中介模式**:希望控制器方法内代码尽量简洁,把请求交给一套机制,该机制返回响应。这里采用中介者模式。
  7. > 视图模型设计
  8. 从前端发来的请求,服务端需要通过类来封装。所有的请求都涉及到分页、集团相关、项目相关、连接字符串相关,提炼出一个基类。
  9. BaseQuery.cs
  10. ```
  11. public SieveModel PagingModel{get;set;}
  12. public string GroupId{get;set;}
  13. public string ProjectId{get;set;}
  14. public string ConnKey{get;set;}
  15. ```
  16. 以上,`SieveModel`来自一个第三方的有关分页的库,叫做`Sieve`。
  17. 考虑到首页和内页不同之处,在基类基础上再设计:
  18. GeneralSumQuery.cs
  19. ```
  20. //IRequest<T>来自一个中介者模式的库MediatR
  21. //意思是说,只要给我GeneralSumQuery,我就返回GeneralSumViewModel给外界
  22. //GeneralSumViewModel这个模型是给手机端,手机端拿着这个模型去填充页面
  23. public class GeneralSumQuery : BaseQuery, IRequest<GeneralSumViewModel>
  24. {
  25. //首页的Project和内页的Location都需要用到
  26. //当Level=0,就代表首页Project,当Level=1比如代表楼层,以此类推
  27. public int Level{get;set;}
  28. //这个属性是给Location用的,就是根据不同的LocationId去加载其下的子级Location
  29. //子级Location有一个属性叫做ParentId,这个属性就指向父级的LocationId
  30. //所有,根据父级的主键,也就是这里的LocationId能获得其下的所有子级Location
  31. public string LocationId{get;set;}
  32. }
  33. ```
  34. `GeneralSumViewModel`是返回给手机端用的,它代表如下红框中的部分:
  35. ![](./imgs/vm1.png)
  36. 对应代码:
  37. ```
  38. public class GeneralSumViewModel
  39. {
  40. //一旦有集合属性,一定要在构造函数内初始化,否则容易报null的错
  41. public GeneralSumViewModel()
  42. {
  43. Items = new List<GeneralSumItemViewModel>();
  44. }
  45. public List<GeneralSumItemViewmodel> Items{get;set;}
  46. }
  47. ```
  48. `GeneralSumItemViewmodel`代表这列表项,即如下红框中的部分:
  49. ![](./imgs/vm2.png)
  50. 代码如下:
  51. ```
  52. public class GeneralSumItemViewModel : BaseViewModel
  53. {
  54. /// <summary>
  55. /// 名称:项目名称、楼层名称、区域名称、电箱名称
  56. /// </summary>
  57. public string Name { get; set; }
  58. /// <summary>
  59. /// 报警颜色:WarningColorEnum
  60. /// </summary>
  61. public short Color { get; set; }
  62. /// <summary>
  63. /// 是否可以进入到下一级
  64. /// </summary>
  65. public bool IsContinue { get; set; }
  66. /// <summary>
  67. /// 下一个位置层级
  68. /// </summary>
  69. public int NextLevel { get; set; }
  70. /// <summary>
  71. /// 本层Location主键,项目的LocationId=-1
  72. /// </summary>
  73. public string LocationId { get; set; }
  74. /// <summary>
  75. /// 报警
  76. /// </summary>
  77. public GeneralWarningItemViewModel WarningItem { get; set; }
  78. /// <summary>
  79. /// 负荷
  80. /// </summary>
  81. public GeneralFuHeItemViewModel FuHeItem { get; set; }
  82. /// <summary>
  83. /// 开关比例
  84. /// </summary>
  85. public GeneralKGBLItemViewModel KGBLItem { get; set; }
  86. ```
  87. 视图模型的基类:
  88. ```
  89. public class BaseViewModel
  90. {
  91. public string ProjectId { get; set; }
  92. public string ConnKey { get; set; }
  93. }
  94. ```
  95. 有关报警的视图模型:
  96. ```
  97. public class GeneralWarningItemViewModel : BaseViewModel
  98. {
  99. /// <summary>
  100. /// 报警描述
  101. /// </summary>
  102. public string Description { get; set; }
  103. /// <summary>
  104. /// 报警颜色:WarningColorEnum
  105. /// </summary>
  106. public short Color { get; set; }
  107. /// <summary>
  108. /// 报警数
  109. /// </summary>
  110. public string WarningNum { get; set; }
  111. }
  112. ```
  113. 有关负荷的视图模型:
  114. ```
  115. public class GeneralFuHeItemViewModel : BaseViewModel
  116. {
  117. /// <summary>
  118. /// 负荷值
  119. /// </summary>
  120. public string FuHeValue { get; set; }
  121. }
  122. ```
  123. 有关开关比例的视图模型:
  124. ```
  125. public class GeneralKGBLItemViewModel : BaseViewModel
  126. {
  127. public string OpenNum { get; set; }
  128. public string CloseNum { get; set; }
  129. }
  130. ```
  131. 以上,包括接口所接受的模型和接口响应给前端的视图模型。
  132. 输入和输出定下来了,现在需要通过中介者模式让处理请求,输出响应。如下:
  133. ![](./imgs/mediator.png)
  134. > 处理请求和响应,中介者模式
  135. MediatR是一个有关中介者模式的第三方库,它的作者就是大名鼎鼎的AutopMapper的作者,而AutoMapper是用来建立DTO和Entity Framework模型之间映射的第三方库。
  136. HomeController.cs
  137. ```
  138. [Route("api/home")]
  139. public class HomeController : BaseController
  140. {
  141. private readonly IHostingEnvironment _env;
  142. public HomeController(IHostingEnvironment env)
  143. {
  144. _env = env;
  145. }
  146. /// <summary>
  147. /// 获取首页项目列表
  148. /// </summary>
  149. /// <returns></returns>
  150. /// <response code="200">返回首页项目列表</response>
  151. [Authorize]
  152. [Route("projects")]
  153. [HttpPost]
  154. [ProducesResponseType(200)]
  155. public async Task<IActionResult> GetProjects(BaseFlatQuery query)
  156. {
  157. var generalQuery = new GeneralSumQuery
  158. {
  159. Level = 0,
  160. LocationId = string.Empty,
  161. GroupId = User.FindFirst(GlobalSettings.Auth_GroupId).Value,
  162. ConnKey = string.Empty,
  163. ProjectId = string.Empty,
  164. PagingModel = new SieveModel
  165. {
  166. Page=query.Page,
  167. PageSize = query.PageSize
  168. }
  169. };
  170. return Ok(await Mediator.Send(generalQuery));
  171. }
  172. /// <summary>
  173. /// 获取楼层、区域等列表
  174. /// </summary>
  175. /// <returns></returns>
  176. /// <response code="200">返回楼层、区域等列表</response>
  177. [Authorize]
  178. [Route("locations")]
  179. [HttpPost]
  180. [ProducesResponseType(200)]
  181. public async Task<IActionResult> GetLocations(GeneralLocationQuery query)
  182. {
  183. var generalQuery = new GeneralSumQuery
  184. {
  185. Level = query.Level,
  186. LocationId = query.LocationId,
  187. GroupId=string.Empty,
  188. ConnKey=query.ConnKey,
  189. ProjectId = query.ProjectId,
  190. PagingModel = new SieveModel
  191. {
  192. Page = query.Page,
  193. PageSize = query.PageSize
  194. }
  195. };
  196. return Ok(await Mediator.Send(generalQuery));
  197. }
  198. /// <summary>
  199. /// 首页测试数据
  200. /// </summary>
  201. /// <returns></returns>
  202. /// <response code="200">返回首页测试数据</response>
  203. [Authorize]
  204. [HttpPost]
  205. [Route("test")]
  206. [ProducesResponseType(200)]
  207. public IActionResult Test()
  208. {
  209. //return Ok(new { Id = 1, Name="这里是测试数据"});
  210. return Ok(User.FindFirst(GlobalSettings.Auth_GroupId).Value);
  211. }
  212. }
  213. ```
  214. 为什么需要`BaseFlatQuery`这个类?为什么不能用`BaseQuery`这个类?因为`BaseQuery`有些属性不是前端输入的,比如GroupId, ConnKey,这些属性是根据用户的相关信息赋值的,前端的请求不需要这个。而且在`BaseQuery`中有关分页的属性SieveModel中的Page和PageSize是可空的,在空的情况Sieve这个分页库会区找全局设置中的相关设置,即到appsettings.json中去找。总之,`BaseFlatQuery`是用来方便前端输入的,尽量让前端输入最简,并且没有嵌套类。同时,使用`BaseFlatQuery`这个类更安全,因为外界无从知晓服务端类的属性结构。
  215. MediatR的工作原理:
  216. - 定义请求:
  217. ```
  218. public class MyQuery : IRequest<MyViewMedel>
  219. {}
  220. ```
  221. - 定义处理流程
  222. ```
  223. public class MyQueryHandler : IRequestHandler<MyQuery,MyViewModel>
  224. {
  225. public async Task<MyViewMedel> Handler(MyQuery request, CancellationToken cancellationToken)
  226. {
  227. }
  228. }
  229. ```
  230. MediatR的实现细节略去。