From c86e4774e103e36e5b617b7c34f3e55102eb21a0 Mon Sep 17 00:00:00 2001 From: wanggang <76527413@qq.com> Date: Mon, 8 Jul 2019 12:14:15 +0800 Subject: [PATCH] update Former-commit-id: 94255d4d01e80d6060137164e11da923eb2cbb06 --- projects/Infrastructure/Web/BaseStartup.cs | 23 +- .../Application/Entities/Users/Permission.cs | 19 ++ .../Entities/Users/PermissionCategory.cs | 12 + .../Application/Entities/Users/Role.cs | 14 + .../Entities/Users/RolePermission.cs | 15 + .../Application/Entities/Users/User.cs | 32 ++ .../Application/Entities/Users/UserRole.cs | 15 + .../Models/Users/ChangePasswordModel.cs | 24 ++ .../Models/Users/EditPermissionModel.cs | 18 + .../Application/Models/Users/EditRoleModel.cs | 18 + .../Application/Models/Users/EditUserModel.cs | 21 ++ .../Models/Users/ForgotPasswordModel.cs | 16 + .../Application/Models/Users/LoginModel.cs | 27 ++ .../Application/Models/Users/RegisterModel.cs | 42 +++ .../Models/Users/ResetPasswordModel.cs | 24 ++ .../Areas/Admin/Controllers/AjaxController.cs | 65 ++++ .../Admin/Controllers/PermissionController.cs | 31 ++ .../Areas/Admin/Controllers/RoleController.cs | 59 ++++ .../Areas/Admin/Controllers/UserController.cs | 67 ++++ .../Controllers/AccountController.cs | 317 ++++++++++++++++++ projects/IoT/IoT.Shared/IoTServiceStartup.cs | 148 ++++++++ .../Views/Account/ChangePassword.cshtml | 5 + .../Views/Account/ForgotPassword.cshtml | 8 + .../IoT/IoT.Shared/Views/Account/Index.cshtml | 3 + .../IoT/IoT.Shared/Views/Account/Login.cshtml | 7 + .../IoT.Shared/Views/Account/Register.cshtml | 7 + .../Views/Account/ResetPassword.cshtml | 7 + .../IoT/IoT.Shared/Views/Account/_Menu.cshtml | 21 ++ .../IoT.Shared/Views/Account/_Script.cshtml | 56 ++++ .../Application/Domain/Entities/FBeeDevice.cs | 5 +- .../Application/Domain/Entities/Gateway.cs | 2 + .../FBeeService/FBeeService.csproj | 1 - .../IoT/IoTServices/FBeeService/Program.cs | 3 +- .../IoT/IoTServices/FBeeService/Startup.cs | 4 +- 34 files changed, 1120 insertions(+), 16 deletions(-) create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/Permission.cs create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/PermissionCategory.cs create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/Role.cs create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/RolePermission.cs create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/User.cs create mode 100644 projects/IoT/IoT.Shared/Application/Entities/Users/UserRole.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/ChangePasswordModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/EditPermissionModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/EditRoleModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/EditUserModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/ForgotPasswordModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/LoginModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/RegisterModel.cs create mode 100644 projects/IoT/IoT.Shared/Application/Models/Users/ResetPasswordModel.cs create mode 100644 projects/IoT/IoT.Shared/Areas/Admin/Controllers/AjaxController.cs create mode 100644 projects/IoT/IoT.Shared/Areas/Admin/Controllers/PermissionController.cs create mode 100644 projects/IoT/IoT.Shared/Areas/Admin/Controllers/RoleController.cs create mode 100644 projects/IoT/IoT.Shared/Areas/Admin/Controllers/UserController.cs create mode 100644 projects/IoT/IoT.Shared/Controllers/AccountController.cs create mode 100644 projects/IoT/IoT.Shared/IoTServiceStartup.cs create mode 100644 projects/IoT/IoT.Shared/Views/Account/ChangePassword.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/ForgotPassword.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/Index.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/Login.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/Register.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/ResetPassword.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/_Menu.cshtml create mode 100644 projects/IoT/IoT.Shared/Views/Account/_Script.cshtml diff --git a/projects/Infrastructure/Web/BaseStartup.cs b/projects/Infrastructure/Web/BaseStartup.cs index f0eca936..64d74b7d 100644 --- a/projects/Infrastructure/Web/BaseStartup.cs +++ b/projects/Infrastructure/Web/BaseStartup.cs @@ -107,15 +107,7 @@ namespace Infrastructure.Web o.SupportedUICultures = supportedCultures; }); this.AddAuthentication(services); - services.AddSwaggerGen(c => - { - c.SwaggerDoc("v1", new Info - { - Title = this._cfg.GetValue("openapi.title", "web api"), - Version = this._cfg.GetValue("openapi.version", "1.0") - }); - c.EnableAnnotations(); - }); + this.AddSwagger(services); services.AddDistributedMemoryCache(); services.AddSession(options => { @@ -166,6 +158,19 @@ namespace Infrastructure.Web }); } + public virtual void AddSwagger(IServiceCollection services) + { + services.AddSwaggerGen(c => + { + c.SwaggerDoc(this._cfg.GetValue("openapi.name", "v1"), new Info + { + Title = this._cfg.GetValue("openapi.title", "web api"), + Version = this._cfg.GetValue("openapi.version", "1.0") + }); + c.EnableAnnotations(); + }); + } + public virtual void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) { if (env.IsDevelopment()) diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/Permission.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/Permission.cs new file mode 100644 index 00000000..4b376e5f --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/Permission.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "权限")] + public class Permission : BaseEntity, IDisableUpdate, IDisableDelete + { + public string Name { get; set; } + + public string Number { get; set; } + public Guid? CategoryId { get; set; } + public PermissionCategory Category { get; set; } = new PermissionCategory(); + + public List RolePermissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/PermissionCategory.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/PermissionCategory.cs new file mode 100644 index 00000000..252cc500 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/PermissionCategory.cs @@ -0,0 +1,12 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "权限分类")] + public class PermissionCategory : BaseTreeEntity + { + public List Permissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/Role.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/Role.cs new file mode 100644 index 00000000..97559793 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/Role.cs @@ -0,0 +1,14 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "角色")] + public class Role : BaseEntity + { + public string Name { get; set; } + public List UserRoles { get; set; } = new List(); + public List RolePermissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/RolePermission.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/RolePermission.cs new file mode 100644 index 00000000..a0ebf3a2 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/RolePermission.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "角色权限")] + public class RolePermission : BaseEntity + { + public Guid RoleId { get; set; } + public Guid PermissionId { get; set; } + public Role Role { get; set; } + public Permission Permission { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/User.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/User.cs new file mode 100644 index 00000000..ca3bc89e --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/User.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "用户")] + public class User : BaseEntity, IDisableDelete + { + /// + /// 登录名 + /// + public string UserName { get; set; } + + /// + /// 加密混淆随机数 + /// + public string SecurityStamp { get; set; } + + /// + /// 加密密码 + /// + public string PasswordHash { get; set; } + + /// + /// 邮箱 + /// + public string Email { get; set; } + + public List UserRoles { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Entities/Users/UserRole.cs b/projects/IoT/IoT.Shared/Application/Entities/Users/UserRole.cs new file mode 100644 index 00000000..0afa0826 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Entities/Users/UserRole.cs @@ -0,0 +1,15 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Domain; + +namespace Application.Domain.Entities +{ + [Display(Name = "用户角色")] + public class UserRole : BaseEntity + { + public Guid UserId { get; set; } + public Guid RoleId { get; set; } + public User User { get; set; } + public Role Role { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/ChangePasswordModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/ChangePasswordModel.cs new file mode 100644 index 00000000..f735d4ec --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/ChangePasswordModel.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; + +namespace Application.Models +{ + [Display(Name = "修改密码")] + public class ChangePasswordModel + { + [Required(ErrorMessage = nameof(RequiredAttribute))] + [DataType(DataType.Password)] + [Display(Name = "当前密码")] + public string OldPassword { get; set; } + + [Required(ErrorMessage = nameof(RequiredAttribute))] + [StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度范围为{2}-{1}")] + [DataType(DataType.Password)] + [Display(Name = "新密码")] + public string NewPassword { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "确认新密码")] + [Compare(nameof(NewPassword), ErrorMessage = "{0}和{1}不符")] + public string ConfirmNewPassword { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/EditPermissionModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/EditPermissionModel.cs new file mode 100644 index 00000000..1f076097 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/EditPermissionModel.cs @@ -0,0 +1,18 @@ +using System.ComponentModel.DataAnnotations; +using Infrastructure.Application; + +namespace Application.Models +{ + [Display(Name = "权限")] + public class EditPermissionModel : EditModel + { + [Display(Name = "分类")] + public string CategoryName { get; set; } + + [Display(Name = "名称"), Required] + public string Name { get; set; } + + [Display(Name = "编号"), Required] + public string Number { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/EditRoleModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/EditRoleModel.cs new file mode 100644 index 00000000..ce799a85 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/EditRoleModel.cs @@ -0,0 +1,18 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Application; + +namespace Application.Models +{ + [Display(Name = "角色")] + public class EditRoleModel : EditModel + { + [Display(Name = "名称"), Required] + public string Name { get; set; } + + [DataType("MultiSelectList")] + [Display(Name = "权限")] + public List Permissions { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/EditUserModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/EditUserModel.cs new file mode 100644 index 00000000..0bf852cc --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/EditUserModel.cs @@ -0,0 +1,21 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using Infrastructure.Application; + +namespace Application.Models +{ + [Display(Name = "用户")] + public class EditUserModel : EditModel + { + [Required(ErrorMessage = nameof(RequiredAttribute))] + [RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage = "用户名只能由数字和字母组成")] + [StringLength(30, MinimumLength = 5, ErrorMessage = "用户名长度为5-30")] + [Display(Name = "用户名")] + public string UserName { get; set; } + + [DataType("MultiSelectList")] + [Display(Name = "角色")] + public List Roles { get; set; } = new List(); + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/ForgotPasswordModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/ForgotPasswordModel.cs new file mode 100644 index 00000000..ded76e42 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/ForgotPasswordModel.cs @@ -0,0 +1,16 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; + +namespace Application.Models +{ + [Display(Name = "忘记密码")] + public class ForgotPasswordModel + { + [Required(ErrorMessage = nameof(RequiredAttribute))] + [DataType(DataType.EmailAddress)] + [RegularExpression(@"^\w+@\w+\.\w+$", ErrorMessage = "邮箱格式错误")] + [Display(Name = "邮箱")] + [Remote("HasEmail", "Account", ErrorMessage = "邮箱不存在")] + public string Email { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/LoginModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/LoginModel.cs new file mode 100644 index 00000000..b92fcfde --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/LoginModel.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; +using Infrastructure.Web; +using Infrastructure.Web.DataAnnotations; + +namespace Application.Models +{ + [Display(Name = "登录")] + public class LoginModel + { + [Display(Name = "用户名")] + [Required(ErrorMessage = nameof(RequiredAttribute))] + public string UserName { get; set; } + + [Display(Name = "密码")] + [Required(ErrorMessage = nameof(RequiredAttribute))] + [DataType(DataType.Password)] + public string Password { get; set; } + + //[ImageCaptcha, UIHint("ImageCaptcha")] + //[Display(Name = "验证码")] + //[Required(ErrorMessage = nameof(RequiredAttribute))] + //public string ImageCaptcha { get; set; } + + [Display(Name = "记住我")] + public bool RememberMe { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/RegisterModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/RegisterModel.cs new file mode 100644 index 00000000..32cd89f3 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/RegisterModel.cs @@ -0,0 +1,42 @@ +using System.ComponentModel.DataAnnotations; +using Infrastructure.Web.DataAnnotations; +using Infrastructure.Web.Mvc; +using Microsoft.AspNetCore.Mvc; + +namespace Application.Models +{ + [Display(Name = "邮箱注册")] + public class RegisterModel + { + [Required(ErrorMessage = nameof(RequiredAttribute))] + [RegularExpression("^[a-zA-Z0-9]+$", ErrorMessage = "用户名只能由数字和字母组成")] + [StringLength(30, MinimumLength = 5, ErrorMessage = "用户名长度为5-30")] + [Display(Name = "用户名")] + [Remote("UserNameNotUsed", "Account", ErrorMessage = "用户名已存在")] + public string UserName { get; set; } + + [Required(ErrorMessage = nameof(RequiredAttribute))] + [StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度范围为{2}-{1}")] + [DataType(DataType.Password)] + [Display(Name = "登录密码")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "确认密码")] + [Compare("Password", ErrorMessage = "确认密码输入错误")] + public string ConfirmPassword { get; set; } + + [Required(ErrorMessage = nameof(RequiredAttribute))] + [DataType(DataType.EmailAddress)] + [RegularExpression(@"^\w+@\w+\.\w+$", ErrorMessage = "邮箱格式错误")] + [Display(Name = "邮箱")] + [Remote("EmailNotUsed", "Account", ErrorMessage = "邮箱已占用")] + public string Email { get; set; } + + [MustBeTrue(ErrorMessage = nameof(MustBeTrueAttribute))] + [AdditionalMetadata("用户服务协议", "/")] + [Display(Name = "我阅读并同意")] + [UIHint("MustBeTrue")] + public bool AgreeWith { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Application/Models/Users/ResetPasswordModel.cs b/projects/IoT/IoT.Shared/Application/Models/Users/ResetPasswordModel.cs new file mode 100644 index 00000000..4346c495 --- /dev/null +++ b/projects/IoT/IoT.Shared/Application/Models/Users/ResetPasswordModel.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; +using Microsoft.AspNetCore.Mvc; + +namespace Application.Models +{ + [Display(Name = "重设密码")] + public class ResetPasswordModel + { + [HiddenInput(DisplayValue = false)] + [ScaffoldColumn(true)] + public string Id { get; set; } + + [Required(ErrorMessage = nameof(RequiredAttribute))] + [StringLength(100, MinimumLength = 6, ErrorMessage = "密码长度范围为{2}-{1}")] + [DataType(DataType.Password)] + [Display(Name = "登录密码")] + public string Password { get; set; } + + [DataType(DataType.Password)] + [Display(Name = "确认密码")] + [Compare("Password", ErrorMessage = "确认密码输入错误")] + public string ConfirmPassword { get; set; } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Areas/Admin/Controllers/AjaxController.cs b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/AjaxController.cs new file mode 100644 index 00000000..a1c32493 --- /dev/null +++ b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/AjaxController.cs @@ -0,0 +1,65 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Application.Domain.Entities; +using Infrastructure.Data; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Rendering; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; + +namespace IoTNode.Areas.Admin.Controllers +{ + [Area("Admin")] + public class AjaxController + { + private readonly ILogger _logger; + private readonly IRepository _roleRepo; + private readonly IRepository _permissionRepo; + + public AjaxController(ILogger logger, + IRepository roleRepo, + IRepository permissionRepo) + { + this._logger = logger; + this._roleRepo = roleRepo; + this._permissionRepo = permissionRepo; + } + + #region Role + + public MultiSelectList GetRoleMultiSelectList(IEnumerable selected) + { + if (selected == null) + { + selected = new List(); + } + var list = this._roleRepo.ReadOnlyTable() + .OrderBy(o => o.DisplayOrder) + .Select(o => new { o.Id, o.Name }) + .ToList(); + return new MultiSelectList(list, "Id", "Name", selected); + } + + #endregion Role + + #region Permission + + public MultiSelectList GetPermissionMultiSelectList(IEnumerable selectedValues) + { + if (selectedValues == null) + { + selectedValues = new List(); + } + var list = this._permissionRepo.Table() + .Include(o => o.Category) + .OrderBy(o => o.DisplayOrder) + .ThenBy(o => o.UpdatedOn) + .Select(o => new { o.Id, o.Name, Group = o.Category.Name }) + .ToList(); + return new MultiSelectList(list, "Id", "Name", selectedValues, "Group"); + } + + #endregion Permission + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Areas/Admin/Controllers/PermissionController.cs b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/PermissionController.cs new file mode 100644 index 00000000..cdb162ca --- /dev/null +++ b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/PermissionController.cs @@ -0,0 +1,31 @@ +using System.Linq; +using Infrastructure.Data; +using Infrastructure.Web.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Application.Domain.Entities; +using Application.Models; +using Infrastructure.Application; + +namespace IoTNode.Areas.Admin.Controllers +{ + [Authorize] + [Area(nameof(Admin))] + public class PermissionController : CrudController, EditPermissionModel, EditPermissionModel> + { + public PermissionController(IRepository repo) : base(repo) + { + } + + public override IQueryable Include(IQueryable query) + { + return query.Include(o => o.Category).ThenInclude(o => o.Parent); + } + + public override void ToModel(Permission entity, EditPermissionModel model) + { + model.CategoryName = entity.Category.GetFullName(); + } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Areas/Admin/Controllers/RoleController.cs b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/RoleController.cs new file mode 100644 index 00000000..c640bc35 --- /dev/null +++ b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/RoleController.cs @@ -0,0 +1,59 @@ +using System.Linq; +using Infrastructure.Data; +using Infrastructure.Extensions; +using Infrastructure.Web.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Application.Domain.Entities; +using Application.Models; +using Infrastructure.Application; + +namespace IoTNode.Areas.Admin.Controllers +{ + [Authorize] + [Area(nameof(Admin))] + public class RoleController : CrudController, EditRoleModel, EditRoleModel> + { + private readonly IRepository _roleRepo; + private readonly AjaxController _ajax; + + public RoleController(IRepository roleRepo, AjaxController ajax) : base(roleRepo) + { + this._roleRepo = roleRepo; + this._ajax = ajax; + } + + public override IQueryable Include(IQueryable query) + { + return query.Include(o => o.RolePermissions).ThenInclude(o => o.Permission).ThenInclude(o => o.Category).ThenInclude(o => o.Parent); + } + + public override void ToModel(Role entity, EditRoleModel model) + { + if (entity != null) + { + model.Permissions = entity.RolePermissions.Select(o => o.PermissionId).ToList(); + } + this.ViewData.MultiSelectList(o => model.Permissions, () => this._ajax.GetPermissionMultiSelectList(model.Permissions)); + } + + public override void ToEntity(EditRoleModel model, Role entity) + { + foreach (var id in entity.RolePermissions.Select(o => o.PermissionId).ToList()) + { + if (!model.Permissions.Any(o => o == id)) + { + entity.RolePermissions.RemoveAll(o => o.PermissionId == id); + } + } + foreach (var id in model.Permissions) + { + if (!entity.RolePermissions.Any(o => o.PermissionId == id)) + { + entity.RolePermissions.Add(new RolePermission { PermissionId = id }); + } + } + } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Areas/Admin/Controllers/UserController.cs b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/UserController.cs new file mode 100644 index 00000000..f9d6b338 --- /dev/null +++ b/projects/IoT/IoT.Shared/Areas/Admin/Controllers/UserController.cs @@ -0,0 +1,67 @@ +using System.Linq; +using Application.Domain.Entities; +using Application.Models; +using Infrastructure.Application; +using Infrastructure.Data; +using Infrastructure.Extensions; +using Infrastructure.Security; +using Infrastructure.Web.Mvc; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; + +namespace IoTNode.Areas.Admin.Controllers +{ + [Authorize] + [Area(nameof(Admin))] + public class UserController : CrudController, EditUserModel, EditUserModel> + { + private readonly IRepository _userRepo; + private readonly IEncryptionService _encrypitonService; + private readonly AjaxController _ajax; + + public UserController(IRepository userRepo, IEncryptionService encrypitonService, AjaxController ajax) : base(userRepo) + { + this._userRepo = userRepo; + this._encrypitonService = encrypitonService; + this._ajax = ajax; + } + + public override IQueryable Include(IQueryable query) + { + return query.Include(o => o.UserRoles).ThenInclude(o => o.Role); + } + + public override void ToModel(User entity, EditUserModel model) + { + if (entity != null) + { + model.Roles = entity.UserRoles.Select(o => o.RoleId).ToList(); + } + this.ViewData.MultiSelectList(o => model.Roles, () => this._ajax.GetRoleMultiSelectList(model.Roles)); + } + + public override void ToEntity(EditUserModel model, User entity) + { + foreach (var id in entity.UserRoles.Select(o => o.RoleId).ToList()) + { + if (!model.Roles.Any(o => o == id)) + { + entity.UserRoles.RemoveAll(o => o.RoleId == id); + } + } + foreach (var id in model.Roles) + { + if (!entity.UserRoles.Any(o => o.RoleId == id)) + { + entity.UserRoles.Add(new UserRole { RoleId = id }); + } + } + } + + public override void OnEdit(User entity, EditUserModel model) + { + model.UserName = entity.UserName; + } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Controllers/AccountController.cs b/projects/IoT/IoT.Shared/Controllers/AccountController.cs new file mode 100644 index 00000000..bbe95656 --- /dev/null +++ b/projects/IoT/IoT.Shared/Controllers/AccountController.cs @@ -0,0 +1,317 @@ +using System; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Application.Domain.Entities; +using Application.Models; +using Infrastructure.Data; +using Infrastructure.Email; +using Infrastructure.Extensions; +using Infrastructure.Resources; +using Infrastructure.Security; +using Infrastructure.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Localization; + +namespace IoT.UI.Shard.Controllers +{ + [Authorize] + [ApiExplorerSettings(IgnoreApi = true)] + public class AccountController : BaseController + { + private readonly IConfiguration _configuration; + private readonly IRepository _userRepo; + private readonly IStringLocalizer _localizer; + private readonly IEncryptionService _encryptionService; + private readonly IEmailSender _emailSender; + + public AccountController(IConfiguration configuration, + IRepository userRepo, + IEncryptionService encryptionService, + IStringLocalizer localizer, + IEmailSender emaliSender) + { + this._configuration = configuration; + this._userRepo = userRepo; + this._encryptionService = encryptionService; + this._localizer = localizer; + this._emailSender = emaliSender; + } + + #region 权限不足 + + [AllowAnonymous] + public IActionResult AccessDenied(string returnUrl) + { + return View(model: returnUrl); + } + + #endregion 权限不足 + + public IActionResult Index() + { + return View(); + } + + #region 注销 + + public IActionResult Logout() + { + HttpContext.SignOutAsync(); + return RedirectToAction("Index", "Home"); + } + + #endregion 注销 + + #region 登录 + + [HttpGet] + [AllowAnonymous] + public IActionResult Login(string returnUrl = null) + { + ViewData["ReturnUrl"] = returnUrl; + return View(); + } + + [AllowAnonymous] + [HttpPost] + public IActionResult Login(LoginModel model, string returnUrl = null) + { + var userName = model.UserName; + var password = model.Password; + if (ModelState.IsValid) + { + try + { + var user = this._userRepo.Table().FirstOrDefault(o => o.UserName == userName); + if (user == null) + { + ModelState.AddModelError(nameof(model.UserName), this._localizer["用户不存在"]); + } + else + { + if (user.PasswordHash == this._encryptionService.CreatePasswordHash(password, user.SecurityStamp)) + { + var userPermissions = this._userRepo.ReadOnlyTable().Where(o => o.UserName == userName) + .SelectMany(o => o.UserRoles) + .Select(o => o.Role) + .SelectMany(o => o.RolePermissions) + .Select(o => o.Permission.Number) + .ToList(); + HttpContext.SignIn(model.UserName, userPermissions, model.RememberMe); + if (string.IsNullOrEmpty(returnUrl)) + { + returnUrl = Url.Action("Index", "Home"); + } + ViewBag.Url = returnUrl; + return Redirect(returnUrl); + } + else + { + ModelState.AddModelError(nameof(model.Password), this._localizer["密码错误"]); + } + } + } + catch (Exception ex) + { + ex.PrintStack(); + ModelState.AddModelError("", ex.Message); + } + } + ViewData["ReturnUrl"] = returnUrl; + return View(model); + } + + #endregion 登录 + + #region 注册 + + [AllowAnonymous] + public IActionResult Register() + { + return View(); + } + + [HttpPost] + [AllowAnonymous] + public IActionResult Register(RegisterModel model) + { + if (User.Identity.IsAuthenticated) + { + return RedirectTo("Index", "Home", "当前已登录,请退出"); + } + if (ModelState.IsValid) + { + try + { + var user = new User().From(model); + user.SecurityStamp = this._encryptionService.CreateSalt(); + user.PasswordHash = this._encryptionService.CreatePasswordHash(model.Password, user.SecurityStamp); + user.Email = model.Email; + this._userRepo.Add(user); + this._userRepo.SaveChanges(); + return RedirectTo("Login", "Account", "注册成功"); + } + catch (Exception ex) + { + ex.PrintStack(); + ModelState.AddModelError("", ex.Message); + } + } + return View(model); + } + + #endregion 注册 + + #region 修改密码 + + public IActionResult ChangePassword() + { + return View(); + } + + [HttpPost] + [ValidateAntiForgeryToken] + public IActionResult ChangePassword(ChangePasswordModel model) + { + if (ModelState.IsValid) + { + try + { + var user = this._userRepo.Table().FirstOrDefault(o => o.UserName == User.Identity.Name); + if (user.PasswordHash == this._encryptionService.CreatePasswordHash(model.OldPassword, user.SecurityStamp)) + { + user.PasswordHash = this._encryptionService.CreatePasswordHash(model.NewPassword, user.SecurityStamp); + this._userRepo.SaveChanges(); + return RedirectTo(message: "密码修改成功"); + } + else + { + ModelState.AddModelError(nameof(model.OldPassword), "当前密码输入错误"); + } + } + catch (Exception ex) + { + ex.PrintStack(); + ModelState.AddModelError("", ex.Message); + } + } + return View(model); + } + + #endregion 修改密码 + + #region 忘记密码 + + [AllowAnonymous] + public IActionResult ForgotPassword() + { + return View(); + } + + [HttpPost] + [AllowAnonymous] + public IActionResult ForgotPassword(ForgotPasswordModel model) + { + if (ModelState.IsValid) + { + var url = Url.FullAction(nameof(ResetPassword), "Account", new { id = this._encryptionService.EncryptObject($"{model.Email}&{DateTime.UtcNow.AddMinutes(10).ToUnixTimeMilliseconds()}") }); + this._emailSender.SendMail(this._configuration["Name"], this._configuration["EmailUser"], model.Email, $"{this._configuration["Name"]}找回密码", $"点击链接或复制链接到浏览器中,10分钟内有效: {url} ", this._configuration["EmailHost"], this._configuration.GetValue("EmailPort"), this._configuration["EmailUser"], this._configuration["EmailPassword"]); + Console.WriteLine(url); + ViewBag.Message = "重设密码邮件已经发送到您的邮箱中"; + } + return View(model); + } + + [AllowAnonymous] + public IActionResult ResetPassword(string id) + { + var values = this._encryptionService.DecryptObject(id).Split('&'); + var timestamp = long.Parse(values[1]); + if (DateTime.UtcNow.AddMinutes(10).ToUnixTimeMilliseconds() > timestamp) + { + return RedirectTo(controller: "Home", message: "链接已失效"); + } + return View(new ResetPasswordModel { Id = id }); + } + + [AllowAnonymous] + [HttpPost] + public IActionResult ResetPassword(ResetPasswordModel model) + { + if (ModelState.IsValid) + { + var values = this._encryptionService.DecryptObject(model.Id).Split('&'); + var email = values[0]; + var timestamp = long.Parse(values[1]); + if (DateTime.UtcNow.AddMinutes(10).ToUnixTimeMilliseconds() > timestamp) + { + return RedirectTo(controller: "Home", message: "链接已失效"); + } + var user = this._userRepo.Table().FirstOrDefault(o => o.Email == email); + user.PasswordHash = this._encryptionService.CreatePasswordHash(model.Password, user.SecurityStamp); + this._userRepo.SaveChanges(); + return RedirectTo(action: "Login", message: "密码设置成功"); + } + return View(model); + } + + #endregion 忘记密码 + + #region Ajax验证 + + [AllowAnonymous] + public JsonResult UserNameNotUsed([Required]string userName) + { + if (ModelState.IsValid) + { + return Json(!this._userRepo.ReadOnlyTable().Any(o => o.UserName == userName)); + } + return Json("用户名不能为空"); + } + + [AllowAnonymous] + public JsonResult EmailNotUsed([Required]string email) + { + if (ModelState.IsValid) + { + return Json(!this._userRepo.ReadOnlyTable().Any(o => o.Email == email)); + } + return Json("邮箱不能为空"); + } + + [AllowAnonymous] + public JsonResult HasEmail([Required]string email) + { + if (ModelState.IsValid) + { + var user = this._userRepo.ReadOnlyTable().FirstOrDefault(o => o.Email == email); + if (user != null) + { + return Json(true); + } + return Json("邮箱不存在"); + } + return Json("邮箱不能为空"); + } + + #endregion Ajax验证 + + [Route("/Login")] + [AllowAnonymous] + public IActionResult Test() + { + var userName = "admin"; + var userPermissions = this._userRepo.ReadOnlyTable().Where(o => o.UserName == userName) + .SelectMany(o => o.UserRoles) + .Select(o => o.Role) + .SelectMany(o => o.RolePermissions) + .Select(o => o.Permission.Number) + .ToList(); + HttpContext.SignIn(userName, userPermissions, true); + return RedirectToAction("Index", "Home"); + } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/IoTServiceStartup.cs b/projects/IoT/IoT.Shared/IoTServiceStartup.cs new file mode 100644 index 00000000..f02d1046 --- /dev/null +++ b/projects/IoT/IoT.Shared/IoTServiceStartup.cs @@ -0,0 +1,148 @@ +using Application.Domain.Entities; +using Infrastructure.Data; +using Infrastructure.Domain; +using Infrastructure.Email; +using Infrastructure.Extensions; +using Infrastructure.Security; +using Infrastructure.UI; +using Infrastructure.Web; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace IoT.UI.Shard +{ + public class IoTServiceStartup : BaseStartup + { + public IoTServiceStartup(IConfiguration configuration, IHostingEnvironment env) : base(configuration, env) + { + } + + public override void ConfigureServices(IServiceCollection services) + { + services.AddTransient(); + base.ConfigureServices(services); + } + + public override void ConfigureOptions(IServiceCollection services) + { + services.ConfigureOptions(new FileConfigureOptions(_env, new List { "IoT.Shared" })); + } + + public override void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + base.Configure(app, env, loggerFactory); + } + + public override Task ValidatePrincipal(CookieValidatePrincipalContext arg) + { + return Task.Run(() => + { + var userRepo = arg.HttpContext.RequestServices.GetService>(); + + var userName = arg.Principal.Identity.Name; + var userPermissions = userRepo.ReadOnlyTable().Where(o => o.UserName == userName) + .SelectMany(o => o.UserRoles) + .Select(o => o.Role) + .SelectMany(o => o.RolePermissions) + .Select(o => o.Permission.Number) + .ToList(); + var currentPermissions = arg.Principal.Claims.Where(o => o.Type == "Role").Select(o => o.Value).ToList(); + if (!currentPermissions.SequenceEqual(userPermissions)) + { + arg.HttpContext.SignOutAsync(); + arg.HttpContext.SignIn(userName, userPermissions, arg.Properties.IsPersistent); + } + }); + } + + public override void OnModelCreating(ModelBuilder modelBuilder) + { + modelBuilder.Entity().HasOne(o => o.Parent).WithMany(o => o.Children).HasForeignKey(o => o.ParentId).OnDelete(DeleteBehavior.SetNull); + modelBuilder.Entity().HasOne(o => o.Category).WithMany(o => o.Permissions).HasForeignKey(o => o.CategoryId).OnDelete(DeleteBehavior.SetNull); + modelBuilder.Entity().HasOne(o => o.User).WithMany(o => o.UserRoles).HasForeignKey(o => o.UserId); + modelBuilder.Entity().HasOne(o => o.Role).WithMany(o => o.UserRoles).HasForeignKey(o => o.RoleId); + modelBuilder.Entity().HasOne(o => o.Role).WithMany(o => o.RolePermissions).HasForeignKey(o => o.RoleId); + modelBuilder.Entity().HasOne(o => o.Permission).WithMany(o => o.RolePermissions).HasForeignKey(o => o.PermissionId); + modelBuilder.Entity().HasIndex(o => o.UserName).IsUnique(); + modelBuilder.Entity().HasIndex(o => o.Email).IsUnique(); + modelBuilder.Entity().HasIndex(o => o.Name).IsUnique(); + modelBuilder.Entity().HasIndex(o => o.Number).IsUnique(); + modelBuilder.Entity().HasIndex(o => o.Number).IsUnique(); + modelBuilder.Entity().HasIndex(o => new { o.UserId, o.RoleId }).IsUnique(); + modelBuilder.Entity().HasIndex(o => new { o.RoleId, o.PermissionId }).IsUnique(); + } + + public override void Seed(DbContext dbContext, IServiceProvider serviceProvider, IConfiguration configuration) + { + dbContext.Set().Add(new PermissionCategory + { + Name = "配置", + Number = "Configuration", + Permissions = new List { + new Permission { Name = "查询配置", Number = "ListConfiguration",DisplayOrder =1 }, + new Permission { Name = "修改配置", Number = "EditConfiguration",DisplayOrder =2 } + } + }); int i = 1; + var skipReadCollection = new string[] { "Permission" }; + var skipAddCollection = new string[] { "Permission", "Setting" }; + foreach (var item in dbContext.Model.GetEntityTypes()) + { + var type = item.ClrType; + var name = type.GetDisplayName(); + var number = type.Name; + var category = new PermissionCategory + { + Name = name, + Number = type.Name, + DisplayOrder = i + }; + category.Permissions.Add(new Permission { Name = $"查询{name}", Number = $"List{number}", DisplayOrder = 10 * i + 1 }); + if (!skipReadCollection.Contains(type.Name)) + { + category.Permissions.Add(new Permission { Name = $"查看{name}", Number = $"Read{number}", DisplayOrder = 10 * i + 2 }); + } + if (!skipAddCollection.Contains(type.Name)) + { + category.Permissions.Add(new Permission { Name = $"添加{name}", Number = $"Add{number}", DisplayOrder = 10 * i + 3 }); + } + if (!typeof(IDisableUpdate).IsAssignableFrom(type)) + { + category.Permissions.Add(new Permission { Name = $"修改{name}", Number = $"Edit{number}", DisplayOrder = 10 * i + 4 }); + } + if (!typeof(IDisableDelete).IsAssignableFrom(type)) + { + category.Permissions.Add(new Permission { Name = $"删除{name}", Number = $"Delete{number}", DisplayOrder = 10 * i + 5 }); + } + dbContext.Set().Add(category); + i += 1; + } + dbContext.SaveChanges(); + var adminRole = new Role { Name = "管理员", IsReadOnly = true }; + foreach (var item in dbContext.Set()) + { + adminRole.RolePermissions.Add(new RolePermission { Permission = item, IsReadOnly = true }); + } + var encryptionService = serviceProvider.GetService(); + var securityStam = "123456"; + dbContext.Set().Add(new User + { + UserName = "admin", + SecurityStamp = securityStam, + PasswordHash = encryptionService.CreatePasswordHash("123456", securityStam), + Email = "test@test.com", + UserRoles = new List { new UserRole { Role = adminRole } } + }); + dbContext.SaveChanges(); + } + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/ChangePassword.cshtml b/projects/IoT/IoT.Shared/Views/Account/ChangePassword.cshtml new file mode 100644 index 00000000..530b288c --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/ChangePassword.cshtml @@ -0,0 +1,5 @@ +@model ChangePasswordModel +@{ + HtmlTitle = ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName(); +} +@Html.EditorForModel() \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/ForgotPassword.cshtml b/projects/IoT/IoT.Shared/Views/Account/ForgotPassword.cshtml new file mode 100644 index 00000000..9df95194 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/ForgotPassword.cshtml @@ -0,0 +1,8 @@ +@model ForgotPasswordModel +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + DisableBackUrl = true; + HtmlTitle = ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName(); +} +@ViewBag.Message +@Html.EditorForModel() \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/Index.cshtml b/projects/IoT/IoT.Shared/Views/Account/Index.cshtml new file mode 100644 index 00000000..dea06376 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/Index.cshtml @@ -0,0 +1,3 @@ +@{ + HtmlTitle = "用户中心"; +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/Login.cshtml b/projects/IoT/IoT.Shared/Views/Account/Login.cshtml new file mode 100644 index 00000000..df710025 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/Login.cshtml @@ -0,0 +1,7 @@ +@model LoginModel +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + DisableBackUrl = true; + HtmlTitle = ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName(); +} +@Html.EditorForModel() \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/Register.cshtml b/projects/IoT/IoT.Shared/Views/Account/Register.cshtml new file mode 100644 index 00000000..509c40b0 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/Register.cshtml @@ -0,0 +1,7 @@ +@model RegisterModel +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + DisableBackUrl = true; + HtmlTitle = ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName(); +} +@Html.EditorForModel() \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/ResetPassword.cshtml b/projects/IoT/IoT.Shared/Views/Account/ResetPassword.cshtml new file mode 100644 index 00000000..a382a498 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/ResetPassword.cshtml @@ -0,0 +1,7 @@ +@model ResetPasswordModel +@{ + Layout = "/Views/Shared/_Layout.cshtml"; + DisableBackUrl = true; + HtmlTitle = ViewContext.ViewData.ModelMetadata.ModelType.GetDisplayName(); +} +@Html.EditorForModel() \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/_Menu.cshtml b/projects/IoT/IoT.Shared/Views/Account/_Menu.cshtml new file mode 100644 index 00000000..c57a0694 --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/_Menu.cshtml @@ -0,0 +1,21 @@ + + +@functions{ + public string GetClass(params string[] controllers) + { + if (controllers.Select(o => o.ToLower()).Contains(this.ViewContext.RouteData.Values["controller"].ToString().ToLower())) + { + if (controllers.Length > 1) + { + return "active open"; + } + return "active"; + } + return ""; + } +} \ No newline at end of file diff --git a/projects/IoT/IoT.Shared/Views/Account/_Script.cshtml b/projects/IoT/IoT.Shared/Views/Account/_Script.cshtml new file mode 100644 index 00000000..98c3a76a --- /dev/null +++ b/projects/IoT/IoT.Shared/Views/Account/_Script.cshtml @@ -0,0 +1,56 @@ +@inject IConfiguration cfg +@{ + var total = cfg.GetValue("CaptchaSeconds"); + var sec = total; + var session = this.Context.Session; + var model = session.Get(CodeCaptchaModel.Key); + if (model != null) + { + sec = (int)(model.ExpireDateUtc - DateTime.UtcNow).TotalSeconds; + } +} + \ No newline at end of file diff --git a/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/FBeeDevice.cs b/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/FBeeDevice.cs index ac619c09..62450afd 100644 --- a/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/FBeeDevice.cs +++ b/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/FBeeDevice.cs @@ -1,10 +1,9 @@ using Infrastructure.Domain; -using Infrastructure.Extensions; -using System; -using System.IO; +using System.ComponentModel.DataAnnotations; namespace Application.Domain.Entities { + [Display(Name = "设备")] public class FBeeDevice : BaseEntity { public int DataType { get; set; } diff --git a/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/Gateway.cs b/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/Gateway.cs index b0e426d7..a3afb456 100644 --- a/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/Gateway.cs +++ b/projects/IoT/IoTServices/FBeeService/Application/Domain/Entities/Gateway.cs @@ -1,7 +1,9 @@ using Infrastructure.Domain; +using System.ComponentModel.DataAnnotations; namespace Application.Domain.Entities { + [Display(Name = "")] public class Gateway : BaseEntity { public string Sn { get; set; } diff --git a/projects/IoT/IoTServices/FBeeService/FBeeService.csproj b/projects/IoT/IoTServices/FBeeService/FBeeService.csproj index 531ad5ce..e38f7002 100644 --- a/projects/IoT/IoTServices/FBeeService/FBeeService.csproj +++ b/projects/IoT/IoTServices/FBeeService/FBeeService.csproj @@ -16,7 +16,6 @@ - diff --git a/projects/IoT/IoTServices/FBeeService/Program.cs b/projects/IoT/IoTServices/FBeeService/Program.cs index 9f061667..72f9bb66 100644 --- a/projects/IoT/IoTServices/FBeeService/Program.cs +++ b/projects/IoT/IoTServices/FBeeService/Program.cs @@ -14,7 +14,8 @@ namespace FBeeService var host = Helper.Instance.GetLocalIP().ToString(); WebHost.CreateDefaultBuilder(args) .Run(new List { - new EFConfigurationValue { Id = "openapi.title", Value= "web api" }, + new EFConfigurationValue { Id = "openapi.name", Value= "v1" }, + new EFConfigurationValue { Id = "openapi.title", Value= "fbee api" }, new EFConfigurationValue { Id = "openapi.version", Value= "1.0" }, new EFConfigurationValue { Id = "security:key", Value= "111111111111111111111111"}, new EFConfigurationValue { Id = "security:iv", Value= "11111111"}, diff --git a/projects/IoT/IoTServices/FBeeService/Startup.cs b/projects/IoT/IoTServices/FBeeService/Startup.cs index a616e878..865c51d4 100644 --- a/projects/IoT/IoTServices/FBeeService/Startup.cs +++ b/projects/IoT/IoTServices/FBeeService/Startup.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Logging; namespace FBeeService { - public class Startup : ShardStartup + public class Startup : IoTServiceStartup { public Startup(IConfiguration configuration, IHostingEnvironment env) : base(configuration, env) { @@ -29,9 +29,9 @@ namespace FBeeService public override void OnModelCreating(ModelBuilder modelBuilder) { - base.OnModelCreating(modelBuilder); modelBuilder.Entity().HasIndex(o => o.Sn).IsUnique(); modelBuilder.Entity().HasIndex(o => new { o.Sn, o.IEEE }).IsUnique(); + base.OnModelCreating(modelBuilder); } } } \ No newline at end of file