feat: 批量发送邮件 API(模板、SMTP 配置、发送记录)
新增模板/账号持久化、SMTP 测试与单封发送接口,发送成功后写入邮件记录表供前端列表展示。 Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
parent
1c0dce9956
commit
e4894f9627
57
db.sql
57
db.sql
@ -10,6 +10,9 @@
|
|||||||
SET NAMES utf8mb4;
|
SET NAMES utf8mb4;
|
||||||
SET FOREIGN_KEY_CHECKS = 0;
|
SET FOREIGN_KEY_CHECKS = 0;
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_system_batch_email_record`;
|
||||||
|
DROP TABLE IF EXISTS `t_system_batch_email_account`;
|
||||||
|
DROP TABLE IF EXISTS `t_system_batch_email_template`;
|
||||||
DROP TABLE IF EXISTS `t_system_other_config`;
|
DROP TABLE IF EXISTS `t_system_other_config`;
|
||||||
DROP TABLE IF EXISTS `t_warehouse_address`;
|
DROP TABLE IF EXISTS `t_warehouse_address`;
|
||||||
DROP TABLE IF EXISTS `t_notice_read`;
|
DROP TABLE IF EXISTS `t_notice_read`;
|
||||||
@ -854,6 +857,60 @@ CREATE TABLE `t_system_other_config` (
|
|||||||
KEY `idx_system_other_config_enabled` (`Enabled`, `Id`)
|
KEY `idx_system_other_config_enabled` (`Enabled`, `Id`)
|
||||||
) COMMENT='系统其他配置表';
|
) COMMENT='系统其他配置表';
|
||||||
|
|
||||||
|
CREATE TABLE `t_system_batch_email_template` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`TemplateName` VARCHAR(128) NOT NULL COMMENT '模板名称',
|
||||||
|
`Subject` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '邮件主题',
|
||||||
|
`Content` TEXT NOT NULL COMMENT '邮件正文',
|
||||||
|
`SortOrder` INT NOT NULL DEFAULT 0 COMMENT '排序',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
UNIQUE KEY `uk_batch_email_tpl_owner_name` (`OwnerUserId`, `TemplateName`),
|
||||||
|
KEY `idx_batch_email_tpl_owner` (`OwnerUserId`, `SortOrder`, `Id`)
|
||||||
|
) COMMENT='批量发送邮件模板';
|
||||||
|
|
||||||
|
CREATE TABLE `t_system_batch_email_account` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`SmtpUsername` VARCHAR(256) NOT NULL COMMENT 'SMTP 账号',
|
||||||
|
`SmtpPassword` VARCHAR(512) NOT NULL COMMENT 'SMTP 密码/授权码',
|
||||||
|
`ServerType` VARCHAR(32) NOT NULL DEFAULT 'qiye' COMMENT 'SMTP 类型',
|
||||||
|
`FromName` VARCHAR(128) NULL COMMENT '发件人显示名',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
UNIQUE KEY `uk_batch_email_account_owner` (`OwnerUserId`)
|
||||||
|
) COMMENT='批量发送邮件 SMTP 账号配置';
|
||||||
|
|
||||||
|
CREATE TABLE `t_system_batch_email_record` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`ToEmail` VARCHAR(256) NOT NULL COMMENT '收件邮箱',
|
||||||
|
`Subject` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '邮件主题',
|
||||||
|
`Content` TEXT NOT NULL COMMENT '邮件正文',
|
||||||
|
`SendTime` DATETIME NOT NULL COMMENT '发送时间',
|
||||||
|
`Sender` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '发送人',
|
||||||
|
`SenderEmail` VARCHAR(256) NULL COMMENT '发件邮箱',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
KEY `idx_batch_email_record_owner_time` (`OwnerUserId`, `SendTime`, `Id`)
|
||||||
|
) COMMENT='批量发送邮件记录';
|
||||||
|
|
||||||
CREATE TABLE `t_notice` (
|
CREATE TABLE `t_notice` (
|
||||||
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键,自增,步长 1',
|
||||||
`NoticeType` VARCHAR(16) NOT NULL COMMENT '通知类型:notice/message',
|
`NoticeType` VARCHAR(16) NOT NULL COMMENT '通知类型:notice/message',
|
||||||
|
|||||||
54
sql/migrate_t_system_batch_email.sql
Normal file
54
sql/migrate_t_system_batch_email.sql
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
-- 批量发送邮件:模板与 SMTP 账号配置
|
||||||
|
CREATE TABLE IF NOT EXISTS `t_system_batch_email_template` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`TemplateName` VARCHAR(128) NOT NULL COMMENT '模板名称',
|
||||||
|
`Subject` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '邮件主题',
|
||||||
|
`Content` TEXT NOT NULL COMMENT '邮件正文',
|
||||||
|
`SortOrder` INT NOT NULL DEFAULT 0 COMMENT '排序',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
UNIQUE KEY `uk_batch_email_tpl_owner_name` (`OwnerUserId`, `TemplateName`),
|
||||||
|
KEY `idx_batch_email_tpl_owner` (`OwnerUserId`, `SortOrder`, `Id`)
|
||||||
|
) COMMENT='批量发送邮件模板';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `t_system_batch_email_account` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`SmtpUsername` VARCHAR(256) NOT NULL COMMENT 'SMTP 账号',
|
||||||
|
`SmtpPassword` VARCHAR(512) NOT NULL COMMENT 'SMTP 密码/授权码',
|
||||||
|
`ServerType` VARCHAR(32) NOT NULL DEFAULT 'qiye' COMMENT 'SMTP 类型',
|
||||||
|
`FromName` VARCHAR(128) NULL COMMENT '发件人显示名',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
UNIQUE KEY `uk_batch_email_account_owner` (`OwnerUserId`)
|
||||||
|
) COMMENT='批量发送邮件 SMTP 账号配置';
|
||||||
|
|
||||||
|
CREATE TABLE IF NOT EXISTS `t_system_batch_email_record` (
|
||||||
|
`Id` INT NOT NULL AUTO_INCREMENT COMMENT '主键',
|
||||||
|
`OwnerUserId` INT NOT NULL DEFAULT 0 COMMENT '所属 RBAC 用户 Id',
|
||||||
|
`ToEmail` VARCHAR(256) NOT NULL COMMENT '收件邮箱',
|
||||||
|
`Subject` VARCHAR(512) NOT NULL DEFAULT '' COMMENT '邮件主题',
|
||||||
|
`Content` TEXT NOT NULL COMMENT '邮件正文',
|
||||||
|
`SendTime` DATETIME NOT NULL COMMENT '发送时间',
|
||||||
|
`Sender` VARCHAR(128) NOT NULL DEFAULT '' COMMENT '发送人',
|
||||||
|
`SenderEmail` VARCHAR(256) NULL COMMENT '发件邮箱',
|
||||||
|
`IsDeleted` TINYINT NOT NULL DEFAULT 0 COMMENT '软删除:0-否,1-是',
|
||||||
|
`ConcurrencyStamp` CHAR(36) NOT NULL DEFAULT (UUID()) COMMENT '并发戳',
|
||||||
|
`CreateUserId` INT NOT NULL DEFAULT 0 COMMENT '创建人',
|
||||||
|
`CreateTime` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
|
||||||
|
`UpdateUserId` INT NULL COMMENT '更新人',
|
||||||
|
`UpdateTime` DATETIME NULL COMMENT '更新时间',
|
||||||
|
PRIMARY KEY (`Id`),
|
||||||
|
KEY `idx_batch_email_record_owner_time` (`OwnerUserId`, `SendTime`, `Id`)
|
||||||
|
) COMMENT='批量发送邮件记录';
|
||||||
12
src/Oms.Application/Dtos/System/BatchEmailAccountOutput.cs
Normal file
12
src/Oms.Application/Dtos/System/BatchEmailAccountOutput.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailAccountOutput
|
||||||
|
{
|
||||||
|
public string SmtpUsername { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SmtpPassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string ServerType { get; set; } = "qiye";
|
||||||
|
|
||||||
|
public string? FromName { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailAccountSaveInput
|
||||||
|
{
|
||||||
|
public string SmtpUsername { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SmtpPassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string ServerType { get; set; } = "qiye";
|
||||||
|
|
||||||
|
public string? FromName { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,8 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailRecordListInput
|
||||||
|
{
|
||||||
|
public int Page { get; set; } = 1;
|
||||||
|
|
||||||
|
public int PageSize { get; set; } = 30;
|
||||||
|
}
|
||||||
16
src/Oms.Application/Dtos/System/BatchEmailRecordOutput.cs
Normal file
16
src/Oms.Application/Dtos/System/BatchEmailRecordOutput.cs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailRecordOutput
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
|
||||||
|
public string ToEmail { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime SendTime { get; set; }
|
||||||
|
|
||||||
|
public string Sender { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
20
src/Oms.Application/Dtos/System/BatchEmailSendOneInput.cs
Normal file
20
src/Oms.Application/Dtos/System/BatchEmailSendOneInput.cs
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailSendOneInput
|
||||||
|
{
|
||||||
|
public string SmtpUsername { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SmtpPassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? ServerType { get; set; }
|
||||||
|
|
||||||
|
public string To { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Body { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? BodyType { get; set; }
|
||||||
|
|
||||||
|
public string? FromName { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,6 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailTemplateDeleteInput
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
10
src/Oms.Application/Dtos/System/BatchEmailTemplateOutput.cs
Normal file
10
src/Oms.Application/Dtos/System/BatchEmailTemplateOutput.cs
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailTemplateOutput
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailTemplateSaveInput
|
||||||
|
{
|
||||||
|
public string Name { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
}
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
namespace Oms.Application.Dtos.System;
|
||||||
|
|
||||||
|
public class BatchEmailTestConnectionInput
|
||||||
|
{
|
||||||
|
public string SmtpUsername { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string SmtpPassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string? ServerType { get; set; }
|
||||||
|
|
||||||
|
public string? FromName { get; set; }
|
||||||
|
}
|
||||||
122
src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs
Normal file
122
src/Oms.Application/Services/System/BatchEmailSmtpHelper.cs
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
using System.Net;
|
||||||
|
using System.Net.Mail;
|
||||||
|
using Fake.Application;
|
||||||
|
|
||||||
|
namespace Oms.Application.Services.System;
|
||||||
|
|
||||||
|
internal static class BatchEmailSmtpHelper
|
||||||
|
{
|
||||||
|
private const string SecretMask = "******";
|
||||||
|
|
||||||
|
internal static bool IsSecretMask(string? value) =>
|
||||||
|
string.Equals(value?.Trim(), SecretMask, StringComparison.Ordinal);
|
||||||
|
|
||||||
|
internal sealed record SmtpEndpoint(string Host, int Port, bool EnableSsl);
|
||||||
|
|
||||||
|
internal static SmtpEndpoint ResolveEndpoint(string? serverType)
|
||||||
|
{
|
||||||
|
return (serverType ?? string.Empty).Trim().ToLowerInvariant() switch
|
||||||
|
{
|
||||||
|
"qiye_ssl" => new SmtpEndpoint("smtp.mxhichina.com", 465, true),
|
||||||
|
"aliyun" => new SmtpEndpoint("smtp.aliyun.com", 25, false),
|
||||||
|
"aliyun_ssl" => new SmtpEndpoint("smtp.aliyun.com", 465, true),
|
||||||
|
"mxhichina" => new SmtpEndpoint("smtp.mxhichina.com", 25, false),
|
||||||
|
_ => new SmtpEndpoint("smtp.mxhichina.com", 25, false)
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task TestConnectionAsync(
|
||||||
|
string smtpUsername,
|
||||||
|
string smtpPassword,
|
||||||
|
string? serverType,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ValidateCredentials(smtpUsername, smtpPassword);
|
||||||
|
var username = smtpUsername.Trim();
|
||||||
|
var endpoint = ResolveEndpoint(serverType);
|
||||||
|
using var client = CreateClient(endpoint, username, smtpPassword);
|
||||||
|
using var message = new MailMessage(username, username)
|
||||||
|
{
|
||||||
|
Subject = "OMS SMTP Test",
|
||||||
|
Body = "OMS batch email connection test"
|
||||||
|
};
|
||||||
|
await SendWithClientAsync(client, message, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static async Task SendAsync(
|
||||||
|
string smtpUsername,
|
||||||
|
string smtpPassword,
|
||||||
|
string? serverType,
|
||||||
|
string to,
|
||||||
|
string subject,
|
||||||
|
string body,
|
||||||
|
string? bodyType,
|
||||||
|
string? fromName,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
ValidateCredentials(smtpUsername, smtpPassword);
|
||||||
|
var recipient = to.Trim();
|
||||||
|
if (string.IsNullOrWhiteSpace(recipient) || !recipient.Contains('@'))
|
||||||
|
{
|
||||||
|
throw new BusinessException("收件人邮箱无效");
|
||||||
|
}
|
||||||
|
|
||||||
|
var username = smtpUsername.Trim();
|
||||||
|
var endpoint = ResolveEndpoint(serverType);
|
||||||
|
using var client = CreateClient(endpoint, username, smtpPassword);
|
||||||
|
var fromAddress = new MailAddress(
|
||||||
|
username,
|
||||||
|
string.IsNullOrWhiteSpace(fromName) ? username : fromName.Trim());
|
||||||
|
using var message = new MailMessage
|
||||||
|
{
|
||||||
|
From = fromAddress,
|
||||||
|
Subject = subject ?? string.Empty,
|
||||||
|
Body = body ?? string.Empty,
|
||||||
|
IsBodyHtml = string.Equals(bodyType, "html", StringComparison.OrdinalIgnoreCase)
|
||||||
|
};
|
||||||
|
message.To.Add(recipient);
|
||||||
|
await SendWithClientAsync(client, message, cancellationToken);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void ValidateCredentials(string smtpUsername, string smtpPassword)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(smtpUsername))
|
||||||
|
{
|
||||||
|
throw new BusinessException("SMTP 账号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(smtpPassword) || IsSecretMask(smtpPassword))
|
||||||
|
{
|
||||||
|
throw new BusinessException("SMTP 密码不能为空");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static SmtpClient CreateClient(SmtpEndpoint endpoint, string username, string password) =>
|
||||||
|
new(endpoint.Host, endpoint.Port)
|
||||||
|
{
|
||||||
|
EnableSsl = endpoint.EnableSsl,
|
||||||
|
DeliveryMethod = SmtpDeliveryMethod.Network,
|
||||||
|
UseDefaultCredentials = false,
|
||||||
|
Credentials = new NetworkCredential(username, password),
|
||||||
|
Timeout = 30_000
|
||||||
|
};
|
||||||
|
|
||||||
|
private static async Task SendWithClientAsync(
|
||||||
|
SmtpClient client,
|
||||||
|
MailMessage message,
|
||||||
|
CancellationToken cancellationToken)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
await client.SendMailAsync(message, cancellationToken);
|
||||||
|
}
|
||||||
|
catch (SmtpException ex)
|
||||||
|
{
|
||||||
|
throw new BusinessException($"SMTP 发送失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
catch (Exception ex) when (ex is InvalidOperationException or FormatException)
|
||||||
|
{
|
||||||
|
throw new BusinessException($"SMTP 发送失败:{ex.Message}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
360
src/Oms.Application/Services/System/SystemBatchEmailService.cs
Normal file
360
src/Oms.Application/Services/System/SystemBatchEmailService.cs
Normal file
@ -0,0 +1,360 @@
|
|||||||
|
using Fake.Application;
|
||||||
|
using Microsoft.AspNetCore.Mvc;
|
||||||
|
using Oms.Application.Dtos.Shared;
|
||||||
|
using Oms.Application.Dtos.System;
|
||||||
|
using Oms.Application.Shared;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
|
||||||
|
namespace Oms.Application.Services.System;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 批量发送邮件:模板、邮箱配置、SMTP 测试与单封发送。
|
||||||
|
/// </summary>
|
||||||
|
[ApiExplorerSettings(GroupName = "OMS-System")]
|
||||||
|
public class SystemBatchEmailService : ApplicationService
|
||||||
|
{
|
||||||
|
private const string SecretMask = "******";
|
||||||
|
private const string DefaultTemplateName = "默认模板";
|
||||||
|
|
||||||
|
private ISystemBatchEmailTemplateRepository TemplateRepository =>
|
||||||
|
LazyServiceProvider.GetRequiredService<ISystemBatchEmailTemplateRepository>();
|
||||||
|
|
||||||
|
private ISystemBatchEmailAccountRepository AccountRepository =>
|
||||||
|
LazyServiceProvider.GetRequiredService<ISystemBatchEmailAccountRepository>();
|
||||||
|
|
||||||
|
private ISystemBatchEmailRecordRepository RecordRepository =>
|
||||||
|
LazyServiceProvider.GetRequiredService<ISystemBatchEmailRecordRepository>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询当前用户邮件发送记录(分页)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("/system/batch-email/records/list")]
|
||||||
|
public async Task<OmsPageResult<BatchEmailRecordOutput>> GetRecordListAsync([FromQuery] BatchEmailRecordListInput input)
|
||||||
|
{
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var pageIndex = OmsDocHelper.NormalizePage(input.Page);
|
||||||
|
var pageSize = OmsDocHelper.NormalizePageSize(input.PageSize);
|
||||||
|
var (items, total) = await RecordRepository.GetPagedByOwnerAsync(ownerUserId, pageIndex, pageSize);
|
||||||
|
return new OmsPageResult<BatchEmailRecordOutput>
|
||||||
|
{
|
||||||
|
Items = items.Select(MapRecord).ToList(),
|
||||||
|
Total = total
|
||||||
|
};
|
||||||
|
}
|
||||||
|
[HttpGet("/system/batch-email/templates/list")]
|
||||||
|
public async Task<List<BatchEmailTemplateOutput>> GetTemplateListAsync()
|
||||||
|
{
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var list = await TemplateRepository.GetListByOwnerAsync(ownerUserId);
|
||||||
|
if (list.Count == 0)
|
||||||
|
{
|
||||||
|
await EnsureDefaultTemplateAsync(ownerUserId);
|
||||||
|
list = await TemplateRepository.GetListByOwnerAsync(ownerUserId);
|
||||||
|
}
|
||||||
|
|
||||||
|
return list.Select(MapTemplate).ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存邮件模板(按名称 upsert)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/system/batch-email/templates/save")]
|
||||||
|
public async Task SaveTemplateAsync(BatchEmailTemplateSaveInput input)
|
||||||
|
{
|
||||||
|
var name = input.Name?.Trim() ?? string.Empty;
|
||||||
|
var content = input.Content?.Trim() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
throw new BusinessException("模板名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(content))
|
||||||
|
{
|
||||||
|
throw new BusinessException("模板内容不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var subject = string.IsNullOrWhiteSpace(input.Subject) ? name : input.Subject.Trim();
|
||||||
|
var existing = await TemplateRepository.GetByOwnerAndNameAsync(ownerUserId, name);
|
||||||
|
if (existing == null)
|
||||||
|
{
|
||||||
|
var count = (await TemplateRepository.GetListByOwnerAsync(ownerUserId)).Count;
|
||||||
|
await TemplateRepository.InsertAsync(new SystemBatchEmailTemplate
|
||||||
|
{
|
||||||
|
OwnerUserId = ownerUserId,
|
||||||
|
TemplateName = name,
|
||||||
|
Subject = subject,
|
||||||
|
Content = input.Content ?? string.Empty,
|
||||||
|
SortOrder = count
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
existing.Subject = subject;
|
||||||
|
existing.Content = input.Content ?? string.Empty;
|
||||||
|
await TemplateRepository.UpdateAsync(existing);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 删除邮件模板。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/system/batch-email/templates/delete")]
|
||||||
|
public async Task DeleteTemplateAsync(BatchEmailTemplateDeleteInput input)
|
||||||
|
{
|
||||||
|
var name = input.Name?.Trim() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(name))
|
||||||
|
{
|
||||||
|
throw new BusinessException("模板名称不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var entity = await TemplateRepository.GetByOwnerAndNameAsync(ownerUserId, name);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
throw new BusinessException("模板不存在");
|
||||||
|
}
|
||||||
|
|
||||||
|
await TemplateRepository.DeleteAsync(entity);
|
||||||
|
var remain = await TemplateRepository.GetListByOwnerAsync(ownerUserId);
|
||||||
|
if (remain.Count == 0)
|
||||||
|
{
|
||||||
|
await EnsureDefaultTemplateAsync(ownerUserId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 查询当前用户邮箱配置。
|
||||||
|
/// </summary>
|
||||||
|
[HttpGet("/system/batch-email/account")]
|
||||||
|
public async Task<BatchEmailAccountOutput?> GetAccountAsync()
|
||||||
|
{
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var entity = await AccountRepository.GetByOwnerAsync(ownerUserId);
|
||||||
|
return entity == null ? null : MapAccount(entity, maskSecret: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 保存当前用户邮箱配置。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/system/batch-email/account/save")]
|
||||||
|
public async Task SaveAccountAsync(BatchEmailAccountSaveInput input)
|
||||||
|
{
|
||||||
|
var username = input.SmtpUsername?.Trim() ?? string.Empty;
|
||||||
|
if (string.IsNullOrWhiteSpace(username))
|
||||||
|
{
|
||||||
|
throw new BusinessException("SMTP 账号不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var password = input.SmtpPassword ?? string.Empty;
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var entity = await AccountRepository.GetByOwnerAsync(ownerUserId);
|
||||||
|
if (string.IsNullOrWhiteSpace(password) || (BatchEmailSmtpHelper.IsSecretMask(password) && entity == null))
|
||||||
|
{
|
||||||
|
throw new BusinessException("SMTP 密码不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
var serverType = NormalizeServerType(input.ServerType);
|
||||||
|
if (entity == null)
|
||||||
|
{
|
||||||
|
await AccountRepository.InsertAsync(new SystemBatchEmailAccount
|
||||||
|
{
|
||||||
|
OwnerUserId = ownerUserId,
|
||||||
|
SmtpUsername = username,
|
||||||
|
SmtpPassword = password,
|
||||||
|
ServerType = serverType,
|
||||||
|
FromName = NormalizeOptional(input.FromName)
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.SmtpUsername = username;
|
||||||
|
if (!BatchEmailSmtpHelper.IsSecretMask(password))
|
||||||
|
{
|
||||||
|
entity.SmtpPassword = password;
|
||||||
|
}
|
||||||
|
|
||||||
|
entity.ServerType = serverType;
|
||||||
|
entity.FromName = NormalizeOptional(input.FromName);
|
||||||
|
await AccountRepository.UpdateAsync(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 测试 SMTP 连接(向发件账号自身发送测试邮件)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/system/batch-email/test-connection")]
|
||||||
|
public async Task TestConnectionAsync(BatchEmailTestConnectionInput input)
|
||||||
|
{
|
||||||
|
var password = await ResolvePasswordForSendAsync(input.SmtpPassword);
|
||||||
|
await BatchEmailSmtpHelper.TestConnectionAsync(
|
||||||
|
input.SmtpUsername,
|
||||||
|
password,
|
||||||
|
input.ServerType);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送单封邮件(批量发送时由前端逐条调用)。
|
||||||
|
/// </summary>
|
||||||
|
[HttpPost("/system/batch-email/send-one")]
|
||||||
|
public async Task SendOneAsync(BatchEmailSendOneInput input)
|
||||||
|
{
|
||||||
|
var password = await ResolvePasswordForSendAsync(input.SmtpPassword);
|
||||||
|
await BatchEmailSmtpHelper.SendAsync(
|
||||||
|
input.SmtpUsername,
|
||||||
|
password,
|
||||||
|
input.ServerType,
|
||||||
|
input.To,
|
||||||
|
input.Subject,
|
||||||
|
input.Body,
|
||||||
|
input.BodyType,
|
||||||
|
input.FromName);
|
||||||
|
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var sender = ResolveSenderDisplay(input);
|
||||||
|
await RecordRepository.InsertAsync(new SystemBatchEmailRecord
|
||||||
|
{
|
||||||
|
OwnerUserId = ownerUserId,
|
||||||
|
ToEmail = input.To.Trim(),
|
||||||
|
Subject = input.Subject ?? string.Empty,
|
||||||
|
Content = input.Body ?? string.Empty,
|
||||||
|
SendTime = DateTime.Now,
|
||||||
|
Sender = sender,
|
||||||
|
SenderEmail = input.SmtpUsername.Trim()
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<string> ResolvePasswordForSendAsync(string? passwordInput)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(passwordInput) && !BatchEmailSmtpHelper.IsSecretMask(passwordInput))
|
||||||
|
{
|
||||||
|
return passwordInput;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ownerUserId = GetCurrentUserId();
|
||||||
|
var entity = await AccountRepository.GetByOwnerAsync(ownerUserId);
|
||||||
|
if (entity == null || string.IsNullOrWhiteSpace(entity.SmtpPassword))
|
||||||
|
{
|
||||||
|
throw new BusinessException("SMTP 密码不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
return entity.SmtpPassword;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task EnsureDefaultTemplateAsync(int ownerUserId)
|
||||||
|
{
|
||||||
|
var existing = await TemplateRepository.GetByOwnerAndNameAsync(ownerUserId, DefaultTemplateName);
|
||||||
|
if (existing != null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await TemplateRepository.InsertAsync(new SystemBatchEmailTemplate
|
||||||
|
{
|
||||||
|
OwnerUserId = ownerUserId,
|
||||||
|
TemplateName = DefaultTemplateName,
|
||||||
|
Subject = "订单通知 - {客户单号}",
|
||||||
|
Content = """
|
||||||
|
尊敬的客户:您好!
|
||||||
|
|
||||||
|
感谢您选择阿里巴巴及我们的物流服务,衷心感谢您的信任与支持!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
您的订单{客户单号},最新物流轨迹显示:{最新轨迹}。为避免包裹滞留或退回,烦请您及时联系收件人核实以下信息:
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
1.收件地址是否准确、完整(含邮编、门牌号等);
|
||||||
|
|
||||||
|
2.收件人联系方式是否畅通;
|
||||||
|
|
||||||
|
3.是否需协调二次派送时间。
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
后续如有物流疑问,可随时通过平台客服联系我们,我们将全力协助处理!
|
||||||
|
|
||||||
|
顺祝商祺!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
Alibaba.com Logistics
|
||||||
|
|
||||||
|
一代国际深圳仓客户服务中心
|
||||||
|
""",
|
||||||
|
SortOrder = 0
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private static BatchEmailTemplateOutput MapTemplate(SystemBatchEmailTemplate entity) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Name = entity.TemplateName,
|
||||||
|
Subject = entity.Subject,
|
||||||
|
Content = entity.Content
|
||||||
|
};
|
||||||
|
|
||||||
|
private static BatchEmailAccountOutput MapAccount(SystemBatchEmailAccount entity, bool maskSecret) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
SmtpUsername = entity.SmtpUsername,
|
||||||
|
SmtpPassword = maskSecret && !string.IsNullOrWhiteSpace(entity.SmtpPassword) ? SecretMask : entity.SmtpPassword,
|
||||||
|
ServerType = entity.ServerType,
|
||||||
|
FromName = entity.FromName
|
||||||
|
};
|
||||||
|
|
||||||
|
private static BatchEmailRecordOutput MapRecord(SystemBatchEmailRecord entity) =>
|
||||||
|
new()
|
||||||
|
{
|
||||||
|
Id = entity.Id,
|
||||||
|
ToEmail = entity.ToEmail,
|
||||||
|
Subject = entity.Subject,
|
||||||
|
Content = entity.Content,
|
||||||
|
SendTime = entity.SendTime,
|
||||||
|
Sender = entity.Sender
|
||||||
|
};
|
||||||
|
|
||||||
|
private string ResolveSenderDisplay(BatchEmailSendOneInput input)
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrWhiteSpace(input.FromName))
|
||||||
|
{
|
||||||
|
return input.FromName.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!string.IsNullOrWhiteSpace(CurrentUser.UserName))
|
||||||
|
{
|
||||||
|
return CurrentUser.UserName.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
return input.SmtpUsername.Trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string NormalizeServerType(string? serverType)
|
||||||
|
{
|
||||||
|
var value = (serverType ?? "qiye").Trim().ToLowerInvariant();
|
||||||
|
return value switch
|
||||||
|
{
|
||||||
|
"qiye" or "qiye_ssl" or "aliyun" or "aliyun_ssl" or "mxhichina" => value,
|
||||||
|
_ => "qiye"
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string? NormalizeOptional(string? value)
|
||||||
|
{
|
||||||
|
var text = value?.Trim();
|
||||||
|
return string.IsNullOrWhiteSpace(text) ? null : text;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int GetCurrentUserId()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var id = CurrentUser.GetId<int>();
|
||||||
|
return id > 0 ? id : 0;
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
using Oms.Domain.Shared;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
[SugarTable("t_system_batch_email_account")]
|
||||||
|
public class SystemBatchEmailAccount : OmsAuditedAggregateRoot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 所属用户(RBAC UserId)。
|
||||||
|
/// </summary>
|
||||||
|
public int OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMTP 登录账号。
|
||||||
|
/// </summary>
|
||||||
|
public string SmtpUsername { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMTP 密码/授权码。
|
||||||
|
/// </summary>
|
||||||
|
public string SmtpPassword { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// SMTP 类型:qiye / qiye_ssl / aliyun / aliyun_ssl / mxhichina。
|
||||||
|
/// </summary>
|
||||||
|
public string ServerType { get; set; } = "qiye";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发件人显示名称。
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
|
public string? FromName { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using Oms.Domain.Shared;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
[SugarTable("t_system_batch_email_record")]
|
||||||
|
public class SystemBatchEmailRecord : OmsAuditedAggregateRoot
|
||||||
|
{
|
||||||
|
public int OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
public string ToEmail { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
[SugarColumn(ColumnDataType = "text", IsNullable = true)]
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
public DateTime SendTime { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发送人展示名。
|
||||||
|
/// </summary>
|
||||||
|
public string Sender { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 发件邮箱账号。
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(IsNullable = true)]
|
||||||
|
public string? SenderEmail { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
using Oms.Domain.Shared;
|
||||||
|
using SqlSugar;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
[SugarTable("t_system_batch_email_template")]
|
||||||
|
public class SystemBatchEmailTemplate : OmsAuditedAggregateRoot
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 所属用户(RBAC UserId)。
|
||||||
|
/// </summary>
|
||||||
|
public int OwnerUserId { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 模板名称。
|
||||||
|
/// </summary>
|
||||||
|
public string TemplateName { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件主题(支持占位符)。
|
||||||
|
/// </summary>
|
||||||
|
public string Subject { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 邮件正文(支持占位符)。
|
||||||
|
/// </summary>
|
||||||
|
[SugarColumn(ColumnDataType = "text", IsNullable = true)]
|
||||||
|
public string Content { get; set; } = string.Empty;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 排序(越小越靠前)。
|
||||||
|
/// </summary>
|
||||||
|
public int SortOrder { get; set; }
|
||||||
|
}
|
||||||
@ -0,0 +1,9 @@
|
|||||||
|
using Fake.Domain.Repositories;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
|
||||||
|
public interface ISystemBatchEmailAccountRepository : IRepository<SystemBatchEmailAccount>
|
||||||
|
{
|
||||||
|
Task<SystemBatchEmailAccount?> GetByOwnerAsync(int ownerUserId, CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@ -0,0 +1,13 @@
|
|||||||
|
using Fake.Domain.Repositories;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
|
||||||
|
public interface ISystemBatchEmailRecordRepository : IRepository<SystemBatchEmailRecord>
|
||||||
|
{
|
||||||
|
Task<(List<SystemBatchEmailRecord> Items, long Total)> GetPagedByOwnerAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
int pageIndex,
|
||||||
|
int pageSize,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@ -0,0 +1,14 @@
|
|||||||
|
using Fake.Domain.Repositories;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
|
||||||
|
namespace Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
|
||||||
|
public interface ISystemBatchEmailTemplateRepository : IRepository<SystemBatchEmailTemplate>
|
||||||
|
{
|
||||||
|
Task<List<SystemBatchEmailTemplate>> GetListByOwnerAsync(int ownerUserId, CancellationToken cancellationToken = default);
|
||||||
|
|
||||||
|
Task<SystemBatchEmailTemplate?> GetByOwnerAndNameAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
string templateName,
|
||||||
|
CancellationToken cancellationToken = default);
|
||||||
|
}
|
||||||
@ -0,0 +1,17 @@
|
|||||||
|
using Fake.DependencyInjection;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
using Oms.SqlSugarCore.Repositories;
|
||||||
|
|
||||||
|
namespace Oms.SqlSugarCore.System;
|
||||||
|
|
||||||
|
public class SystemBatchEmailAccountRepository
|
||||||
|
: OmsSqlSugarRepository<SystemBatchEmailAccount>,
|
||||||
|
ISystemBatchEmailAccountRepository,
|
||||||
|
IScopedDependency
|
||||||
|
{
|
||||||
|
public Task<SystemBatchEmailAccount?> GetByOwnerAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
FirstOrDefaultAsync(x => x.OwnerUserId == ownerUserId, cancellationToken);
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
using Fake.DependencyInjection;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
using Oms.SqlSugarCore.Repositories;
|
||||||
|
|
||||||
|
namespace Oms.SqlSugarCore.System;
|
||||||
|
|
||||||
|
public class SystemBatchEmailRecordRepository
|
||||||
|
: OmsSqlSugarRepository<SystemBatchEmailRecord>,
|
||||||
|
ISystemBatchEmailRecordRepository,
|
||||||
|
IScopedDependency
|
||||||
|
{
|
||||||
|
public async Task<(List<SystemBatchEmailRecord> Items, long Total)> GetPagedByOwnerAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
int pageIndex,
|
||||||
|
int pageSize,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var total = await CountAsync(x => x.OwnerUserId == ownerUserId, cancellationToken);
|
||||||
|
var items = await GetPagedListAsync(
|
||||||
|
x => x.OwnerUserId == ownerUserId,
|
||||||
|
pageIndex,
|
||||||
|
pageSize,
|
||||||
|
new Dictionary<string, bool>
|
||||||
|
{
|
||||||
|
[nameof(SystemBatchEmailRecord.SendTime)] = false,
|
||||||
|
[nameof(SystemBatchEmailRecord.Id)] = false
|
||||||
|
},
|
||||||
|
cancellationToken);
|
||||||
|
return (items, total);
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,31 @@
|
|||||||
|
using Fake.DependencyInjection;
|
||||||
|
using Oms.Domain.SystemAggregate.Entities;
|
||||||
|
using Oms.Domain.SystemAggregate.Repositories;
|
||||||
|
using Oms.SqlSugarCore.Repositories;
|
||||||
|
|
||||||
|
namespace Oms.SqlSugarCore.System;
|
||||||
|
|
||||||
|
public class SystemBatchEmailTemplateRepository
|
||||||
|
: OmsSqlSugarRepository<SystemBatchEmailTemplate>,
|
||||||
|
ISystemBatchEmailTemplateRepository,
|
||||||
|
IScopedDependency
|
||||||
|
{
|
||||||
|
public async Task<List<SystemBatchEmailTemplate>> GetListByOwnerAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
CancellationToken cancellationToken = default)
|
||||||
|
{
|
||||||
|
var list = await GetListAsync(x => x.OwnerUserId == ownerUserId, cancellationToken: cancellationToken);
|
||||||
|
return list
|
||||||
|
.OrderBy(x => x.SortOrder)
|
||||||
|
.ThenBy(x => x.Id)
|
||||||
|
.ToList();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<SystemBatchEmailTemplate?> GetByOwnerAndNameAsync(
|
||||||
|
int ownerUserId,
|
||||||
|
string templateName,
|
||||||
|
CancellationToken cancellationToken = default) =>
|
||||||
|
FirstOrDefaultAsync(
|
||||||
|
x => x.OwnerUserId == ownerUserId && x.TemplateName == templateName,
|
||||||
|
cancellationToken);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue
Block a user