V1.0.4
1. 新增用户注册需要邮箱验证码 2. 机器码、软件权限控制、用户 隔离,除非超级管理员,其他用户只能看到自己下面的用户,管理员可以看到除超级管理员以外的所有
This commit is contained in:
parent
1c5b9ed3c8
commit
57402e0dda
@ -5,12 +5,47 @@ namespace LMS.Common.Dictionary;
|
||||
|
||||
public class AllOptions
|
||||
{
|
||||
|
||||
public static class AllOptionKey
|
||||
{
|
||||
/// <summary>
|
||||
/// 获取所有的 Option
|
||||
/// </summary>
|
||||
public const string All = "all";
|
||||
|
||||
/// <summary>
|
||||
/// 获取TTS相关的 Option
|
||||
/// </summary>
|
||||
public const string TTS = "tts";
|
||||
|
||||
/// <summary>
|
||||
/// 获取软件相关的 Option
|
||||
/// </summary>
|
||||
public const string Software = "software";
|
||||
|
||||
/// <summary>
|
||||
/// 软件试用相关 Option
|
||||
/// </summary>
|
||||
public const string Trial = "trial";
|
||||
|
||||
/// <summary>
|
||||
/// 出图相关的 Option
|
||||
/// </summary>
|
||||
public const string Image = "image";
|
||||
|
||||
/// <summary>
|
||||
/// 邮件设置相关 Option
|
||||
/// </summary>
|
||||
public const string MailSetting = "mailSetting";
|
||||
}
|
||||
|
||||
public static readonly Dictionary<string, List<string>> AllOptionsRequestQuery = new()
|
||||
{
|
||||
{ "all", [] },
|
||||
{ "tts", ["EdgeTTsRoles"] },
|
||||
{ "software", ["LaitoolHomePage", "LaitoolNotice", "LaitoolUpdateContent","LaitoolVersion"]},
|
||||
{ "trial" , ["LaiToolTrialDays"] },
|
||||
{ "image", [OptionKeyName.LaitoolFluxApiModelList] }
|
||||
{ AllOptionKey.All, [] },
|
||||
{ AllOptionKey.TTS, ["EdgeTTsRoles"] },
|
||||
{ AllOptionKey.Software, ["LaitoolHomePage", "LaitoolNotice", "LaitoolUpdateContent","LaitoolVersion"]},
|
||||
{ AllOptionKey.Trial , ["LaiToolTrialDays"] },
|
||||
{ AllOptionKey.Image, [OptionKeyName.LaitoolFluxApiModelList] },
|
||||
{ AllOptionKey.MailSetting , [OptionKeyName.SMTPMailSetting] }
|
||||
};
|
||||
}
|
||||
|
||||
@ -4,10 +4,35 @@ namespace LMS.Common.Dictionary;
|
||||
|
||||
public class SimpleOptions
|
||||
{
|
||||
|
||||
public static class SimpleOptionKey
|
||||
{
|
||||
/// <summary>
|
||||
/// TTS的角色Option
|
||||
/// </summary>
|
||||
public const string Ttsrole = "ttsrole";
|
||||
|
||||
/// <summary>
|
||||
/// LaiTool信息相关的配置
|
||||
/// </summary>
|
||||
public const string Laitoolinfo = "laitoolinfo";
|
||||
|
||||
/// <summary>
|
||||
/// LaiTool FluxAPI对应的模型信息
|
||||
/// </summary>
|
||||
public const string LaitoolFluxApiModelList = "LaitoolFluxApiModelList";
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启邮箱信息
|
||||
/// </summary>
|
||||
public const string EnableMailService = "EnableMailService";
|
||||
}
|
||||
|
||||
public static readonly Dictionary<string, List<string>> SimpleOptionsRequestQuery = new()
|
||||
{
|
||||
{ "ttsrole", ["EdgeTTsRoles"] },
|
||||
{ "laitoolinfo", ["LaitoolHomePage", "LaitoolNotice", "LaitoolUpdateContent", "LaitoolVersion"] },
|
||||
{ OptionKeyName.LaitoolFluxApiModelList, [OptionKeyName.LaitoolFluxApiModelList] }
|
||||
{ SimpleOptionKey.Ttsrole, ["EdgeTTsRoles"] },
|
||||
{ SimpleOptionKey.Laitoolinfo, ["LaitoolHomePage", "LaitoolNotice", "LaitoolUpdateContent", "LaitoolVersion"] },
|
||||
{ SimpleOptionKey.LaitoolFluxApiModelList, [OptionKeyName.LaitoolFluxApiModelList] },
|
||||
{ SimpleOptionKey.EnableMailService, [OptionKeyName.EnableMailService]}
|
||||
};
|
||||
}
|
||||
@ -5,6 +5,7 @@ public enum OptionTypeEnum
|
||||
String = 1,
|
||||
JSON = 2,
|
||||
Number = 3,
|
||||
Boolean = 4
|
||||
}
|
||||
|
||||
public static class OptionKeyName
|
||||
@ -13,4 +14,14 @@ public static class OptionKeyName
|
||||
/// LaiTool Flux API 模型列表的Option Key
|
||||
/// </summary>
|
||||
public const string LaitoolFluxApiModelList = "LaitoolFluxApiModelList";
|
||||
|
||||
/// <summary>
|
||||
/// SMTP的邮件设置
|
||||
/// </summary>
|
||||
public const string SMTPMailSetting = "SMTPMailSetting";
|
||||
|
||||
/// <summary>
|
||||
/// 是否开启邮箱服务
|
||||
/// </summary>
|
||||
public const string EnableMailService = "EnableMailService";
|
||||
}
|
||||
|
||||
@ -6,4 +6,8 @@
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MailKit" Version="4.11.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
37
LMS.Common/Templates/EmailTemplateService.cs
Normal file
37
LMS.Common/Templates/EmailTemplateService.cs
Normal file
@ -0,0 +1,37 @@
|
||||
namespace LMS.Common.Templates
|
||||
{
|
||||
public class EmailTemplateService()
|
||||
{
|
||||
/// <summary>
|
||||
/// 注册邮件模板
|
||||
/// </summary>
|
||||
public const string RegisterHtmlTemplates = """
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<body>
|
||||
<h1>LMS Registration Code</h1>
|
||||
<p>Hi there,</p>
|
||||
<p>Your code is <strong>{RegisterCode}</strong></p>
|
||||
<p>The code is valid for 10 minutes, if it is not you, please ignore it.</p>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 替换模板的占位符
|
||||
/// </summary>
|
||||
/// <param name="template"></param>
|
||||
/// <param name="parameters"></param>
|
||||
/// <returns></returns>
|
||||
public static string ReplaceTemplate(string template, Dictionary<string, string> parameters)
|
||||
{
|
||||
// 替换占位符
|
||||
foreach (var param in parameters)
|
||||
{
|
||||
template = template.Replace($"{{{param.Key}}}", param.Value);
|
||||
}
|
||||
return template;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,6 +4,7 @@ using LMS.Repository.DB;
|
||||
using LMS.Repository.Models.DB;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.ChangeTracking;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace LMS.DAO
|
||||
@ -52,6 +53,15 @@ namespace LMS.DAO
|
||||
v => string.IsNullOrEmpty(v) || v == "[]"
|
||||
? new List<string>() // 如果存储的是空字符串或空数组,则返回空列表
|
||||
: JsonSerializer.Deserialize<List<string>>(v, (JsonSerializerOptions?)null) ?? new List<string>()
|
||||
).Metadata.SetValueComparer(
|
||||
new ValueComparer<List<string>>(
|
||||
// 比较两个集合是否相等
|
||||
(c1, c2) => c1 != null && c2 != null && c1.SequenceEqual(c2),
|
||||
// 计算集合的哈希码 - 这里修复了问题
|
||||
c => c == null ? 0 : c.Aggregate(0, (a, v) => HashCode.Combine(a, v != null ? v.GetHashCode() : 0)),
|
||||
// 创建集合的副本
|
||||
c => c == null ? null : c.ToList()
|
||||
)
|
||||
);
|
||||
modelBuilder.Entity<UserSoftware>()
|
||||
.HasKey(us => new { us.UserId, us.SoftwareId });
|
||||
|
||||
@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||
<PackageReference Include="OneOf" Version="3.0.271" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -55,6 +55,42 @@ namespace LMS.DAO.UserDAO
|
||||
bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin");
|
||||
return isSuperAdmin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查用户是不是管理员
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public async Task<bool> CheckUserIsAdmin(long? userId)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
User? user = await _userManager.FindByIdAsync(userId.ToString() ?? "0") ?? throw new Exception("用户不存在");
|
||||
|
||||
bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Admin");
|
||||
return isSuperAdmin;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查用户是不是代理
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public async Task<bool> CheckUserIsAgent(long? userId)
|
||||
{
|
||||
if (userId == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
User? user = await _userManager.FindByIdAsync(userId.ToString() ?? "0") ?? throw new Exception("用户不存在");
|
||||
|
||||
bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Agent User");
|
||||
return isSuperAdmin;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -6,7 +6,8 @@ namespace LMS.Repository.Models.User
|
||||
{
|
||||
[Required]
|
||||
public required string UserName { get; set; }
|
||||
public string? Email { get; set; }
|
||||
[Required]
|
||||
public required string Email { get; set; }
|
||||
[Required]
|
||||
public required string Password { get; set; }
|
||||
[Required]
|
||||
@ -14,5 +15,7 @@ namespace LMS.Repository.Models.User
|
||||
|
||||
[Required]
|
||||
public required string AffiliateCode { get; set; }
|
||||
|
||||
public string? VerificationCode { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
29
LMS.service/Configuration/AddLoggerConfig.cs
Normal file
29
LMS.service/Configuration/AddLoggerConfig.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using Serilog;
|
||||
|
||||
namespace LMS.service.Configuration
|
||||
{
|
||||
public static class AddLoggerConfig
|
||||
{
|
||||
public static void AddLoggerService(this IServiceCollection services)
|
||||
{
|
||||
// 确保logs目录存在
|
||||
Directory.CreateDirectory("logs");
|
||||
// 加载配置
|
||||
var configuration = new ConfigurationBuilder()
|
||||
.SetBasePath(Directory.GetCurrentDirectory())
|
||||
.AddJsonFile("appsettings.json", optional: false)
|
||||
.Build();
|
||||
// 配置Serilog
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.ReadFrom.Configuration(configuration)
|
||||
.CreateLogger();
|
||||
|
||||
// 添加Serilog到.NET Core的日志系统
|
||||
services.AddLogging(builder =>
|
||||
{
|
||||
builder.ClearProviders();
|
||||
builder.AddSerilog(dispose: true);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2,7 +2,7 @@
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
|
||||
namespace Lai_server.Configuration
|
||||
namespace LMS.service.Configuration
|
||||
{
|
||||
public static class AuthenticationExtensions
|
||||
{
|
||||
|
||||
@ -101,7 +101,9 @@ public class DatabaseConfiguration(IServiceProvider serviceProvider) : IHostedSe
|
||||
new Options { Key = "LaitoolNotice", Value = string.Empty, Type = OptionTypeEnum.String },
|
||||
new Options { Key = "LaitoolVersion", Value = string.Empty, Type = OptionTypeEnum.String },
|
||||
new Options { Key = "LaiToolTrialDays", Value = "2" , Type = OptionTypeEnum.Number},
|
||||
new Options { Key = OptionKeyName.LaitoolFluxApiModelList, Value = "{}" , Type = OptionTypeEnum.JSON }
|
||||
new Options { Key = OptionKeyName.LaitoolFluxApiModelList, Value = "{}" , Type = OptionTypeEnum.JSON },
|
||||
new Options {Key = OptionKeyName.EnableMailService, Value = false.ToString(), Type = OptionTypeEnum.Boolean},
|
||||
new Options {Key = OptionKeyName.SMTPMailSetting, Value ="{}" , Type = OptionTypeEnum.JSON }
|
||||
];
|
||||
|
||||
// 遍历所有的配置项,如果没有则添加
|
||||
|
||||
@ -3,6 +3,7 @@ using LMS.DAO.PermissionDAO;
|
||||
using LMS.DAO.RoleDAO;
|
||||
using LMS.DAO.UserDAO;
|
||||
using LMS.service.Configuration.InitConfiguration;
|
||||
using LMS.service.Extensions.Mail;
|
||||
using LMS.service.Service;
|
||||
using LMS.service.Service.PermissionService;
|
||||
using LMS.service.Service.PromptService;
|
||||
@ -44,6 +45,13 @@ namespace Lai_server.Configuration
|
||||
services.AddScoped<PermissionBasicDao>();
|
||||
services.AddScoped<PermissionTypeDao>();
|
||||
|
||||
|
||||
// 注入 Extensions
|
||||
services.AddScoped<EmailService>();
|
||||
services.AddScoped<EmailVerificationService>();
|
||||
|
||||
// 添加分布式缓存(用于存储验证码)
|
||||
services.AddDistributedMemoryCache();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using LMS.Repository.DB;
|
||||
using LMS.Repository.DTO;
|
||||
using LMS.Repository.Models.DB;
|
||||
using LMS.Repository.Options;
|
||||
using LMS.service.Service;
|
||||
using LMS.Tools.Extensions;
|
||||
@ -61,5 +62,16 @@ namespace LMS.service.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 测试邮箱发送
|
||||
|
||||
[HttpPost]
|
||||
[Authorize]
|
||||
public async Task<ActionResult<APIResponseModel<string>>> TestSendMail()
|
||||
{
|
||||
long userId = ConvertExtension.ObjectToLong(HttpContext.Items["UserId"] ?? 0);
|
||||
return await _optionsService.TestSendMail(userId);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -77,7 +77,7 @@ namespace LMS.service.Controllers
|
||||
var cookieOptions = new CookieOptions
|
||||
{
|
||||
HttpOnly = true,
|
||||
Secure = false, // 如果使用 HTTPS
|
||||
Secure = true, // 如果使用 HTTPS
|
||||
SameSite = SameSiteMode.None,
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
};
|
||||
@ -129,6 +129,19 @@ namespace LMS.service.Controllers
|
||||
|
||||
#endregion
|
||||
|
||||
#region 获取邮箱验证码
|
||||
|
||||
[HttpPost]
|
||||
public async Task<ActionResult<APIResponseModel<string>>> SendVerificationCode([FromBody] EmailVerificationService.SendVerificationCodeDto model)
|
||||
{
|
||||
if (!ModelState.IsValid)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError);
|
||||
|
||||
return await _userService.SendVerificationCode(model);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 刷新token
|
||||
|
||||
[HttpPost]
|
||||
|
||||
180
LMS.service/Extensions/Mail/EmailService.cs
Normal file
180
LMS.service/Extensions/Mail/EmailService.cs
Normal file
@ -0,0 +1,180 @@
|
||||
using LMS.Common.Enum;
|
||||
using LMS.DAO;
|
||||
using MailKit.Net.Smtp;
|
||||
using MailKit.Security;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using MimeKit;
|
||||
using Newtonsoft.Json;
|
||||
using ILogger = Serilog.ILogger; // 明确使用 Serilog 的 ILogger
|
||||
namespace LMS.service.Extensions.Mail
|
||||
{
|
||||
|
||||
public class MailSetting
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否开启邮箱服务
|
||||
/// </summary>
|
||||
public bool EnableMailService { get; set; }
|
||||
/// <summary>
|
||||
/// 是否开启SSL
|
||||
/// </summary>
|
||||
public bool EnableSSL { get; set; }
|
||||
/// <summary>
|
||||
/// SMTP服务器
|
||||
/// </summary>
|
||||
public string SmtpServer { get; set; }
|
||||
/// <summary>
|
||||
/// 端口
|
||||
/// </summary>
|
||||
public int Port { get; set; }
|
||||
/// <summary>
|
||||
/// 发送用户名
|
||||
/// </summary>
|
||||
public string Username { get; set; }
|
||||
/// <summary>
|
||||
/// 密码
|
||||
/// </summary>
|
||||
public string Password { get; set; }
|
||||
/// <summary>
|
||||
/// 发送者邮箱
|
||||
/// </summary>
|
||||
public string SenderEmail { get; set; }
|
||||
/// <summary>
|
||||
/// 测试接收邮箱
|
||||
/// </summary>
|
||||
public string TestReceiveMail { get; set; }
|
||||
}
|
||||
|
||||
public class EmailService
|
||||
{
|
||||
private readonly ILogger _logger;
|
||||
private readonly ApplicationDbContext _context;
|
||||
|
||||
// 构造函数注入
|
||||
public EmailService(ILogger logger, ApplicationDbContext context)
|
||||
{
|
||||
_logger = logger;
|
||||
_context = context;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送安全邮件
|
||||
/// </summary>
|
||||
/// <param name="to"> 收信邮箱 </param>
|
||||
/// <param name="subject"> 邮件主题 </param>
|
||||
/// <param name="body"> 邮件信息 body </param>
|
||||
/// <returns></returns>
|
||||
public async Task SendEmailSafelyAsync(string to, string subject, string body, bool isTest = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(to))
|
||||
{
|
||||
_logger.Information("收件人地址为空,邮件未发送");
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SendEmailInternalAsync(to, subject, body, isTest);
|
||||
_logger.Information($"邮件已成功发送至: {to}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误但不抛出异常,不影响业务流程
|
||||
_logger.Error(ex, $"发送邮件至 {to} 时出错: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 发送邮件,捕获异常并记录
|
||||
/// </summary>
|
||||
/// <param name="to"> 收信邮箱 </param>
|
||||
/// <param name="subject"> 邮件主题 </param>
|
||||
/// <param name="body"> 邮件信息 body </param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="Exception"></exception>
|
||||
public async Task SendEmailAsync(string to, string subject, string body, bool isTest = false)
|
||||
{
|
||||
if (string.IsNullOrEmpty(to))
|
||||
{
|
||||
_logger.Information("收件人地址为空,邮件未发送");
|
||||
throw new Exception("收件人地址为空,邮件未发送");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
await SendEmailInternalAsync(to, subject, body, isTest);
|
||||
_logger.Information($"邮件已成功发送至: {to}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
// 记录错误但不抛出异常,不影响业务流程
|
||||
_logger.Error(ex, $"发送邮件至 {to} 时出错: {ex.Message}");
|
||||
throw new Exception(ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task SendEmailInternalAsync(string to, string subject, string body, bool isTest = false)
|
||||
{
|
||||
try
|
||||
{
|
||||
|
||||
var mailSettingString = await _context.Options.FirstOrDefaultAsync(x => x.Key == OptionKeyName.SMTPMailSetting) ?? throw new Exception("邮件配置不存在,请先配置");
|
||||
|
||||
MailSetting? mailSetting = JsonConvert.DeserializeObject<MailSetting>(mailSettingString.Value ?? "{}") ?? throw new Exception("邮件配置不存在,请先配置");
|
||||
string smtpServer = mailSetting.SmtpServer;
|
||||
int port = mailSetting.Port;
|
||||
string username = mailSetting.Username;
|
||||
string password = mailSetting.Password;
|
||||
string senderEmail = mailSetting.SenderEmail;
|
||||
// 加载邮件设置
|
||||
|
||||
if (mailSetting.EnableMailService == false)
|
||||
{
|
||||
throw new Exception("邮件服务未开启,请先开启");
|
||||
}
|
||||
|
||||
if (isTest)
|
||||
{
|
||||
if (string.IsNullOrEmpty(mailSetting.TestReceiveMail))
|
||||
{
|
||||
throw new Exception("测试接收邮箱未设置,请先设置");
|
||||
}
|
||||
to = mailSetting.TestReceiveMail;
|
||||
}
|
||||
|
||||
// 邮箱信息检查成功,开始发送邮件
|
||||
|
||||
var message = new MimeMessage();
|
||||
message.From.Add(new MailboxAddress(username, senderEmail));
|
||||
message.To.Add(new MailboxAddress("", to));
|
||||
message.Subject = subject;
|
||||
|
||||
var bodyBuilder = new BodyBuilder
|
||||
{
|
||||
HtmlBody = body
|
||||
};
|
||||
message.Body = bodyBuilder.ToMessageBody();
|
||||
|
||||
// 发送邮件
|
||||
using var client = new SmtpClient();
|
||||
|
||||
// 使用配置的服务器和端口
|
||||
await client.ConnectAsync(smtpServer, port, SecureSocketOptions.SslOnConnect);
|
||||
|
||||
// 登录
|
||||
await client.AuthenticateAsync(senderEmail, password);
|
||||
|
||||
// 设置超时
|
||||
client.Timeout = 30000; // 30秒超时
|
||||
|
||||
// 发送
|
||||
await client.SendAsync(message);
|
||||
await client.DisconnectAsync(true);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
throw new Exception("邮件发送失败,失败信息" + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
71
LMS.service/Extensions/Mail/EmailVerificationService.cs
Normal file
71
LMS.service/Extensions/Mail/EmailVerificationService.cs
Normal file
@ -0,0 +1,71 @@
|
||||
using LMS.Common.Templates;
|
||||
using LMS.service.Extensions.Mail;
|
||||
using Microsoft.Extensions.Caching.Distributed;
|
||||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
public class EmailVerificationService
|
||||
{
|
||||
private readonly IDistributedCache _cache;
|
||||
private readonly Random _random;
|
||||
private readonly EmailService _emailService;
|
||||
|
||||
public EmailVerificationService(IDistributedCache cache, EmailService emailService)
|
||||
{
|
||||
_cache = cache;
|
||||
_random = new Random();
|
||||
_emailService = emailService;
|
||||
}
|
||||
|
||||
public class SendVerificationCodeDto
|
||||
{
|
||||
[Required(ErrorMessage = "电子邮件地址是必填项")]
|
||||
[EmailAddress(ErrorMessage = "请输入有效的电子邮件地址")]
|
||||
public string Email { get; set; }
|
||||
}
|
||||
|
||||
// 生成并发送验证码
|
||||
public async Task SendVerificationCodeAsync(string email)
|
||||
{
|
||||
// 生成6位数验证码
|
||||
string verificationCode = GenerateVerificationCode();
|
||||
|
||||
// 将验证码保存到分布式缓存,设置10分钟过期
|
||||
var options = new DistributedCacheEntryOptions
|
||||
{
|
||||
AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10)
|
||||
};
|
||||
|
||||
await _cache.SetStringAsync($"EmailVerification_{email}", verificationCode, options);
|
||||
|
||||
var emailBody = EmailTemplateService.ReplaceTemplate(EmailTemplateService.RegisterHtmlTemplates, new Dictionary<string, string>
|
||||
{
|
||||
{ "RegisterCode", verificationCode }
|
||||
});
|
||||
|
||||
// 发送验证码邮件
|
||||
await _emailService.SendEmailAsync(email, "LMS注册验证码", emailBody);
|
||||
}
|
||||
|
||||
// 验证用户提交的验证码
|
||||
public async Task<bool> VerifyCodeAsync(string email, string code)
|
||||
{
|
||||
var storedCode = await _cache.GetStringAsync($"EmailVerification_{email}");
|
||||
|
||||
if (string.IsNullOrEmpty(storedCode))
|
||||
return false;
|
||||
|
||||
// 使用完就删除验证码
|
||||
if (storedCode == code)
|
||||
{
|
||||
await _cache.RemoveAsync($"EmailVerification_{email}");
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private string GenerateVerificationCode()
|
||||
{
|
||||
return _random.Next(100000, 999999).ToString();
|
||||
}
|
||||
}
|
||||
@ -4,14 +4,9 @@ using System.Security.Claims;
|
||||
|
||||
namespace LMS.service.Extensions.Middleware
|
||||
{
|
||||
public class DynamicPermissionMiddleware
|
||||
public class DynamicPermissionMiddleware(RequestDelegate next)
|
||||
{
|
||||
private readonly RequestDelegate _next;
|
||||
|
||||
public DynamicPermissionMiddleware(RequestDelegate next)
|
||||
{
|
||||
_next = next;
|
||||
}
|
||||
private readonly RequestDelegate _next = next;
|
||||
|
||||
public async Task InvokeAsync(HttpContext context, PremissionValidationService _premissionValidationServices)
|
||||
{
|
||||
@ -45,7 +40,7 @@ namespace LMS.service.Extensions.Middleware
|
||||
}
|
||||
}
|
||||
|
||||
private long GetUserIdFromContext(HttpContext context)
|
||||
private static long GetUserIdFromContext(HttpContext context)
|
||||
{
|
||||
var userIdClaim = context.User.Claims.FirstOrDefault(c => c.Type == ClaimTypes.NameIdentifier);
|
||||
var userId = userIdClaim?.Value;
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
@ -7,11 +7,10 @@
|
||||
<UserSecretsId>ed64fb6f-9c93-43d0-b418-61f507f28420</UserSecretsId>
|
||||
<DockerDefaultTargetOS>Linux</DockerDefaultTargetOS>
|
||||
<DockerfileContext>.</DockerfileContext>
|
||||
<Version>1.0.3</Version>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="13.0.1" />
|
||||
<PackageReference Include="AutoMapper" Version="14.0.0" />
|
||||
<PackageReference Include="Betalgo.Ranul.OpenAI" Version="8.9.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="8.0.8" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="8.0.8" />
|
||||
@ -23,8 +22,15 @@
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.Extensions.Caching.Memory" Version="8.0.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration.Json" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="9.0.3" />
|
||||
<PackageReference Include="Microsoft.VisualStudio.Azure.Containers.Tools.Targets" Version="1.19.6" />
|
||||
<PackageReference Include="Pomelo.EntityFrameworkCore.MySql" Version="8.0.2" />
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Extensions.Logging" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Settings.Configuration" Version="9.0.0" />
|
||||
<PackageReference Include="Serilog.Sinks.File" Version="6.0.0" />
|
||||
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
|
||||
<PackageReference Include="System.Runtime" Version="4.3.1" />
|
||||
</ItemGroup>
|
||||
|
||||
@ -6,8 +6,7 @@ using LMS.service.Configuration.InitConfiguration;
|
||||
using LMS.service.Extensions.Middleware;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
@ -33,6 +32,10 @@ builder.Services.ConfigureApplicationCookie(options =>
|
||||
//ÅäÖÃJWT
|
||||
builder.Services.AddJWTAuthentication();
|
||||
builder.Services.AddAutoMapper(typeof(AutoMapperConfig));
|
||||
builder.Services.AddLoggerService();
|
||||
builder.Host.UseSerilog();
|
||||
// 关键步骤:注册 Serilog.ILogger 到 DI 容器
|
||||
builder.Services.AddSingleton(Log.Logger);
|
||||
|
||||
builder.Services.AddDbContext<ApplicationDbContext>(options =>
|
||||
{
|
||||
@ -76,6 +79,8 @@ builder.Services.AddHostedService<DatabaseConfiguration>();
|
||||
|
||||
var app = builder.Build();
|
||||
|
||||
var version = builder.Configuration["Version"];
|
||||
|
||||
// Configure the HTTP request pipeline.
|
||||
if (app.Environment.IsDevelopment())
|
||||
{
|
||||
@ -102,5 +107,6 @@ app.UseEndpoints(endpoints =>
|
||||
_ = endpoints.MapControllers();
|
||||
});
|
||||
|
||||
Log.Information("后台启动成功,系统版本:" + version);
|
||||
|
||||
app.Run();
|
||||
@ -10,6 +10,8 @@ using LMS.Tools.Extensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using static LMS.Common.Enums.MachineEnum;
|
||||
using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
using static LMS.Repository.DTO.MachineResponse.MachineDto;
|
||||
@ -321,7 +323,7 @@ namespace LMS.service.Service
|
||||
/// <param name="ownUserName"></param>
|
||||
/// <param name="requestUserId"></param>
|
||||
/// <returns></returns>
|
||||
internal async Task<ActionResult<APIResponseModel<CollectionResponse<Machine>>>> QueryMachineCollection(int page, int pageSize, string? machineId, string? createdUserName, MachineStatus? status, MachineUseStatus? useStatus, string? remark, string? ownUserName, long requestUserId)
|
||||
public async Task<ActionResult<APIResponseModel<CollectionResponse<Machine>>>> QueryMachineCollection(int page, int pageSize, string? machineId, string? createdUserName, MachineStatus? status, MachineUseStatus? useStatus, string? remark, string? ownUserName, long requestUserId)
|
||||
{
|
||||
try
|
||||
{
|
||||
@ -332,35 +334,94 @@ namespace LMS.service.Service
|
||||
}
|
||||
bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin");
|
||||
bool isAdmin = await _userManager.IsInRoleAsync(user, "Admin");
|
||||
bool isAgent = await _userManager.IsInRoleAsync(user, "Agent User");
|
||||
|
||||
IQueryable<Machine> query = _context.Machine;
|
||||
|
||||
if (isAdmin)
|
||||
{
|
||||
List<long> superAdminUserIds = ((List<User>)await _userManager.GetUsersInRoleAsync("Super Admin")).Select(x => x.Id).ToList();
|
||||
|
||||
//.Result.Select(x => x.Id).ToList();
|
||||
query = query.Where(x => !superAdminUserIds.Contains(x.UserID));
|
||||
}
|
||||
else if (!isSuperAdmin)
|
||||
{
|
||||
query = query.Where(x => x.UserID == requestUserId);
|
||||
}
|
||||
// 添加其他的查询条件
|
||||
if (!string.IsNullOrWhiteSpace(machineId))
|
||||
{
|
||||
query = query.Where(x => x.MachineId == machineId);
|
||||
}
|
||||
// 管理员和超级管理员可以使用该字段查询所有创建者的机器码
|
||||
if (!string.IsNullOrWhiteSpace(createdUserName) && (isAdmin || isSuperAdmin))
|
||||
|
||||
// 更具用户角色判断当前可能查询那些用户的机器码
|
||||
|
||||
if (!isAdmin && !isSuperAdmin && !isAgent)
|
||||
{
|
||||
List<long> queryUserId = (await _userManager.Users.Where(x => x.UserName.Contains(createdUserName)).ToListAsync()).Select(x => x.Id).ToList();
|
||||
query = query.Where(x => queryUserId.Contains(x.CreateId));
|
||||
// 普通用户只能查看所属自己的机器码,不具备查询创建者和所属者的权限
|
||||
query = query.Where(x => x.UserID == user.Id);
|
||||
}
|
||||
// 普通用户只能查找自己创建的机器码
|
||||
else if (!string.IsNullOrWhiteSpace(createdUserName))
|
||||
else
|
||||
{
|
||||
query = query.Where(x => x.CreateId == user.Id);
|
||||
// 获取相关用户ID
|
||||
var userLookupQuery = _userManager.Users.AsNoTracking();
|
||||
|
||||
HashSet<long> filteredCreatorIds = null;
|
||||
HashSet<long> filteredOwnerIds = null;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(createdUserName))
|
||||
{
|
||||
// 获取列表后直接转换为HashSet
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.UserName.Contains(createdUserName))
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
filteredCreatorIds = new HashSet<long>(list);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(ownUserName))
|
||||
{
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.UserName.Contains(ownUserName))
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
filteredOwnerIds = new HashSet<long>(list);
|
||||
}
|
||||
|
||||
// 数据过滤
|
||||
if (filteredCreatorIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => filteredCreatorIds.Contains(x.CreateId));
|
||||
}
|
||||
|
||||
if (filteredOwnerIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => filteredOwnerIds.Contains(x.UserID));
|
||||
}
|
||||
|
||||
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
List<long> superUserIds = superUsers.Select(x => x.Id).ToList();
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == null || superUserIds.Contains((long)u.ParentId) || superUserIds.Contains(u.Id))
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.UserID) || x.UserID == requestUserId);
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == requestUserId)
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.UserID) || x.UserID == requestUserId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (status != null)
|
||||
@ -376,18 +437,6 @@ namespace LMS.service.Service
|
||||
query = query.Where(x => x.Remark.Contains(remark));
|
||||
}
|
||||
|
||||
// 管理员和超级管理员可以使用该字段查询所有的机器码的拥有者
|
||||
if (!string.IsNullOrWhiteSpace(ownUserName) && (isAdmin || isSuperAdmin))
|
||||
{
|
||||
List<long> queryUserId = (await _userManager.Users.Where(x => x.UserName.Contains(ownUserName)).ToListAsync()).Select(x => x.Id).ToList();
|
||||
query = query.Where(x => queryUserId.Contains(x.UserID));
|
||||
}
|
||||
// 普通用户只能查找自己拥有的机器码
|
||||
else if (!string.IsNullOrWhiteSpace(ownUserName))
|
||||
{
|
||||
query = query.Where(x => x.UserID == user.Id);
|
||||
}
|
||||
|
||||
int total = await query.CountAsync();
|
||||
|
||||
// 降序,取指定的条数的数据
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
using AutoMapper;
|
||||
using LMS.Common.Dictionary;
|
||||
using LMS.Common.Templates;
|
||||
using LMS.DAO;
|
||||
using LMS.DAO.UserDAO;
|
||||
using LMS.Repository.DB;
|
||||
using LMS.Repository.DTO;
|
||||
using LMS.Repository.DTO.UserDto;
|
||||
using LMS.Repository.Models.DB;
|
||||
using LMS.Repository.Options;
|
||||
using LMS.service.Extensions.Mail;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
@ -13,11 +15,13 @@ using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
|
||||
namespace LMS.service.Service
|
||||
{
|
||||
public class OptionsService(ApplicationDbContext context, UserManager<User> userManager, IMapper mapper)
|
||||
public class OptionsService(ApplicationDbContext context, UserManager<User> userManager, IMapper mapper, UserBasicDao userBasicDao, EmailService emailService)
|
||||
{
|
||||
private readonly ApplicationDbContext _context = context;
|
||||
private readonly UserManager<User> _userManager = userManager;
|
||||
private readonly IMapper _mapper = mapper;
|
||||
private readonly UserBasicDao _userBasicDao = userBasicDao;
|
||||
private readonly EmailService _emailService = emailService;
|
||||
|
||||
#region 获取简单的配置项,无需权限
|
||||
|
||||
@ -159,5 +163,42 @@ namespace LMS.service.Service
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 测试邮箱发送
|
||||
|
||||
/// <summary>
|
||||
/// 测试邮箱发送
|
||||
/// </summary>
|
||||
/// <param name="userId"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<ActionResult<APIResponseModel<string>>> TestSendMail(long userId)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 判断是不是超级管理员
|
||||
bool isSuperAdmin = await _userBasicDao.CheckUserIsSuperAdmin(userId);
|
||||
if (!isSuperAdmin)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.NotPermissionAction);
|
||||
}
|
||||
|
||||
var emailBody = EmailTemplateService.ReplaceTemplate(EmailTemplateService.RegisterHtmlTemplates, new Dictionary<string, string>
|
||||
{
|
||||
{ "RegisterCode", "验证码" }
|
||||
});
|
||||
|
||||
// 调用发送邮件的方法
|
||||
await _emailService.SendEmailAsync("user@example.com", "邮件连通测试", emailBody, true);
|
||||
|
||||
|
||||
return APIResponseModel<string>.CreateSuccessResponseModel("邮箱测试发送成功");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ using LMS.Tools.Extensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Generic;
|
||||
using static LMS.Common.Enums.PromptEnum;
|
||||
using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
|
||||
|
||||
@ -10,17 +10,19 @@ using LMS.Repository.Models.DB;
|
||||
using LMS.Repository.Software;
|
||||
using LMS.Tools;
|
||||
using LMS.Tools.Extensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
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)
|
||||
public class SoftwareControlService(UserBasicDao userBasicDao, ApplicationDbContext dbContext, IMapper mapper, UserManager<User> userManager)
|
||||
{
|
||||
private readonly UserBasicDao _userBasicDao = userBasicDao;
|
||||
private readonly ApplicationDbContext _dbContext = dbContext;
|
||||
private readonly IMapper _mapper = mapper;
|
||||
private readonly UserManager<User> _userManager = userManager;
|
||||
|
||||
#region 软件控制-同步用户的软件控制权限
|
||||
/// <summary>
|
||||
@ -251,9 +253,13 @@ namespace LMS.service.Service.SoftwareService
|
||||
{
|
||||
// 判断权限,如果不是管理员或超级管理员,就判断是不是自己的数据,不是的话,返回无权限操作
|
||||
IQueryable<SoftwareControl> query = _dbContext.SoftwareControl.AsQueryable();
|
||||
bool isAdminOrSuperAdmin = await _userBasicDao.CheckUserIsAdminOrSuperAdmin(requestUserId);
|
||||
|
||||
if (!isAdminOrSuperAdmin && userId != requestUserId)
|
||||
bool isSuperAdmin = await _userBasicDao.CheckUserIsSuperAdmin(requestUserId);
|
||||
bool isAdmin = await _userBasicDao.CheckUserIsAdmin(requestUserId);
|
||||
bool isAgent = await _userBasicDao.CheckUserIsAgent(requestUserId);
|
||||
|
||||
|
||||
if (!(isSuperAdmin || isAdmin || isAgent) && userId != requestUserId)
|
||||
{
|
||||
return APIResponseModel<CollectionResponse<SoftwareControlCollectionDto>>.CreateErrorResponseModel(ResponseCode.NotPermissionAction);
|
||||
}
|
||||
@ -275,14 +281,52 @@ namespace LMS.service.Service.SoftwareService
|
||||
query = query.Where(x => x.Remark.Contains(remark));
|
||||
}
|
||||
|
||||
|
||||
if (!isAdminOrSuperAdmin)
|
||||
if (!(isSuperAdmin || isAdmin))
|
||||
{
|
||||
// 通过 softwareId 过滤掉isUse为false的数
|
||||
List<string> softwareIds = await _dbContext.Software.Where(x => x.IsUse == true).Select(x => x.Id).ToListAsync();
|
||||
query = query.Where(x => softwareIds.Contains(x.SoftwareId));
|
||||
}
|
||||
|
||||
// 做筛选权限
|
||||
// 获取相关用户ID
|
||||
var userLookupQuery = _userManager.Users.AsNoTracking();
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
List<long> superUserIds = superUsers.Select(x => x.Id).ToList();
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == null || superUserIds.Contains((long)u.ParentId) || superUserIds.Contains(u.Id))
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.UserId) || x.UserId == requestUserId);
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == requestUserId)
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.UserId));
|
||||
}
|
||||
}
|
||||
else if (!isSuperAdmin)
|
||||
{
|
||||
// 普通用户只能看到自己的
|
||||
query = query.Where(x => x.UserId == requestUserId);
|
||||
}
|
||||
|
||||
// 通过ID降序
|
||||
query = query.OrderByDescending(x => x.CreatedTime);
|
||||
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
using LMS.Common.RSAKey;
|
||||
using LMS.Common.Enum;
|
||||
using LMS.Common.RSAKey;
|
||||
using LMS.DAO;
|
||||
using LMS.DAO.UserDAO;
|
||||
using LMS.Repository.DB;
|
||||
using LMS.Repository.DTO;
|
||||
using LMS.Repository.DTO.UserDto;
|
||||
using LMS.Repository.Models.DB;
|
||||
@ -7,7 +10,6 @@ using LMS.Repository.Models.User;
|
||||
using LMS.Repository.User;
|
||||
using LMS.Tools.Extensions;
|
||||
using Microsoft.AspNetCore.Identity;
|
||||
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System.Collections.Concurrent;
|
||||
@ -15,12 +17,14 @@ using static LMS.Common.Enums.ResponseCodeEnum;
|
||||
|
||||
namespace LMS.service.Service.UserService
|
||||
{
|
||||
public class UserService(UserManager<User> userManager, RoleManager<Role> roleManager, ApplicationDbContext context, SecurityService securityService)
|
||||
public class UserService(UserManager<User> userManager, RoleManager<Role> roleManager, ApplicationDbContext context, SecurityService securityService, EmailVerificationService emailVerificationService, UserBasicDao userBasicDao)
|
||||
{
|
||||
private readonly UserManager<User> _userManager = userManager;
|
||||
private readonly RoleManager<Role> _roleManager = roleManager;
|
||||
private readonly ApplicationDbContext _context = context;
|
||||
private readonly SecurityService _securityService = securityService;
|
||||
private readonly EmailVerificationService _verificationService = emailVerificationService;
|
||||
private readonly UserBasicDao _userBasicDao = userBasicDao;
|
||||
|
||||
#region 获取用户信息
|
||||
/// <summary>
|
||||
@ -97,11 +101,10 @@ namespace LMS.service.Service.UserService
|
||||
{
|
||||
return APIResponseModel<CollectionResponse<UserCollectionDto>>.CreateErrorResponseModel(ResponseCode.FindUserByIdFail);
|
||||
}
|
||||
|
||||
bool isAdminOrSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin") || await _userManager.IsInRoleAsync(user, "Admin");
|
||||
bool isAdmin = await _userBasicDao.CheckUserIsAdmin(reuqertUserId);
|
||||
bool isSuperAdmin = await _userManager.IsInRoleAsync(user, "Super Admin");
|
||||
bool isAgent = !isAdminOrSuperAdmin && await _userManager.IsInRoleAsync(user, "Agent User");
|
||||
if (!isAdminOrSuperAdmin && !isAgent)
|
||||
bool isAgent = await _userManager.IsInRoleAsync(user, "Agent User");
|
||||
if (!isAdmin && !isSuperAdmin && !isAgent)
|
||||
{
|
||||
return APIResponseModel<CollectionResponse<UserCollectionDto>>.CreateErrorResponseModel(ResponseCode.NotPermissionAction);
|
||||
}
|
||||
@ -128,21 +131,46 @@ namespace LMS.service.Service.UserService
|
||||
// 开始查询数据
|
||||
IQueryable<User>? query = _userManager.Users;
|
||||
|
||||
if (isAgent)
|
||||
{
|
||||
query = query.Where(x => x.ParentId == user.Id);
|
||||
}
|
||||
// 判断是不是管理员
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
List<long> superUserIds = superUsers.Select(x => x.Id).ToList();
|
||||
// 默认把自己排除
|
||||
//query = query.Where(x => x.Id != reuqertUserId);
|
||||
if (!isSuperAdmin)
|
||||
{
|
||||
// 不是草鸡管理员,就把超级管理员排除
|
||||
query = query.Where(x => reuqertUserId == x.Id || (!superUserIds.Contains(x.ParentId ?? 0) && !superUserIds.Contains(x.Id)));
|
||||
query = query.Where(x => x.Id != reuqertUserId);
|
||||
}
|
||||
|
||||
// 获取相关用户ID
|
||||
var userLookupQuery = _userManager.Users.AsNoTracking();
|
||||
if (isAdmin && !isSuperAdmin)
|
||||
{
|
||||
// 除了超级管理员的代理 其他都能看到
|
||||
IList<User> superUsers = await _userManager.GetUsersInRoleAsync("Super Admin");
|
||||
List<long> superUserIds = superUsers.Select(x => x.Id).ToList();
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == null || superUserIds.Contains((long)u.ParentId) || superUserIds.Contains(u.Id))
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => !filteredParentIds.Contains(x.Id));
|
||||
}
|
||||
}
|
||||
else if (isAgent && !isSuperAdmin)
|
||||
{
|
||||
// 代理只能看到自己下面的用户
|
||||
var list = await userLookupQuery
|
||||
.Where(u => u.ParentId == reuqertUserId)
|
||||
.Select(u => u.Id)
|
||||
.ToListAsync();
|
||||
|
||||
HashSet<long> filteredParentIds = new(list);
|
||||
if (filteredParentIds?.Count > 0)
|
||||
{
|
||||
query = query.Where(x => filteredParentIds.Contains(x.Id));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// 添加查询条件
|
||||
if (!string.IsNullOrWhiteSpace(userName))
|
||||
{
|
||||
@ -214,7 +242,7 @@ namespace LMS.service.Service.UserService
|
||||
{
|
||||
List<string>? roles = [.. (await _userManager.GetRolesAsync(users[i]))];
|
||||
userCollections[i].RoleNames = roles;
|
||||
if (!isAdminOrSuperAdmin)
|
||||
if (!isSuperAdmin || isAdmin)
|
||||
{
|
||||
userCollections[i].PhoneNumber = "***********";
|
||||
userCollections[i].Email = "***********";
|
||||
@ -448,6 +476,25 @@ namespace LMS.service.Service.UserService
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.InvalidAffiliateCode);
|
||||
}
|
||||
|
||||
// 判断邮箱是不是被使用了
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser != null)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "当前邮箱已注册,请直接登录!");
|
||||
|
||||
// 验证验证码
|
||||
// 判断是不是需要校验验证码
|
||||
Options? enaleMailService = await _context.Options.FirstOrDefaultAsync(x => x.Key == OptionKeyName.EnableMailService);
|
||||
if (enaleMailService != null)
|
||||
{
|
||||
_ = bool.TryParse(enaleMailService.Value, out bool enableMail);
|
||||
if (enableMail)
|
||||
{
|
||||
var isCodeValid = await _verificationService.VerifyCodeAsync(model.Email, model.VerificationCode);
|
||||
if (!isCodeValid)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "验证码无效或已过期");
|
||||
}
|
||||
}
|
||||
|
||||
var rsaKeyId = keyInfo.Key;
|
||||
var privateKey = _securityService.DecryptWithAES(rsaKeyId);
|
||||
|
||||
@ -491,5 +538,26 @@ namespace LMS.service.Service.UserService
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 发送用户注册验证码
|
||||
public async Task<ActionResult<APIResponseModel<string>>> SendVerificationCode(EmailVerificationService.SendVerificationCodeDto model)
|
||||
{
|
||||
try
|
||||
{
|
||||
// 检查邮箱是否已被使用
|
||||
var existingUser = await _userManager.FindByEmailAsync(model.Email);
|
||||
if (existingUser != null)
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.ParameterError, "当前邮箱已注册,请直接登录!");
|
||||
|
||||
// 发送验证码
|
||||
await _verificationService.SendVerificationCodeAsync(model.Email);
|
||||
return APIResponseModel<string>.CreateSuccessResponseModel(ResponseCode.Success, "验证码发送成功,请在邮箱中查收!");
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return APIResponseModel<string>.CreateErrorResponseModel(ResponseCode.SystemError, e.Message);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
||||
@ -5,5 +5,27 @@
|
||||
"Microsoft.AspNetCore": "Warning"
|
||||
}
|
||||
},
|
||||
"Serilog": {
|
||||
"MinimumLevel": {
|
||||
"Default": "Information",
|
||||
"Override": {
|
||||
"Microsoft": "Warning",
|
||||
"System": "Warning"
|
||||
}
|
||||
},
|
||||
"WriteTo": [
|
||||
{
|
||||
"Name": "File",
|
||||
"Args": {
|
||||
"path": "logs/app-.log",
|
||||
"rollingInterval": "Day",
|
||||
"outputTemplate": "{Timestamp:yyyy-MM-dd HH:mm:ss.fff zzz} [{Level:u3}] {Message:lj}{NewLine}{Exception}",
|
||||
"retainedFileCountLimit": 31
|
||||
}
|
||||
}
|
||||
],
|
||||
"Enrich": [ "FromLogContext" ]
|
||||
},
|
||||
"Version": "1.0.4",
|
||||
"AllowedHosts": "*"
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user