From 54bbfe2fe4cb42f4612fce33a72199fb09ac071a Mon Sep 17 00:00:00 2001 From: lq1405 <2769838458@qq.com> Date: Fri, 27 Dec 2024 21:50:13 +0800 Subject: [PATCH] =?UTF-8?q?V1.0.2=20=20=E6=96=B0=E5=A2=9E=E8=BD=AF?= =?UTF-8?q?=E4=BB=B6=E7=AE=A1=E7=90=86=E6=9D=83=E9=99=90=EF=BC=8C=E6=96=B0?= =?UTF-8?q?=E5=A2=9E=EF=BC=8C=E5=88=A0=E9=99=A4=EF=BC=8C=E7=AE=A1=E7=90=86?= =?UTF-8?q?=E7=AD=89=EF=BC=8C=E8=BF=98=E6=9C=89=E8=BD=AF=E4=BB=B6=E6=8E=A7?= =?UTF-8?q?=E5=88=B6=E6=9D=83=E9=99=90=E7=AD=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- LMS.Common/Attributes/NotEmptyAttribute.cs | 22 + LMS.Common/Enum/ResponseCodeEnum.cs | 9 + LMS.Common/Enum/SoftwareControlEnum.cs | 31 ++ LMS.DAO/ApplicationDbContext.cs | 8 + LMS.DAO/UserDAO/UserBasicDAO.cs | 24 +- LMS.Repository/DB/Software.cs | 41 ++ LMS.Repository/DB/SoftwareControl.cs | 45 ++ LMS.Repository/DB/UserSoftware.cs | 22 + .../DTO/Software/SoftwareBasicDto.cs | 13 + .../DTO/Software/SoftwareCollectionDto.cs | 44 ++ .../Software/SoftwareControlCollectionDto.cs | 47 ++ LMS.Repository/DTO/UserDto/UserBaseDto.cs | 7 + LMS.Repository/Software/AddSoftwareModel.cs | 22 + .../ModifySoftwareControlValidityModel.cs | 11 + LMS.Repository/User/LoginResponse.cs | 21 + .../Configuration/ServiceConfiguration.cs | 3 + LMS.service/Controllers/SoftWareController.cs | 228 ++++++++- LMS.service/Controllers/UserController.cs | 24 +- .../Middleware/DynamicPermissionMiddleware.cs | 1 + LMS.service/LMS.service.csproj | 2 +- .../SoftwareService/SoftwareControlService.cs | 442 ++++++++++++++++++ .../SoftwareService/SoftwareService.cs | 320 +++++++++++++ .../Service/UserService/LoginService.cs | 164 ++++++- SQL/v1.0.2/Software.sql | 37 ++ SQL/v1.0.2/SoftwareControl.sql | 38 ++ SQL/v1.0.2/UserSoftware.sql | 32 ++ 26 files changed, 1625 insertions(+), 33 deletions(-) create mode 100644 LMS.Common/Attributes/NotEmptyAttribute.cs create mode 100644 LMS.Common/Enum/SoftwareControlEnum.cs create mode 100644 LMS.Repository/DB/Software.cs create mode 100644 LMS.Repository/DB/SoftwareControl.cs create mode 100644 LMS.Repository/DB/UserSoftware.cs create mode 100644 LMS.Repository/DTO/Software/SoftwareBasicDto.cs create mode 100644 LMS.Repository/DTO/Software/SoftwareCollectionDto.cs create mode 100644 LMS.Repository/DTO/Software/SoftwareControlCollectionDto.cs create mode 100644 LMS.Repository/Software/AddSoftwareModel.cs create mode 100644 LMS.Repository/Software/ModifySoftwareControlValidityModel.cs create mode 100644 LMS.Repository/User/LoginResponse.cs create mode 100644 LMS.service/Service/SoftwareService/SoftwareControlService.cs create mode 100644 LMS.service/Service/SoftwareService/SoftwareService.cs create mode 100644 SQL/v1.0.2/Software.sql create mode 100644 SQL/v1.0.2/SoftwareControl.sql create mode 100644 SQL/v1.0.2/UserSoftware.sql diff --git a/LMS.Common/Attributes/NotEmptyAttribute.cs b/LMS.Common/Attributes/NotEmptyAttribute.cs new file mode 100644 index 0000000..a43fb93 --- /dev/null +++ b/LMS.Common/Attributes/NotEmptyAttribute.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace LMS.Common.Attributes +{ + [AttributeUsage(AttributeTargets.Property)] + public class NotEmptyAttribute : ValidationAttribute + { + protected override ValidationResult IsValid(object value, ValidationContext validationContext) + { + if (value is string str && string.IsNullOrWhiteSpace(str)) + { + return new ValidationResult(ErrorMessage ?? "字段不能为空"); + } + return ValidationResult.Success; + } + } +} diff --git a/LMS.Common/Enum/ResponseCodeEnum.cs b/LMS.Common/Enum/ResponseCodeEnum.cs index 3f04303..a41e529 100644 --- a/LMS.Common/Enum/ResponseCodeEnum.cs +++ b/LMS.Common/Enum/ResponseCodeEnum.cs @@ -77,6 +77,11 @@ namespace LMS.Common.Enums [Description("无效的邀请码")] InvalidAffiliateCode = 1014, + // 无效token + [Result("无效token")] + [Description("无效token")] + InvalidToken = 1015, + #endregion #region Machine 操作失败 @@ -141,6 +146,10 @@ namespace LMS.Common.Enums [Result(ResponseString.InvalidOptions)] [Description(ResponseString.InvalidOptions)] InvalidOptions = 5002, + + [Result(ResponseString.IdDateNotFound)] + [Description(ResponseString.IdDateNotFound)] + IdDateNotFound = 5003, #endregion #region 提示词操作失败 diff --git a/LMS.Common/Enum/SoftwareControlEnum.cs b/LMS.Common/Enum/SoftwareControlEnum.cs new file mode 100644 index 0000000..7a04ac0 --- /dev/null +++ b/LMS.Common/Enum/SoftwareControlEnum.cs @@ -0,0 +1,31 @@ +namespace LMS.Common.Enum +{ + public class SoftwareControlEnum + { + public enum SoftwareControlValidityEnum + { + /// + /// 0 + /// + Zero = 0, + /// + /// 一个月 + /// + Thirty = 30, + /// + /// 三个月 + /// + Ninety = 90, + + /// + /// 六个月 + /// + OneHundredAndEighty = 180, + /// + /// 一年 + /// + ThreeHundredAndSixtyFive = 365, + + } + } +} diff --git a/LMS.DAO/ApplicationDbContext.cs b/LMS.DAO/ApplicationDbContext.cs index 5d8fff3..6c45f11 100644 --- a/LMS.DAO/ApplicationDbContext.cs +++ b/LMS.DAO/ApplicationDbContext.cs @@ -34,6 +34,12 @@ namespace LMS.DAO public DbSet Options { get; set; } + public DbSet Software { get; set; } + + public DbSet UserSoftware { get; set; } + + public DbSet SoftwareControl { get; set; } + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); @@ -47,6 +53,8 @@ namespace LMS.DAO ? new List() // 如果存储的是空字符串或空数组,则返回空列表 : JsonSerializer.Deserialize>(v, (JsonSerializerOptions?)null) ?? new List() ); + modelBuilder.Entity() + .HasKey(us => new { us.UserId, us.SoftwareId }); } } } diff --git a/LMS.DAO/UserDAO/UserBasicDAO.cs b/LMS.DAO/UserDAO/UserBasicDAO.cs index efdcc69..2c18e3a 100644 --- a/LMS.DAO/UserDAO/UserBasicDAO.cs +++ b/LMS.DAO/UserDAO/UserBasicDAO.cs @@ -1,11 +1,5 @@ using LMS.Repository.Models.DB; using Microsoft.AspNetCore.Identity; -using OneOf.Types; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace LMS.DAO.UserDAO { @@ -43,6 +37,24 @@ namespace LMS.DAO.UserDAO bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin") || await _userManager.IsInRoleAsync(user, "Super Admin"); return isAdminOrSuperAdmin; } + + /// + /// 检查用户是不是超级管理员 + /// + /// + /// + /// + public async Task CheckUserIsSuperAdmin(long? userId) + { + if (userId == null) + { + return false; + } + User? user = await _userManager.FindByIdAsync(userId.ToString() ?? "0") ?? throw new Exception("用户不存在"); + + bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin"); + return isSuperAdmin; + } } } diff --git a/LMS.Repository/DB/Software.cs b/LMS.Repository/DB/Software.cs new file mode 100644 index 0000000..733f083 --- /dev/null +++ b/LMS.Repository/DB/Software.cs @@ -0,0 +1,41 @@ +namespace LMS.Repository.DB; + +public class Software +{ + /// + /// ID + /// + public string Id { get; set; } + /// + /// 软件名称 + /// + public string SoftwareName { get; set; } + /// + /// 软件代码 + /// + public string SoftwareCode { get; set; } + /// + /// 创建用户ID + /// + public long CreatedUserId { get; set; } + /// + /// 更新用户ID + /// + public long UpdatedUserId { get; set; } + /// + /// 创建时间 + /// + public DateTime CreatedTime { get; set; } + /// + /// 更新时间 + /// + public DateTime UpdatedTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 是否启用 + /// + public bool IsUse { get; set; } +} diff --git a/LMS.Repository/DB/SoftwareControl.cs b/LMS.Repository/DB/SoftwareControl.cs new file mode 100644 index 0000000..0f584e1 --- /dev/null +++ b/LMS.Repository/DB/SoftwareControl.cs @@ -0,0 +1,45 @@ +namespace LMS.Repository.DB; + +public class SoftwareControl +{ + /// + /// ID + /// + public string Id { get; set; } + /// + /// 用户ID + /// + public long UserId { get; set; } + /// + /// 软件ID + /// + public string SoftwareId { get; set; } + /// + /// 创建用户ID + /// + public long CreatedUserId { get; set; } + /// + /// 创建时间 + /// + public DateTime CreatedTime { get; set; } + /// + /// 更新用户ID + /// + public long UpdatedUserId { get; set; } + /// + /// 更新时间 + /// + public DateTime UpdatedTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 到期时间 + /// + public DateTime? ExpirationTime { get; set; } + /// + /// 是否永久 + /// + public bool IsForever { get; set; } +} diff --git a/LMS.Repository/DB/UserSoftware.cs b/LMS.Repository/DB/UserSoftware.cs new file mode 100644 index 0000000..f6d75ed --- /dev/null +++ b/LMS.Repository/DB/UserSoftware.cs @@ -0,0 +1,22 @@ +namespace LMS.Repository.DB; + +public class UserSoftware +{ + /// + /// 用户ID + /// + public long UserId { get; set; } + /// + /// 软件ID + /// + public string SoftwareId { get; set; } + /// + /// + /// + public string MachineId { get; set; } + + /// + /// 软件控制ID + /// + public string SoftwareControlId { get; set; } +} diff --git a/LMS.Repository/DTO/Software/SoftwareBasicDto.cs b/LMS.Repository/DTO/Software/SoftwareBasicDto.cs new file mode 100644 index 0000000..41c1eb1 --- /dev/null +++ b/LMS.Repository/DTO/Software/SoftwareBasicDto.cs @@ -0,0 +1,13 @@ +namespace LMS.Repository.DTO.Software +{ + public class SoftwareBasicDto + { + public string Id { get; set; } = string.Empty; + + public string SoftwareName { get; set; } = string.Empty; + + public string SoftwareCode { get; set; } = string.Empty; + + public bool IsUse { get; set; } + } +} diff --git a/LMS.Repository/DTO/Software/SoftwareCollectionDto.cs b/LMS.Repository/DTO/Software/SoftwareCollectionDto.cs new file mode 100644 index 0000000..71d81ac --- /dev/null +++ b/LMS.Repository/DTO/Software/SoftwareCollectionDto.cs @@ -0,0 +1,44 @@ +using LMS.Repository.DTO.UserDto; + +namespace LMS.Repository.DTO.Software +{ + public class SoftwareCollectionDto + { + /// + /// ID + /// + public string Id { get; set; } + /// + /// 软件名称 + /// + public string SoftwareName { get; set; } + /// + /// 软件代码 + /// + public string SoftwareCode { get; set; } + /// + /// 创建用户ID + /// + public UserBaseDto CreatedUser { get; set; } + /// + /// 更新用户ID + /// + public UserBaseDto UpdatedUser { get; set; } + /// + /// 创建时间 + /// + public DateTime CreatedTime { get; set; } + /// + /// 更新时间 + /// + public DateTime UpdatedTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 是否启用 + /// + public bool IsUse { get; set; } + } +} diff --git a/LMS.Repository/DTO/Software/SoftwareControlCollectionDto.cs b/LMS.Repository/DTO/Software/SoftwareControlCollectionDto.cs new file mode 100644 index 0000000..d650fb0 --- /dev/null +++ b/LMS.Repository/DTO/Software/SoftwareControlCollectionDto.cs @@ -0,0 +1,47 @@ +using LMS.Repository.DTO.UserDto; +namespace LMS.Repository.DTO.Software +{ + public class SoftwareControlCollectionDto + { + /// + /// ID + /// + public string Id { get; set; } + /// + /// 用户ID + /// + public UserBaseDto User { get; set; } + /// + /// 软件ID + /// + public DB.Software Software { get; set; } + /// + /// 创建用户ID + /// + public UserBaseDto CreatedUser { get; set; } + /// + /// 创建时间 + /// + public DateTime CreatedTime { get; set; } + /// + /// 更新用户ID + /// + public UserBaseDto UpdatedUser { get; set; } + /// + /// 更新时间 + /// + public DateTime UpdatedTime { get; set; } + /// + /// 备注 + /// + public string Remark { get; set; } + /// + /// 到期时间 + /// + public DateTime? ExpirationTime { get; set; } + /// + /// 是否永久 + /// + public bool IsForever { get; set; } + } +} diff --git a/LMS.Repository/DTO/UserDto/UserBaseDto.cs b/LMS.Repository/DTO/UserDto/UserBaseDto.cs index 9bfd3e4..f041919 100644 --- a/LMS.Repository/DTO/UserDto/UserBaseDto.cs +++ b/LMS.Repository/DTO/UserDto/UserBaseDto.cs @@ -15,4 +15,11 @@ public class UserBaseDto public string PhoneNumber { get; set; } = string.Empty; public string Avatar { get; set; } = string.Empty; + + // 添加隐藏联系信息的方法 + public void HideContactInfo() + { + Email = "**********"; + PhoneNumber = "***********"; + } } diff --git a/LMS.Repository/Software/AddSoftwareModel.cs b/LMS.Repository/Software/AddSoftwareModel.cs new file mode 100644 index 0000000..fb626c1 --- /dev/null +++ b/LMS.Repository/Software/AddSoftwareModel.cs @@ -0,0 +1,22 @@ +using LMS.Common.Attributes; +using System.ComponentModel.DataAnnotations; + +namespace LMS.Repository.Software +{ + public class AddSoftwareModel + { + /// + /// 软件名称 + /// + [Required] + [NotEmpty(ErrorMessage = "123")] + public string SoftwareName { get; set; } + + /// + /// 软件代码 + /// + [Required] + [NotEmpty(ErrorMessage = "123")] + public string SoftwareCode { get; set; } + } +} diff --git a/LMS.Repository/Software/ModifySoftwareControlValidityModel.cs b/LMS.Repository/Software/ModifySoftwareControlValidityModel.cs new file mode 100644 index 0000000..596d9cd --- /dev/null +++ b/LMS.Repository/Software/ModifySoftwareControlValidityModel.cs @@ -0,0 +1,11 @@ +using LMS.Common.Enum; + +namespace LMS.Repository.Software +{ + public class ModifySoftwareControlValidityModel + { + public SoftwareControlEnum.SoftwareControlValidityEnum? ExpirationTime { get; set; } + + public bool? IsForever { get; set; } + } +} diff --git a/LMS.Repository/User/LoginResponse.cs b/LMS.Repository/User/LoginResponse.cs new file mode 100644 index 0000000..fcfeafe --- /dev/null +++ b/LMS.Repository/User/LoginResponse.cs @@ -0,0 +1,21 @@ +namespace LMS.Repository.User; +public class LoginResponseBase +{ + + public string Token { get; set; } + public string UserName { get; set; } + public long Id { get; set; } + public string NickName { get; set; } +} + +public class LoginResponse : LoginResponseBase +{ + public string RefreshToken { get; set; } +} + +public class SimpleLoginResponse : LoginResponseBase +{ + public bool IsForver { get; set; } + + public DateTime? ExpirationTime { get; set; } +} diff --git a/LMS.service/Configuration/ServiceConfiguration.cs b/LMS.service/Configuration/ServiceConfiguration.cs index 0b5e103..f965608 100644 --- a/LMS.service/Configuration/ServiceConfiguration.cs +++ b/LMS.service/Configuration/ServiceConfiguration.cs @@ -7,6 +7,7 @@ using LMS.service.Service; using LMS.service.Service.PermissionService; using LMS.service.Service.PromptService; using LMS.service.Service.RoleService; +using LMS.service.Service.SoftwareService; using LMS.service.Service.UserService; namespace Lai_server.Configuration @@ -31,6 +32,8 @@ namespace Lai_server.Configuration services.AddScoped(); services.AddScoped(); services.AddScoped(); + services.AddScoped(); + services.AddScoped(); diff --git a/LMS.service/Controllers/SoftWareController.cs b/LMS.service/Controllers/SoftWareController.cs index 33830ca..df6c314 100644 --- a/LMS.service/Controllers/SoftWareController.cs +++ b/LMS.service/Controllers/SoftWareController.cs @@ -1,14 +1,230 @@ -using Microsoft.AspNetCore.Mvc; +using LMS.Repository.DB; +using LMS.Repository.DTO; +using LMS.Repository.DTO.Software; +using LMS.Repository.Models.User; +using LMS.Repository.Software; +using LMS.Repository.User; +using LMS.service.Service.SoftwareService; +using LMS.service.Service.UserService; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.RazorPages; +using Microsoft.IdentityModel.Tokens; +using System.ComponentModel.DataAnnotations; +using System.IdentityModel.Tokens.Jwt; +using static LMS.Common.Enums.ResponseCodeEnum; namespace LMS.service.Controllers { [Route("lms/[controller]/[action]")] public class SoftWareController : ControllerBase { - //public Index() - //{ - // var version = Assembly.GetExecutingAssembly().GetName().Version; - // Console.WriteLine($"Version: {version}"); - //} + private readonly SoftwareControlService _softwareControlService; + private readonly SoftwareService _softwareService; + + public SoftWareController(SoftwareControlService softwareControlService, SoftwareService softwareService) + { + _softwareControlService = softwareControlService; + _softwareService = softwareService; + } + + #region 软件控制-同步用户的软件控制权限 + /// + /// 同步用户的软件控制权限,用于用户登录后同步用户的软件控制权限 + /// + /// + /// + [HttpPost("{userId}")] + [Authorize] + public async Task>> ApplySoftwareControl(long userId) + { + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareControlService.ApplySoftwareControl(userId, reuqestUserId); + } + + #endregion + + #region 软件控制-设置软件的到期时间和是否永久 + + [HttpPost("{id}")] + [Authorize] + public async Task>> ModifySoftwareControlValidity(string id, [FromBody] ModifySoftwareControlValidityModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + if (model.IsForever == null && model.ExpirationTime == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareControlService.ModifySoftwareControlValidity(id, model, requestUserId); + } + + #endregion + + #region 软件控制-获取软件信息的集合 + + [HttpGet] + [Authorize] + public async Task>>> GetSoftwareControlCollection([Required] int page, [Required] int pageSize, long? userId, string? softwareId, bool? IsForever, string? remark) + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareControlService.GetSoftwareControlCollection(page, pageSize, userId, softwareId, IsForever, remark, requestUserId); + } + + #endregion + + #region 软件控制-删除软件控制数据 + + [HttpDelete("{id}")] + [Authorize] + public async Task>> DeleteSoftwareControl(string id) + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareControlService.DeleteSoftwareControl(id, requestUserId); + } + #endregion + + #region 软件控制-各个软件检测软件控制权限 + + [HttpPost("{userID}/{machineId}/{softwareId}")] + [Authorize] + public async Task>> CheckSoftwareControl(long userID, string machineId, string softwareId) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + // 解析jwt,获取machineID + // 从请求头中获取 JWT + var token = HttpContext.Request.Headers["Authorization"].ToString().Replace("Bearer ", ""); + + // 解析 JWT 并获取 MachineId + var handler = new JwtSecurityTokenHandler(); + var jwtToken = handler.ReadJwtToken(token); + + // 假设 MachineId 存储在 JWT 的声明中 + var machineIdClaim = jwtToken.Claims.FirstOrDefault(claim => claim.Type == "DeviceInfo"); + string parsedMachineId = machineIdClaim?.Value ?? ""; + if (parsedMachineId != machineId || userID != requestUserId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.InvalidToken, "登录失效,请重新登录"); + } + + return await _softwareControlService.CheckSoftwareControl(userID, machineId, softwareId); + } + + #endregion + + #region 软件控制-传入用户可以未申请权限数量 + + [HttpGet("{userId}")] + [Authorize] + public async Task>> GetUserSoftwareControlCount(long userId) + { + long requestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareControlService.GetUserSoftwareControlCount(userId, requestUserId); + } + + #endregion + + + + #region 软件-添加软件数据 + /// + /// 添加软件数据 + /// + /// + /// + [HttpPost] + [Authorize] + public async Task>> AddSoftware([FromBody] AddSoftwareModel software) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.AddSoftware(software, reuqestUserId); + } + #endregion + + #region 软件-修改软件信息 + + /// + /// 修改软件信息 + /// + /// + /// + /// + [HttpPost("{id}")] + [Authorize] + public async Task>> MpdifySoftware(string id, [FromBody] AddSoftwareModel software) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.ModifySoftware(id, software, reuqestUserId); + } + + #endregion + + #region 软件-获取软件列表信息 + /// + /// 获取软件的列表信息,可以使用查询条件 softwareName, softwareCode + /// + /// 页数 + /// 一页多少 + /// 软件名字 + /// 软件编码 + /// + [HttpGet] + [Authorize] + public async Task>>> GetSoftwareCollection([Required] int page, [Required] int pageSize, string? softwareName, string? softwareCode, bool? isUse = true) + { + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.GetSoftwareCollection(page, pageSize, softwareName, softwareCode, reuqestUserId, isUse); + } + #endregion + + #region 软件-获取软件的信息信息,通过ID + + [HttpGet("{id}")] + [Authorize] + public async Task>> GetSoftwareInfoById(string id) + { + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.GetSoftwareInfoById(id, reuqestUserId); + } + #endregion + + #region 软件-删除软件数据 + [HttpDelete("{id}")] + [Authorize] + public async Task>> DeleteSoftware(string id) + { + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.DeleteSoftware(id, reuqestUserId); + } + + #endregion + + #region 软件-获取所有软件的基础信息 + + [HttpGet] + [Authorize] + public async Task>>> GetSoftwareBaseCollection() + { + long reuqestUserId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0); + return await _softwareService.GetSoftwareBaseCollection(reuqestUserId); + } + + #endregion } } diff --git a/LMS.service/Controllers/UserController.cs b/LMS.service/Controllers/UserController.cs index 9965a42..ad6dc1a 100644 --- a/LMS.service/Controllers/UserController.cs +++ b/LMS.service/Controllers/UserController.cs @@ -46,7 +46,7 @@ namespace LMS.service.Controllers #endregion - #region 登录 + #region 登录 会生成刷新token,用户后台的刷新 /// /// 用户登录接口 /// @@ -81,10 +81,30 @@ namespace LMS.service.Controllers SameSite = SameSiteMode.None, Expires = DateTime.UtcNow.AddDays(7), }; - Response.Cookies.Append("refreshToken", ((LoginService.LoginResponse)apiResponse.Data).RefreshToken, cookieOptions); + Response.Cookies.Append("refreshToken", ((LoginResponse)apiResponse.Data).RefreshToken, cookieOptions); return apiResponse; } + #endregion + + #region 简单登录,不生成刷新token,绑定机器码,有效期一天 + + [HttpPost("{softwareId}")] + public async Task>> SimpleLogin(string softwareId, [FromBody] LoginModel model) + { + if (!ModelState.IsValid) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + string? ip = HttpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); + if (string.IsNullOrEmpty(ip)) + { + ip = HttpContext.Connection.RemoteIpAddress?.ToString(); + } + return await _loginService.SimpleLogin(model, softwareId, ip, _keyStore); + } + + #endregion #region 注册 diff --git a/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs b/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs index b224d03..2f798d9 100644 --- a/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs +++ b/LMS.service/Extensions/Middleware/DynamicPermissionMiddleware.cs @@ -59,6 +59,7 @@ namespace LMS.service.Extensions.Middleware context.Items["UserId"] = userId; return Convert.ToInt64(userIdClaim?.Value); } + return 0; } } diff --git a/LMS.service/LMS.service.csproj b/LMS.service/LMS.service.csproj index 83a9e0e..8dc7350 100644 --- a/LMS.service/LMS.service.csproj +++ b/LMS.service/LMS.service.csproj @@ -7,7 +7,7 @@ ed64fb6f-9c93-43d0-b418-61f507f28420 Linux . - 1.0.0 + 1.0.2 diff --git a/LMS.service/Service/SoftwareService/SoftwareControlService.cs b/LMS.service/Service/SoftwareService/SoftwareControlService.cs new file mode 100644 index 0000000..6a3c90f --- /dev/null +++ b/LMS.service/Service/SoftwareService/SoftwareControlService.cs @@ -0,0 +1,442 @@ +using AutoMapper; +using LMS.Common.Enum; +using LMS.DAO; +using LMS.DAO.UserDAO; +using LMS.Repository.DB; +using LMS.Repository.DTO; +using LMS.Repository.DTO.Software; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Software; +using LMS.Tools; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.SoftwareService +{ + public class SoftwareControlService(UserBasicDao userBasicDao, ApplicationDbContext dbContext, IMapper mapper) + { + private readonly UserBasicDao _userBasicDao = userBasicDao; + private readonly ApplicationDbContext _dbContext = dbContext; + private readonly IMapper _mapper = mapper; + + #region 软件控制-同步用户的软件控制权限 + /// + /// 用户自动同步软件控制,和software中的一致,只要没有的数据都同步上,不管isUse,这个会在后面过滤掉 + /// + /// + /// + /// + /// + internal async Task>> ApplySoftwareControl(long id, long requestUserId) + { + using var transaction = _dbContext.Database.BeginTransaction(); + try + { + // 用户ID和请求ID不一致,且请求ID不是管理员或超级管理员,返回无权限操作 + if (id != requestUserId && !await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 检查用户是否存在 + if (!await _userBasicDao.CheckUserExistsByID(id)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + List softwareControl = await _dbContext.SoftwareControl.Where(x => x.UserId == id).ToListAsync(); + List software = await _dbContext.Software.ToListAsync(); + + bool isAdd = false; + + // 判断是不是所有的软件都有权限,如果没有就添加 + foreach (var item in software) + { + if (softwareControl.FirstOrDefault(x => x.SoftwareId == item.Id) == null) + { + SoftwareControl softwareControlModel = new SoftwareControl + { + Id = Guid.NewGuid().ToString(), + UserId = id, + SoftwareId = item.Id, + CreatedUserId = requestUserId, + CreatedTime = BeijingTimeExtension.GetBeijingTime(), + UpdatedUserId = requestUserId, + UpdatedTime = BeijingTimeExtension.GetBeijingTime(), + Remark = string.Empty, + IsForever = false, + ExpirationTime = null, + }; + await _dbContext.SoftwareControl.AddAsync(softwareControlModel); + isAdd = true; + } + + } + // 判断现在的,将再soft中没有的软件控制删除 + foreach (var item1 in softwareControl) + { + if (software.FirstOrDefault(x => x.Id == item1.SoftwareId) == null) + { + _dbContext.SoftwareControl.Remove(item1); + isAdd = true; + } + } + + await _dbContext.SaveChangesAsync(); + await transaction.CommitAsync(); + if (isAdd) + { + return APIResponseModel.CreateSuccessResponseModel("同步所有的软件权限成功"); + } + else + { + return APIResponseModel.CreateSuccessResponseModel("当前已获取所有的软件控制权限,不需要做处理"); + } + } + catch (Exception ex) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + + #region 软件控制-设置软件的到期时间和是否永久 + /// + /// 设置软件的到期时间和是否永久 + /// + /// 软件控制项的ID + /// 传入的数据 + /// 请求的用户ID + /// + public async Task>> ModifySoftwareControlValidity(string id, ModifySoftwareControlValidityModel model, long requestUserId) + { + try + { + if (!await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + if (model.ExpirationTime != null) + { + // 判断model传入的枚举值是不是再对应的enum中是有效的 + if (!Enum.IsDefined(typeof(SoftwareControlEnum.SoftwareControlValidityEnum), model.ExpirationTime)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "传入的到期时间不在有效范围内"); + } + } + + // 判断指定ID的软件控制项是否存在 + SoftwareControl? softwareControl = await _dbContext.SoftwareControl.FirstOrDefaultAsync(x => x.Id == id); + if (softwareControl == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.IdDateNotFound); + } + + // 判断当前数据是不是永久,如果是永久,就不能修改 + if (softwareControl.IsForever && model.IsForever == true) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "当前已是永久,不可再修改"); + } + + // 判断传入的modal的IsForever是不是true,是的话直接设置未永久,不是的话,设置到期时间 + if (model.IsForever == true) + { + softwareControl.IsForever = true; + softwareControl.ExpirationTime = null; + } + else + { + // 这边判断现在的到期时间是不是大于现在的时间,是的话,再到期时间的基础上加上传入的时间,不是的话,直接再现在时间加上传入的时间,如果现在是永久,直接设置到期时间为现在时间加上传入的时间 + if (model.ExpirationTime == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "当前到期时间为空,不可修改"); + } + + if (softwareControl.IsForever == true) + { + softwareControl.ExpirationTime = BeijingTimeExtension.GetBeijingTime().AddDays((double)model.ExpirationTime.Value); + softwareControl.IsForever = false; + } + else + { + + if (softwareControl.ExpirationTime > BeijingTimeExtension.GetBeijingTime()) + { + if (model.ExpirationTime == SoftwareControlEnum.SoftwareControlValidityEnum.Zero) + { + softwareControl.ExpirationTime = BeijingTimeExtension.GetBeijingTime(); + } + else + { + softwareControl.ExpirationTime = softwareControl.ExpirationTime.Value.AddDays((double)model.ExpirationTime.Value); + } + } + else + { + softwareControl.ExpirationTime = BeijingTimeExtension.GetBeijingTime().AddDays((double)model.ExpirationTime.Value); + } + + + } + } + // 设置更新时间和更新人 + softwareControl.UpdatedTime = BeijingTimeExtension.GetBeijingTime(); + softwareControl.UpdatedUserId = requestUserId; + // 更新数据库 + _dbContext.SoftwareControl.Update(softwareControl); + await _dbContext.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel("修改成功"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + #endregion + + #region 软件控制-获取软件信息的集合 + /// + /// 获取软件信息的集合 + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public async Task>>> GetSoftwareControlCollection(int page, int pageSize, long? userId, string? softwareId, bool? isForever, string? remark, long requestUserId) + { + try + { + // 判断权限,如果不是管理员或超级管理员,就判断是不是自己的数据,不是的话,返回无权限操作 + IQueryable query = _dbContext.SoftwareControl.AsQueryable(); + bool isAdminOrSuperAdmin = await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId); + + if (!isAdminOrSuperAdmin && userId != requestUserId) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + if (userId != null) + { + query = query.Where(x => x.UserId == userId); + } + if (softwareId != null) + { + query = query.Where(x => x.SoftwareId == softwareId); + } + if (isForever != null) + { + query = query.Where(x => x.IsForever == isForever); + } + if (!string.IsNullOrWhiteSpace(remark)) + { + query = query.Where(x => x.Remark.Contains(remark)); + } + + + if (!isAdminOrSuperAdmin) + { + // 通过 softwareId 过滤掉isUse为false的数 + List softwareIds = await _dbContext.Software.Where(x => x.IsUse == true).Select(x => x.Id).ToListAsync(); + query = query.Where(x => softwareIds.Contains(x.SoftwareId)); + } + + // 通过ID降序 + query = query.OrderByDescending(x => x.CreatedTime); + + // 查询总数 + int total = await query.CountAsync(); + + // 分页 + query = query.Skip((page - 1) * pageSize).Take(pageSize); + + List softwareControls = await query.ToListAsync(); + List softwareControlCollectionDtos = new List(); + + foreach (var item in softwareControls) + { + SoftwareControlCollectionDto softwareControlCollectionDto = new SoftwareControlCollectionDto + { + Id = item.Id, + IsForever = item.IsForever, + ExpirationTime = item.ExpirationTime, + Remark = item.Remark, + CreatedTime = item.CreatedTime, + UpdatedTime = item.UpdatedTime, + }; + Software? software = await _dbContext.Software.FirstOrDefaultAsync(x => x.Id == item.SoftwareId); + if (software != null) + { + softwareControlCollectionDto.Software = software; + } + User? user = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == item.UserId); + softwareControlCollectionDto.User = _mapper.Map(user); + softwareControlCollectionDto.User.HideContactInfo(); + + User? createdUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == item.CreatedUserId); + softwareControlCollectionDto.CreatedUser = _mapper.Map(createdUser); + softwareControlCollectionDto.CreatedUser.HideContactInfo(); + + User? updatedUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == item.UpdatedUserId); + softwareControlCollectionDto.UpdatedUser = _mapper.Map(updatedUser); + softwareControlCollectionDto.UpdatedUser.HideContactInfo(); + + softwareControlCollectionDtos.Add(softwareControlCollectionDto); + } + return APIResponseModel>.CreateSuccessResponseModel(new CollectionResponse + { + Total = total, + Collection = softwareControlCollectionDtos, + Current = page, + }); + } + catch (Exception ex) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + + + } + #endregion + + #region 软件控制-删除软件控制数据 + + /// + /// 删除软件控制数据 + /// + /// + /// + /// + public async Task>> DeleteSoftwareControl(string id, long requestUserId) + { + try + { + if (!await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + SoftwareControl? softwareControl = await _dbContext.SoftwareControl.FirstOrDefaultAsync(x => x.Id == id); + if (softwareControl == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.IdDateNotFound); + } + + _dbContext.SoftwareControl.Remove(softwareControl); + await _dbContext.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel("删除成功"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + + #region 软件检查权限以及登录状态 + /// + /// 检查软件控制权限 以及登录状态 + /// + /// + /// + /// + /// + public async Task>> CheckSoftwareControl(long userID, string machineId, string softwareId) + { + try + { + // 判断软件是不是存在 + if (!await _dbContext.Software.AnyAsync(x => x.Id == softwareId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.IdDateNotFound); + } + + // 判断是不是有关联信息,没有的话就是没有登录,或者是机器码不一致,再其他地方登录过了 + UserSoftware? userSoftware = await _dbContext.UserSoftware.FirstOrDefaultAsync(x => x.UserId == userID && x.MachineId == machineId); + if (userSoftware == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction, "无用户登录信息或登录信息不正确,请确认是不是登录多次,请重新登录"); + } + + // 判断是不是有权限 + SoftwareControl? softwareControl = await _dbContext.SoftwareControl.FirstOrDefaultAsync(x => x.UserId == userID && x.SoftwareId == softwareId); + if (softwareControl == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction, "无权限操作,请先申请权限"); + } + + // 判断是不是还能用 + if (softwareControl.IsForever == true || softwareControl.ExpirationTime > BeijingTimeExtension.GetBeijingTime()) + { + // 可用 + return APIResponseModel.CreateSuccessResponseModel("1"); + } + else + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction, "权限已过期,请重新申请"); + } + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + + #region 软件控制-用户可申请的软件控制数量 + /// + /// 用户可申请的软件控制数量 + /// + /// + /// + /// + /// + public async Task>> GetUserSoftwareControlCount(long userId, long requestUserId) + { + try + { + bool isAdminOrSuperAdmin = await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId); + if (!isAdminOrSuperAdmin && userId != requestUserId) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + User? user = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == userId); + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByIdFail); + } + + List software = await _dbContext.Software.Where(x => x.IsUse == true).ToListAsync(); + + List softwareControls = await _dbContext.SoftwareControl.Where(x => x.UserId == userId).ToListAsync(); + // 判断software中的softwareId是否在softwareControl中,不在的话,不在的话计数 + int count = 0; + foreach (var item in software) + { + if (softwareControls.FirstOrDefault(x => x.SoftwareId == item.Id) == null) + { + count++; + } + } + return APIResponseModel.CreateSuccessResponseModel(count); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + } +} diff --git a/LMS.service/Service/SoftwareService/SoftwareService.cs b/LMS.service/Service/SoftwareService/SoftwareService.cs new file mode 100644 index 0000000..72b6ace --- /dev/null +++ b/LMS.service/Service/SoftwareService/SoftwareService.cs @@ -0,0 +1,320 @@ +using AutoMapper; +using LMS.DAO; +using LMS.DAO.UserDAO; +using LMS.Repository.DB; +using LMS.Repository.DTO; +using LMS.Repository.DTO.Software; +using LMS.Repository.DTO.UserDto; +using LMS.Repository.Models.DB; +using LMS.Repository.Software; +using LMS.Tools.Extensions; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using static LMS.Common.Enums.ResponseCodeEnum; + +namespace LMS.service.Service.SoftwareService +{ + public class SoftwareService(UserBasicDao userBasicDao, ApplicationDbContext dbContext, IMapper mapper) + { + private readonly UserBasicDao _userBasicDao = userBasicDao; + private readonly ApplicationDbContext _dbContext = dbContext; + private readonly IMapper _mapper = mapper; + + #region 添加软件数据 + /// + /// 添加软件数据 + /// + /// + /// + /// + public async Task>> AddSoftware(AddSoftwareModel software, long reuqestUserId) + { + try + { + // 判断用户是不是超级管理员 + if (!(await _userBasicDao.CheckUserIsSuperAdmin(reuqestUserId))) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + // 判断软件名字软件代码是不是存在,存在的话,报错 + bool isSoftwareExists = await _dbContext.Software.AnyAsync(x => x.SoftwareName == software.SoftwareName || x.SoftwareCode == software.SoftwareCode); + if (isSoftwareExists) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "软件名称或软件编码已存在,请检查"); + } + + // 开始添加软件 + Software software1 = new Software + { + Id = Guid.NewGuid().ToString(), + SoftwareName = software.SoftwareName, + SoftwareCode = software.SoftwareCode, + CreatedUserId = reuqestUserId, + UpdatedUserId = reuqestUserId, + CreatedTime = BeijingTimeExtension.GetBeijingTime(), + UpdatedTime = BeijingTimeExtension.GetBeijingTime(), + Remark = string.Empty, + IsUse = true + }; + await _dbContext.Software.AddAsync(software1); + await _dbContext.SaveChangesAsync(); + // Add software + return APIResponseModel.CreateSuccessResponseModel("Add software successfully"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + #endregion + + #region 修改软件信息 + /// + /// 修改软件信息 + /// + /// + /// + /// + /// + /// + public async Task>> ModifySoftware(string id, AddSoftwareModel software, long reuqestUserId) + { + try + { + // 判断用户是不是超级管理员 + if (!(await _userBasicDao.CheckUserIsSuperAdmin(reuqestUserId))) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + // 判断传入的ID是不是存在 + Software? software2 = await _dbContext.Software.FirstOrDefaultAsync(x => x.Id == id); + if (software2 == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "Software does not exist"); + } + // 判断软件名字软件代码是不是存在,不包含当前的,存在的话,报错 + bool isSoftwareExists = await _dbContext.Software.AnyAsync(x => (x.SoftwareName == software.SoftwareName || x.SoftwareCode == software.SoftwareCode) && x.Id != id); + if (isSoftwareExists) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "软件名称或软件编码已存在,请检查"); + } + software2.SoftwareName = software.SoftwareName; + software2.SoftwareCode = software.SoftwareCode; + software2.UpdatedUserId = reuqestUserId; + software2.UpdatedTime = BeijingTimeExtension.GetBeijingTime(); + _dbContext.Update(software2); + await _dbContext.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel("Modify software successfully"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + #endregion + + #region 获取软件列表信息 + + /// + /// 获取软件信息的列表,可以使用查询条件 softwareName, softwareCode + /// + /// + /// + /// + /// + /// + /// + public async Task>>> GetSoftwareCollection(int page, int pageSize, string? softwareName, string? softwareCode, long reuqestUserId, bool? isUse) + { + try + { + if (!await _userBasicDao.CheckUserIsSuperAdmin(reuqestUserId)) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + IQueryable query = _dbContext.Software.AsQueryable(); + if (!string.IsNullOrWhiteSpace(softwareName)) + { + query = query.Where(x => x.SoftwareName.Contains(softwareName)); + } + if (!string.IsNullOrWhiteSpace(softwareCode)) + { + query = query.Where(x => x.SoftwareCode.Contains(softwareCode)); + } + if (isUse != null) + { + query = query.Where(x => x.IsUse == isUse); + } + // 按照创建时间排序 + query = query.OrderByDescending(x => x.CreatedTime); + // 计算总数 + int count = await query.CountAsync(); + // 分页 + List softwareList = await query.Skip((page - 1) * pageSize).Take(pageSize).ToListAsync(); + + List softwareCollectionDtos = []; + for (int i = 0; i < softwareList.Count; i++) + { + + SoftwareCollectionDto softwareCollectionDto = new() + { + Id = softwareList[i].Id, + SoftwareName = softwareList[i].SoftwareName, + SoftwareCode = softwareList[i].SoftwareCode, + CreatedTime = softwareList[i].CreatedTime, + UpdatedTime = softwareList[i].UpdatedTime, + Remark = softwareList[i].Remark, + IsUse = softwareList[i].IsUse + }; + + User? createdUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == softwareList[i].CreatedUserId); + softwareCollectionDto.CreatedUser = _mapper.Map(createdUser); + softwareCollectionDto.CreatedUser.HideContactInfo(); + User? updatedUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == softwareList[i].UpdatedUserId); + softwareCollectionDto.UpdatedUser = _mapper.Map(updatedUser); + softwareCollectionDto.UpdatedUser.HideContactInfo(); + softwareCollectionDtos.Add(softwareCollectionDto); + } + return APIResponseModel>.CreateSuccessResponseModel(new CollectionResponse + { + Total = count, + Collection = softwareCollectionDtos, + Current = page + }); + } + catch (Exception ex) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + #endregion + + #region 获取软件信息,通用ID + /// + /// 获取软件信息,通过ID + /// + /// + /// + /// + public async Task>> GetSoftwareInfoById(string id, long reuqestUserId) + { + try + { + if (!await _userBasicDao.CheckUserIsSuperAdmin(reuqestUserId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + Software? software = await _dbContext.Software.FirstOrDefaultAsync(x => x.Id == id); + if (software == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "Software does not exist"); + } + + SoftwareCollectionDto softwareCollection = new SoftwareCollectionDto + { + Id = software.Id, + SoftwareName = software.SoftwareName, + SoftwareCode = software.SoftwareCode, + CreatedTime = software.CreatedTime, + UpdatedTime = software.UpdatedTime, + Remark = software.Remark, + IsUse = software.IsUse + }; + + User? createdUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == software.CreatedUserId); + softwareCollection.CreatedUser = _mapper.Map(createdUser); + User? updatedUser = await _dbContext.Users.FirstOrDefaultAsync(x => x.Id == software.UpdatedUserId); + softwareCollection.UpdatedUser = _mapper.Map(updatedUser); + return APIResponseModel.CreateSuccessResponseModel(softwareCollection); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + + #region 删除软件数据 + + /// + /// 删除软件,通过软件ID + /// + /// + /// + /// + public async Task>> DeleteSoftware(string id, long reuqestUserId) + { + try + { + if (!await _userBasicDao.CheckUserIsSuperAdmin(reuqestUserId)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction); + } + + Software? software = await _dbContext.Software.FirstOrDefaultAsync(x => x.Id == id); + if (software == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "Software does not exist"); + } + software.IsUse = false; + _dbContext.Software.Update(software); + await _dbContext.SaveChangesAsync(); + return APIResponseModel.CreateSuccessResponseModel("soft Delete software successfully"); + + + //Software? software = await _dbContext.Software.FirstOrDefaultAsync(x => x.Id == id); + //if (software == null) + //{ + // return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError, "Software does not exist"); + //} + //// 开始删除软件 + //_dbContext.Software.Remove(software); + + ////TODO: 删除软件相关的数据(用户软件关联表,软件控制表) + //await _dbContext.SaveChangesAsync(); + //return APIResponseModel.CreateSuccessResponseModel("Delete software successfully"); + } + catch (Exception ex) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + + #endregion + + /// + /// 获取所有软件的基础信息 + /// + /// + /// + /// + public async Task>>> GetSoftwareBaseCollection(long reuqestUserId) + { + try + { + List softwares = await _dbContext.Software.ToListAsync(); + List softwareBasicDtos = []; + for (int i = 0; i < softwares.Count; i++) + { + SoftwareBasicDto softwareBasicDto = new SoftwareBasicDto + { + Id = softwares[i].Id, + SoftwareName = softwares[i].SoftwareName, + SoftwareCode = softwares[i].SoftwareCode, + IsUse = softwares[i].IsUse + }; + softwareBasicDtos.Add(softwareBasicDto); + } + return APIResponseModel>.CreateSuccessResponseModel(softwareBasicDtos); + } + catch (Exception ex) + { + return APIResponseModel>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message); + } + } + } +} diff --git a/LMS.service/Service/UserService/LoginService.cs b/LMS.service/Service/UserService/LoginService.cs index 87afe1b..4443374 100644 --- a/LMS.service/Service/UserService/LoginService.cs +++ b/LMS.service/Service/UserService/LoginService.cs @@ -1,9 +1,10 @@  using LMS.Common.RSAKey; using LMS.DAO; +using LMS.Repository.DB; using LMS.Repository.Models.DB; using LMS.Repository.Models.User; -using LMS.service.Data; +using LMS.Repository.User; using LMS.Tools.Extensions; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; @@ -12,7 +13,6 @@ using Microsoft.IdentityModel.Tokens; using System.Collections.Concurrent; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; -using System.Security.Cryptography.Xml; using System.Text; using static LMS.Common.Enums.ResponseCodeEnum; @@ -31,28 +31,26 @@ namespace LMS.service.Service.UserService _securityService = securityService; } - public class LoginResponse - { - public string Token { get; set; } - public string UserName { get; set; } - public long Id { get; set; } - public string NickName { get; set; } - public string RefreshToken { get; set; } - } - + #region 生成JWT /// /// 生成JWT /// /// /// /// - public string GenerateJWT(User user) + public string GenerateJWT(User user, int hours = 6, string deviceInfo = "") { - var claims = new List(); - claims.Add(new Claim(ClaimTypes.NameIdentifier, user.Id.ToString())); - claims.Add(new Claim(ClaimTypes.Name, user.UserName)); - // 获取角色(目前只有管理员) - claims.Add(new Claim(ClaimTypes.Role, "AdminUser")); + var claims = new List + { + new(ClaimTypes.NameIdentifier, user.Id.ToString()), + new(ClaimTypes.Name, user.UserName), + // 获取角色(目前只有管理员) + new(ClaimTypes.Role, "AdminUser") + }; + if (!string.IsNullOrEmpty(deviceInfo)) + { + claims.Add(new Claim("DeviceInfo", deviceInfo)); + } //foreach (string role in roles) //{ // claims.Add(new Claim(ClaimTypes.Role, role)); @@ -67,7 +65,7 @@ namespace LMS.service.Service.UserService //设置Token的过期时间 - DateTime expires = DateTime.Now.AddHours(6); + DateTime expires = DateTime.Now.AddHours(hours); byte[] secBytes = Encoding.UTF8.GetBytes(key); var secKey = new SymmetricSecurityKey(secBytes); @@ -81,6 +79,9 @@ namespace LMS.service.Service.UserService return jwt; } + #endregion + + #region 用户登录,生成刷新token /// /// 用户登录的服务层方法 /// @@ -172,6 +173,9 @@ namespace LMS.service.Service.UserService } } + #endregion + + #region 刷新token的方法 /// /// 刷新token的方法 /// @@ -208,6 +212,8 @@ namespace LMS.service.Service.UserService } } + #endregion + #region 将用户设置为代理 /// /// 将用户设置为代理,要做一系列检查 @@ -273,6 +279,128 @@ namespace LMS.service.Service.UserService return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); } } + + #endregion + + //TODO 未测试 + #region 用户登录的服务层方法,简化版,只是登录,不生成刷新token + /// + /// 用户登录的服务层方法,简化版,只是登录,不生成刷新token + /// + /// + /// + /// + /// + public async Task>> SimpleLogin(LoginModel model, string softwareId, string ip, ConcurrentDictionary _keyStore) + { + using var transaction = await _context.Database.BeginTransactionAsync(); + try + { + if (string.IsNullOrWhiteSpace(model.DeviceInfo)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.ParameterError); + } + if (!_keyStore.TryRemove(model.TokenId, out var keyInfo)) // 没有找到对应的公钥 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + if (BeijingTimeExtension.GetBeijingTime() > keyInfo.Expiry) // 公钥超时 + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + var rsaKeyId = keyInfo.Key; + var privateKey = _securityService.DecryptWithAES(rsaKeyId); + // 用户密码解密 + string decryptedPassword = RsaKeyPairGenerator.Decrypt(privateKey, model.Password); + + if (model.UserName == null) + { + throw new ArgumentNullException("UserName"); + } + + User? user = await _userManager.FindByNameAsync(model.UserName); + + if (user == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.FindUserByNameFail); + } + + if (await _userManager.IsLockedOutAsync(user)) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserIsLockedOut); + } + + var result = await _userManager.CheckPasswordAsync(user, decryptedPassword); + if (!result) + { + // 失败计数器加1 + await _userManager.AccessFailedAsync(user); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.UserPasswordFail); + } + + // 密码正确,确认权限 + // 检查当前用户是不是有软件控制权限 + Software? software = await _context.Software.FirstOrDefaultAsync(s => s.Id == softwareId); + if (software == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.IdDateNotFound, "没有找到指定的软件信息,请联系管理员"); + } + SoftwareControl? softwareControl = await _context.SoftwareControl.FirstOrDefaultAsync(sc => sc.UserId == user.Id && sc.SoftwareId == softwareId); + if (softwareControl == null) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction, "当前用户没有软件控制权限,请联系管理员"); + } + // 不是永久,需要判断是不是到期 + if (softwareControl.IsForever == false && softwareControl.ExpirationTime <= BeijingTimeExtension.GetBeijingTime()) + { + return APIResponseModel.CreateErrorResponseModel(ResponseCode.NotPermissionAction, "当前用户的软件权限已到期,请联系管理员"); + } + + UserSoftware? userSoftware = await _context.UserSoftware.FirstOrDefaultAsync(us => us.UserId == user.Id && us.SoftwareId == softwareId); + if (userSoftware == null) + { + // 新建 + userSoftware = new UserSoftware() + { + UserId = user.Id, + SoftwareId = softwareId, + MachineId = model.DeviceInfo, + SoftwareControlId = softwareControl.Id + }; + await _context.UserSoftware.AddAsync(userSoftware); + } + else + { + // 修改 + userSoftware.MachineId = model.DeviceInfo; + _context.Update(userSoftware); + await _context.SaveChangesAsync(); + } + + // 密码正确,返回token + string jwt = GenerateJWT(user, 24, model.DeviceInfo); + + await _context.SaveChangesAsync(); + await transaction.CommitAsync(); + + + return APIResponseModel.CreateSuccessResponseModel(new SimpleLoginResponse() + { + Token = jwt, + UserName = user.UserName ?? "", + Id = user.Id, + NickName = user.NickName + }); + } + catch (Exception e) + { + await transaction.RollbackAsync(); + return APIResponseModel.CreateErrorResponseModel(ResponseCode.SystemError, e.Message); + } + } + #endregion } } diff --git a/SQL/v1.0.2/Software.sql b/SQL/v1.0.2/Software.sql new file mode 100644 index 0000000..ad17a36 --- /dev/null +++ b/SQL/v1.0.2/Software.sql @@ -0,0 +1,37 @@ +/* + Navicat Premium Dump SQL + + Source Server : 亿速云(国内) + Source Server Type : MySQL + Source Server Version : 80018 (8.0.18) + Source Host : yisurds-66dc0b453c05d4.rds.ysydb1.com:14080 + Source Schema : LMS_TEST + + Target Server Type : MySQL + Target Server Version : 80018 (8.0.18) + File Encoding : 65001 + + Date: 27/12/2024 21:36:00 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for Software +-- ---------------------------- +DROP TABLE IF EXISTS `Software`; +CREATE TABLE `Software` ( + `Id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'ID', + `SoftwareName` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '软件名称', + `SoftwareCode` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '软件代码', + `CreatedUserId` bigint(20) NULL DEFAULT NULL COMMENT '创建用户ID', + `UpdatedUserId` bigint(20) NULL DEFAULT NULL COMMENT '更新用户ID', + `CreatedTime` datetime NULL DEFAULT NULL COMMENT '创建时间', + `UpdatedTime` datetime NULL DEFAULT NULL COMMENT '更新时间', + `Remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `IsUse` tinyint(1) NULL DEFAULT NULL COMMENT '是否启用', + PRIMARY KEY (`Id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/SQL/v1.0.2/SoftwareControl.sql b/SQL/v1.0.2/SoftwareControl.sql new file mode 100644 index 0000000..62cb6d2 --- /dev/null +++ b/SQL/v1.0.2/SoftwareControl.sql @@ -0,0 +1,38 @@ +/* + Navicat Premium Dump SQL + + Source Server : 亿速云(国内) + Source Server Type : MySQL + Source Server Version : 80018 (8.0.18) + Source Host : yisurds-66dc0b453c05d4.rds.ysydb1.com:14080 + Source Schema : LMS_TEST + + Target Server Type : MySQL + Target Server Version : 80018 (8.0.18) + File Encoding : 65001 + + Date: 27/12/2024 21:36:10 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for SoftwareControl +-- ---------------------------- +DROP TABLE IF EXISTS `SoftwareControl`; +CREATE TABLE `SoftwareControl` ( + `Id` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT 'ID', + `UserId` bigint(20) NOT NULL COMMENT '用户ID', + `SoftwareId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '软件ID', + `CreatedUserId` bigint(20) NULL DEFAULT NULL COMMENT '创建用户ID', + `CreatedTime` datetime NULL DEFAULT NULL COMMENT '创建时间', + `UpdatedUserId` bigint(20) NULL DEFAULT NULL COMMENT '更新用户ID', + `UpdatedTime` datetime NULL DEFAULT NULL COMMENT '更新时间', + `Remark` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NULL DEFAULT NULL COMMENT '备注', + `ExpirationTime` datetime NULL DEFAULT NULL COMMENT '到期时间', + `IsForever` tinyint(1) NULL DEFAULT NULL COMMENT '是否永久', + PRIMARY KEY (`Id`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1; diff --git a/SQL/v1.0.2/UserSoftware.sql b/SQL/v1.0.2/UserSoftware.sql new file mode 100644 index 0000000..01fc9d8 --- /dev/null +++ b/SQL/v1.0.2/UserSoftware.sql @@ -0,0 +1,32 @@ +/* + Navicat Premium Dump SQL + + Source Server : 亿速云(国内) + Source Server Type : MySQL + Source Server Version : 80018 (8.0.18) + Source Host : yisurds-66dc0b453c05d4.rds.ysydb1.com:14080 + Source Schema : LMS_TEST + + Target Server Type : MySQL + Target Server Version : 80018 (8.0.18) + File Encoding : 65001 + + Date: 27/12/2024 21:36:16 +*/ + +SET NAMES utf8mb4; +SET FOREIGN_KEY_CHECKS = 0; + +-- ---------------------------- +-- Table structure for UserSoftware +-- ---------------------------- +DROP TABLE IF EXISTS `UserSoftware`; +CREATE TABLE `UserSoftware` ( + `UserId` bigint(20) NOT NULL COMMENT '用户ID', + `SoftwareId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '软件ID', + `MachineId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '机器码ID', + `SoftwareControlId` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_0900_ai_ci NOT NULL COMMENT '软件控制ID', + PRIMARY KEY (`UserId`, `SoftwareId`) USING BTREE +) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_0900_ai_ci ROW_FORMAT = Dynamic; + +SET FOREIGN_KEY_CHECKS = 1;