邮件模板生成优化

dev
嵇文龙 5 days ago
parent 79fe1625b6
commit 4db89893f4

@ -283,10 +283,10 @@ namespace DS.Module.Core
BL_CONFIRMOR_PA = 453,
/// <summary>
/// 开船通知PA
/// 分单提单确认方PA
/// </summary>
[Description("开船通知PA")]
DEPARTURE_NOTICE_PA = 454,
[Description("分单提单确认方PA")]
SUB_BL_CONFIRMOR_PA = 454,
#endregion
#region 基础数据审核

@ -78,24 +78,21 @@ namespace DS.Module.QuartzModuleInstall
/// <param name="configuration"></param>
public static void AddOpQuartzModuleInstall(this IServiceCollection services, IConfiguration configuration)
{
int jobCount = 0;
//var jobKey1 = new JobKey(nameof(WSLReportJob));
//string cron1 = configuration["JobConfig:" + jobKey1.Name];
//if (!string.IsNullOrEmpty(cron1))
//{
// services.AddQuartz(q =>
// {
// q.UseMicrosoftDependencyInjectionJobFactory();
// q.AddJob<WSLReportJob>(opts => opts.WithIdentity(jobKey1));
// q.AddTrigger(opts => opts
// .ForJob(jobKey1)
// .WithIdentity(nameof(WSLReportJob) + "-trigger")
// .WithCronSchedule(cron1)
// );
// });
// jobCount++;
//}
var jobKey1 = new JobKey(nameof(WSLReportJob));
string cron1 = configuration["JobConfig:" + jobKey1.Name];
if (!string.IsNullOrEmpty(cron1))
{
services.AddQuartz(q =>
{
q.UseMicrosoftDependencyInjectionJobFactory();
q.AddJob<WSLReportJob>(opts => opts.WithIdentity(jobKey1));
q.AddTrigger(opts => opts
.ForJob(jobKey1)
.WithIdentity(nameof(WSLReportJob) + "-trigger")
.WithCronSchedule(cron1)
);
});
}
var jobKey2 = new JobKey(nameof(BackgroundTaskJob));
services.AddQuartz(q =>
@ -110,8 +107,23 @@ namespace DS.Module.QuartzModuleInstall
.WithIntervalInMinutes(5)
.RepeatForever()));
});
jobCount++;
var jobKey3 = new JobKey(nameof(PATaskJob));
string cron3 = configuration["JobConfig:" + jobKey3.Name];
if (!string.IsNullOrEmpty(cron3))
{
services.AddQuartz(q =>
{
// 配置 Quartz
q.UseMicrosoftDependencyInjectionJobFactory();
q.AddJob<PATaskJob>(opts => opts.WithIdentity(jobKey3));
q.AddTrigger(opts => opts
.ForJob(jobKey3)
.WithIdentity(nameof(PATaskJob) + "-trigger")
.WithCronSchedule(cron3)
);
});
}
//var jobKey2 = new JobKey(nameof(WSLWeeklyReportJob));
//services.AddQuartz(q =>
@ -125,7 +137,7 @@ namespace DS.Module.QuartzModuleInstall
// );
//});
if (jobCount > 0)
// 添加 Quartz 主机服务
services.AddQuartzServer(q => q.WaitForJobsToComplete = true);
}
}

@ -45,7 +45,7 @@ namespace DS.WMS.Core.QuarztJobs
{
if (api.BaseUri == null)
{
var baseUrl = configuration["TaskMail:FileBaseUrl"];
var baseUrl = configuration["TaskMail:OpBaseUrl"];
if (string.IsNullOrEmpty(baseUrl))
{
logger.LogInformation("未配置【定时作业触发】请求基础URL");

@ -48,9 +48,6 @@ namespace DS.WMS.Core.QuarztJobs
var dbLinks = await db.Queryable<Module.SqlSugar.SysTenantLink>().ToListAsync();
foreach (var dbLink in dbLinks)
{
//if (configuration["AutoFeeTemplate:Tenant"] != dbLink.TenantId.ToString())
// continue;
var adminUser = await db.Queryable<SysUser>()
.Where(x => x.TenantId == dbLink.TenantId && x.Status == 0 && x.UserType == 1)
.OrderByDescending(x => x.CreateTime)

@ -0,0 +1,118 @@
using System.Net.Http.Headers;
using DS.Module.Core;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.Sys.Entity;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Quartz;
using SqlSugar;
namespace DS.WMS.Core.QuarztJobs
{
/// <summary>
/// PA任务生成
/// </summary>
public sealed class PATaskJob : IJob
{
static readonly ApiFox api;
ISqlSugarClient? db;
IConfiguration configuration;
ILogger<PATaskJob> logger;
static PATaskJob()
{
api = new ApiFox();
}
/// <summary>
/// 初始化
/// </summary>
/// <param name="serviceProvider"></param>
public PATaskJob(IServiceProvider serviceProvider)
{
db = serviceProvider.GetRequiredService<ISqlSugarClient>();
configuration = serviceProvider.GetRequiredService<IConfiguration>();
logger = serviceProvider.GetRequiredService<ILogger<PATaskJob>>();
}
/// <summary>
/// 生成PA任务
/// </summary>
/// <param name="context"></param>
/// <returns></returns>
public async Task Execute(IJobExecutionContext context)
{
logger.LogInformation("开始运行【PA任务】生成...");
db.QueryFilter.Clear();
var dbLinks = await db.Queryable<Module.SqlSugar.SysTenantLink>().ToListAsync();
foreach (var dbLink in dbLinks)
{
if (dbLink.TenantId.ToString() != configuration["TaskMail:DefaultSetting:Tenant"])
continue;
var adminUser = await db.Queryable<SysUser>()
.Where(x => x.TenantId == dbLink.TenantId && x.Status == 0 && x.UserType == 1)
.OrderByDescending(x => x.CreateTime)
.Select(x => new
{
x.Id,
x.UserName,
x.Password,
x.DefaultOrgId,
x.DefaultOrgName,
x.TenantId,
x.TenantName
}).FirstAsync();
if (adminUser == null)
{
logger.LogInformation("未能获取租户系统管理员租户ID" + dbLink.TenantId);
continue;
}
var tokenModel = new JwtHelper.JwtTokenModel
{
Uid = adminUser.Id.ToString(),
Name = adminUser.UserName,
OrgId = adminUser.DefaultOrgId.ToString(),
TenantId = adminUser.TenantId.ToString(),
TenantName = adminUser.TenantName
};
var token = JwtHelper.Encrypt(tokenModel, false, true);
await SendRequestAsync(token);
}
}
internal async Task SendRequestAsync(string token)
{
if (api.BaseUri == null)
{
var baseUrl = configuration["TaskMail:OpBaseUrl"];
if (string.IsNullOrEmpty(baseUrl))
{
logger.LogInformation("未配置【PA任务】请求基础URL");
return;
}
api.BaseUri = new Uri(baseUrl, UriKind.Absolute);
}
api.DefaultHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
if (!int.TryParse(configuration["TaskMail:DefaultSetting:ETDDays"], out int days))
days = 3;
var response = await api.SendRequestAsync(HttpMethod.Post, configuration["TaskMail:PATask"]!, new
{
businessType = (int)BusinessType.OceanShippingExport,
etd = DateTime.Now.Date.AddDays(days)
});
if (response.IsSuccessStatusCode)
logger.LogInformation("【PA任务】生成完成...");
else
logger.LogInformation($"【PA任务】生成失败{response.ToString()}),详情查看日志!");
}
}
}

@ -1,4 +1,5 @@
using SqlSugar;
using DS.WMS.Core.Op.Entity;
using SqlSugar;
namespace DS.WMS.Core.TaskInteraction.Entity
{
@ -38,6 +39,12 @@ namespace DS.WMS.Core.TaskInteraction.Entity
[SugarColumn(ColumnDescription = "数据源内容", IsNullable = true)]
public string? Content { get; set; }
/// <summary>
/// 业务类型
/// </summary>
[SugarColumn(ColumnDescription = "业务类型")]
public BusinessType BusinessType { get; set; }
/// <summary>
/// 创建人
/// </summary>

@ -31,6 +31,12 @@ namespace DS.WMS.Core.TaskInteraction.Entity
/// </summary>
[SugarColumn(ColumnDescription = "文件类型", IsNullable = true)]
public FileFormat? FileType { get; set; }
/// <summary>
/// 生成条件
/// </summary>
[SugarColumn(ColumnDescription = "生成条件", ColumnDataType = "longtext", IsNullable = true)]
public string? Condition { get; set; }
}
/// <summary>

@ -3,6 +3,7 @@ using DS.Module.Core.Data;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.TaskInteraction.Dtos;
using DS.WMS.Core.TaskInteraction.Entity;
using Microsoft.AspNetCore.Mvc;
namespace DS.WMS.Core.TaskInteraction.Interface
{
@ -82,9 +83,9 @@ namespace DS.WMS.Core.TaskInteraction.Interface
/// <summary>
/// 使用邮件草稿发送邮件
/// </summary>
/// <param name="draft">邮件草稿</param>
/// <param name="draftId">邮件草稿ID</param>
/// <returns></returns>
Task<DataResult> SendAsync(MailDraft draft);
Task<DataResult> SendAsync(long draftId);
/// <summary>
/// 根据任务类型的默认配置创建草稿
@ -106,6 +107,14 @@ namespace DS.WMS.Core.TaskInteraction.Interface
Task<DataResult<MailDraft>> CreateDraftAsync(BusinessTask task, BusinessTaskMail mailConfig,
MailTemplateModel? templateModel = null, bool saveDraft = false);
/// <summary>
/// 保存邮件草稿
/// </summary>
/// <param name="draft">邮件草稿</param>
/// <param name="sendAfterSave">是否保存后发送</param>
/// <returns></returns>
Task<DataResult> SaveDraftAsync(MailDraft draft, bool sendAfterSave);
/// <summary>
/// 根据业务ID和类型获取模板所需数据
/// </summary>
@ -114,5 +123,23 @@ namespace DS.WMS.Core.TaskInteraction.Interface
/// <param name="bsType">业务类型</param>
/// <returns></returns>
Task<MailTemplateModel> GetBusinessModelAsync(BusinessTaskMail mailConfig, long bsId, BusinessType? bsType = null);
/// <summary>
/// 获取数据提供程序的数据输出
/// </summary>
/// <param name="providerId">提供程序ID</param>
/// <param name="businessId">业务ID</param>
/// <returns></returns>
Task<DataResult<dynamic?>> GetProviderDataAsync(long providerId, long? businessId = null);
/// <summary>
/// 预览邮件模板
/// </summary>
/// <param name="id">邮件模板ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="number">编号</param>
/// <returns></returns>
[HttpGet, Route("Preview")]
Task<DataResult<MailDraft>> PreviewAsync(long id, BusinessType businessType, string number);
}
}

@ -1,10 +1,19 @@
namespace DS.WMS.Core.TaskInteraction.Interface
using DS.Module.Core;
using DS.WMS.Core.Op.Entity;
namespace DS.WMS.Core.TaskInteraction.Interface
{
/// <summary>
/// PA任务
/// </summary>
public interface IPreAlertTaskService : ITaskService
{
/// <summary>
/// 创建PA任务
/// </summary>
/// <param name="businessType">业务类型</param>
/// <param name="etd">出港日期</param>
/// <returns></returns>
Task<DataResult> CreateTaskAsync(BusinessType businessType, DateTime etd);
}
}

@ -13,7 +13,6 @@ namespace DS.WMS.Core.TaskInteraction.Method.ActionExecutor.BLConfirm
/// </summary>
public class SubBLConfirmActionExecutor : ServiceBase, IActionExecutor
{
//ITaskManageBCService bCService;
ISeaExportTaskService taskService;
ITaskLogService logService;
@ -23,7 +22,6 @@ namespace DS.WMS.Core.TaskInteraction.Method.ActionExecutor.BLConfirm
/// <param name="provider"></param>
public SubBLConfirmActionExecutor(IServiceProvider provider) : base(provider)
{
//bCService = provider.GetRequiredService<ITaskManageBCService>();
taskService = provider.GetRequiredService<ISeaExportTaskService>();
logService = provider.GetRequiredService<ITaskLogService>();
}

@ -1,5 +1,4 @@
using DS.Module.Core;
using DS.Module.UserModule;
using DS.WMS.Core.Info.Interface;
using DS.WMS.Core.Op.Interface;
using DS.WMS.Core.Sys.Entity;

@ -1,6 +1,10 @@
using System.Collections.Concurrent;
using System.Net.Mail;
using System.Reflection;
using Autofac.Core;
using Castle.Core.Resource;
using DS.Module.Core;
using DS.Module.Core.Condition;
using DS.Module.Core.Data;
using DS.Module.Core.Extensions;
using DS.Module.PrintModule;
@ -14,8 +18,10 @@ using DS.WMS.Core.TaskInteraction.Entity;
using DS.WMS.Core.TaskInteraction.Interface;
using Fasterflect;
using HtmlAgilityPack;
using LanguageExt.Pretty;
using Masuit.Tools;
using Masuit.Tools.Systems;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
@ -51,6 +57,7 @@ namespace DS.WMS.Core.TaskInteraction.Method
readonly IConfiguration config;
readonly ILogger<MailTemplateService> logger;
readonly Lazy<IActionManagerService> actionService;
/// <summary>
/// 初始化
@ -60,6 +67,7 @@ namespace DS.WMS.Core.TaskInteraction.Method
{
config = provider.GetRequiredService<IConfiguration>();
logger = provider.GetRequiredService<ILogger<MailTemplateService>>();
actionService = new Lazy<IActionManagerService>(provider.GetRequiredService<IActionManagerService>());
Db.QueryFilter.Clear<ITenantId>();
TenantDb.QueryFilter.Clear<IOrgId>();
@ -252,6 +260,91 @@ namespace DS.WMS.Core.TaskInteraction.Method
{
var actionManager = ServiceProvider.GetRequiredService<IActionManagerService>();
model.Primary = await actionManager.GetBusinessDataAsync(bsId, bsType.GetValueOrDefault());
var custQuery = TenantDb.UnionAll(
TenantDb.Queryable<InfoClient>().Where(c => SqlFunc.Subqueryable<SeaExport>().Where(s => s.CustomerId == c.Id && s.Id == bsId).Any())
.Select(c => new InfoClient { Description = c.Description, Note = "controller" }),
TenantDb.Queryable<InfoClient>().Where(c => SqlFunc.Subqueryable<SeaExport>().Where(s => s.ForwarderId == c.Id && s.Id == bsId).Any())
.Select(c => new InfoClient { Description = c.Description, Note = "booking" }),
TenantDb.Queryable<InfoClient>().Where(c => SqlFunc.Subqueryable<SeaExport>().Where(s => s.ShipperCnId == c.Id && s.Id == bsId).Any())
.Select(c => new InfoClient { Description = c.Description, Note = "shipperCN" })
);
var custList = await custQuery.ToListAsync();
model.CustomerName = custList.Find(x => x.Note == "controller")?.Description ?? string.Empty;
model.ForwarderName = custList.Find(x => x.Note == "booking")?.Description ?? string.Empty;
model.DomesticShipperName = custList.Find(x => x.Note == "shipperCN")?.Description ?? string.Empty;
var userQuery = TenantDb.UnionAll(
TenantDb.Queryable<SeaExport>().InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == bsId)
.Select((s, u) => new Contact
{
Code = u.UserCode,
Email = u.Email,
EnName = u.UserEnName,
Name = u.UserName,
Mobile = u.Phone,
Tel = u.OfficePhone,
QQ = u.QQ,
CustomerType = "s"
}),
TenantDb.Queryable<SeaExport>().InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == bsId)
.Select((s, u) => new Contact
{
Code = u.UserCode,
Email = u.Email,
EnName = u.UserEnName,
Name = u.UserName,
Mobile = u.Phone,
Tel = u.OfficePhone,
QQ = u.QQ,
CustomerType = "o"
}),
TenantDb.Queryable<SeaExport>().InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == bsId)
.Select((s, u) => new Contact
{
Code = u.UserCode,
Email = u.Email,
EnName = u.UserEnName,
Name = u.UserName,
Mobile = u.Phone,
Tel = u.OfficePhone,
QQ = u.QQ,
CustomerType = "c"
}),
TenantDb.Queryable<SeaExport>().InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == bsId)
.Select((s, u) => new Contact
{
Code = u.UserCode,
Email = u.Email,
EnName = u.UserEnName,
Name = u.UserName,
Mobile = u.Phone,
Tel = u.OfficePhone,
QQ = u.QQ,
CustomerType = "d"
})
);
var userList = await userQuery.ToListAsync();
model.Sales = userList.Find(x => x.CustomerType == "s");
model.Operator = userList.Find(x => x.CustomerType == "o");
model.CustomerService = userList.Find(x => x.CustomerType == "c");
model.Document = userList.Find(x => x.CustomerType == "d");
//获取箱型价格
model.CtnPriceList = await TenantDb.Queryable<BusinessCtnPrice>().Where(x => x.BusinessId == model.BusinessId)
.Select(x => new BusinessCtnPrice
{
Ctn = x.Ctn,
CtnAll = x.CtnAll,
CtnNum = x.CtnNum,
QuotePrice = x.QuotePrice,
GuidePrice = x.GuidePrice,
FloorPrice = x.FloorPrice
}).ToListAsync();
}
else
{
@ -282,109 +375,6 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
}
if (model.Primary == null)
return model;
List<Tuple<long, string>> list = [];
long? forwarderId = null, shipperCNId = null, customerId = null;
var order = model.Primary as SeaExportRes;
order ??= await TenantDb.Queryable<SeaExport>().Where(x => x.Id == bsId)
.Select(x => new SeaExportRes
{
SaleId = x.SaleId,
OperatorId = x.OperatorId,
CustomerService = x.CustomerService,
Doc = x.Doc,
ForwarderId = x.ForwarderId,
ShipperCnId = x.ShipperCnId,
CustomerId = x.CustomerId
}).FirstAsync();
if (order != null)
{
list.Add(new Tuple<long, string>(order.SaleId, "sale"));
list.Add(new Tuple<long, string>(order.OperatorId, "op"));
list.Add(new Tuple<long, string>(order.CustomerService, "cs"));
list.Add(new Tuple<long, string>(order.Doc, "doc"));
forwarderId = order.ForwarderId;
shipperCNId = order.ShipperCnId;
customerId = order.CustomerId;
}
else
{
if (model.Primary is not IDictionary<string, object> dic)
return model;
if (dic.TryGetValue(nameof(SeaExport.SaleId), out object? sale))
list.Add(new Tuple<long, string>((long)sale, nameof(sale)));
if (dic.TryGetValue(nameof(SeaExport.OperatorId), out object? op))
list.Add(new Tuple<long, string>((long)op, nameof(op)));
if (dic.TryGetValue(nameof(SeaExport.CustomerService), out object? cs))
list.Add(new Tuple<long, string>((long)cs, nameof(cs)));
if (dic.TryGetValue(nameof(SeaExport.Doc), out object? doc))
list.Add(new Tuple<long, string>((long)doc, nameof(doc)));
if (dic.TryGetValue(nameof(SeaExport.ForwarderId), out object? forwarder) && forwarder != null)
forwarderId = (long)forwarder;
if (dic.TryGetValue(nameof(SeaExport.ShipperCnId), out object? shipperCN) && shipperCN != null)
shipperCNId = (long)shipperCN;
if (dic.TryGetValue(nameof(SeaExport.CustomerId), out object? customer) && customer != null)
customerId = (long)customer;
}
long?[] custIds = [forwarderId, shipperCNId, customerId];
var custList = await TenantDb.Queryable<InfoClient>().Where(x => custIds.Contains(x.Id))
.Select(x => new { x.Id, x.Description }).ToListAsync();
model.ForwarderName = custList.Find(x => x.Id == forwarderId)?.Description;
model.DomesticShipperName = custList.Find(x => x.Id == shipperCNId)?.Description;
model.CustomerName = custList.Find(x => x.Id == customerId)?.Description;
if (list.Count > 0)
{
var ids = list.Select(x => x.Item1).ToArray();
var userList = await Db.Queryable<SysUser>().Where(x => ids.Contains(x.Id)).Select(x => new Contact
{
Id = x.Id,
Code = x.UserCode,
Email = x.Email,
EnName = x.UserEnName,
Name = x.UserName,
Mobile = x.Phone,
Tel = x.OfficePhone,
QQ = x.QQ
}).ToListAsync();
long saleId = list.Find(x => x.Item2 == "sale")!.Item1;
model.Sales = userList.Find(x => x.Id == saleId);
long opId = list.Find(x => x.Item2 == "op")!.Item1;
model.Operator = userList.Find(x => x.Id == opId);
long csId = list.Find(x => x.Item2 == "cs")!.Item1;
model.CustomerService = userList.Find(x => x.Id == csId);
long docId = list.Find(x => x.Item2 == "doc")!.Item1;
model.Document = userList.Find(x => x.Id == docId);
}
//获取箱型价格
model.CtnPriceList = await TenantDb.Queryable<BusinessCtnPrice>().Where(x => x.BusinessId == model.BusinessId)
.Select(x => new BusinessCtnPrice
{
Ctn = x.Ctn,
CtnAll = x.CtnAll,
CtnNum = x.CtnNum,
QuotePrice = x.QuotePrice,
GuidePrice = x.GuidePrice,
FloorPrice = x.FloorPrice
}).ToListAsync();
return model;
}
@ -545,6 +535,17 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
}
/// <summary>
/// 使用邮件草稿发送邮件
/// </summary>
/// <param name="draftId">邮件草稿ID</param>
/// <returns></returns>
public async Task<DataResult> SendAsync(long draftId)
{
var draft = await TenantDb.Queryable<MailDraft>().FirstAsync(x => x.Id == draftId);
return await SendAsync(draft);
}
/// <summary>
/// 使用邮件草稿发送邮件
/// </summary>
@ -552,11 +553,26 @@ namespace DS.WMS.Core.TaskInteraction.Method
/// <returns></returns>
public async Task<DataResult> SendAsync(MailDraft draft)
{
ArgumentNullException.ThrowIfNull(draft, nameof(draft));
if (draft == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
if (string.IsNullOrEmpty(draft.SendTo) || string.IsNullOrEmpty(draft.Account) || string.IsNullOrEmpty(draft.Password) || string.IsNullOrEmpty(draft.Server) || draft.Port <= 0)
return DataResult.Failed("邮件信息不完整,请补全(收件人/发件SMTP配置后重试");
var mailResult = await Api.SendRequestAsync(HttpMethod.Post, config["TaskMail:MailApiUrl"], draft);
if (mailResult.IsSuccessStatusCode)
{
draft.HasSent = true;
draft.UpdateBy = UserId;
draft.UpdateTime = DateTime.Now;
await TenantDb.Updateable(draft).UpdateColumns(x => new
{
x.HasSent,
x.UpdateBy,
x.UpdateTime
}).ExecuteCommandAsync();
return DataResult.Successed($"已发邮件,收件人:" + string.Join(",", draft.SendTo) + "发件人:" + draft.Account);
}
return DataResult.Failed($"邮件发送失败API返回状态为{(int)mailResult.StatusCode} {mailResult.StatusCode}"
+ $"收件人:" + string.Join(",", draft.SendTo) + "发件人:" + draft.Account);
@ -589,8 +605,7 @@ namespace DS.WMS.Core.TaskInteraction.Method
/// <returns></returns>
public async Task<DataResult<MailDraft>> CreateDraftAsync(BusinessTask task, MailTemplateModel? templateModel = null, bool saveDraft = false)
{
var mailConfig = await TenantDb.Queryable<BusinessTaskMail>()
.Includes(x => x.Sender).Includes(x => x.Receivers).Includes(x => x.CC).Includes(x => x.Attachments)
var mailConfig = await TenantDb.Queryable<BusinessTaskMail>().Includes(x => x.Attachments)
.Where(x => SqlFunc.Subqueryable<TaskMailRelation>().Where(y => x.Id == y.MailId && y.TaskType == task.TaskType && y.IsDefault).Any())
.FirstAsync();
if (mailConfig == null)
@ -614,77 +629,197 @@ namespace DS.WMS.Core.TaskInteraction.Method
ArgumentNullException.ThrowIfNull(task, nameof(task));
templateModel ??= await GetBusinessModelAsync(mailConfig, task.BusinessId, task.BusinessType);
var order = await TenantDb.Queryable<SeaExport>().Where(x => x.Id == templateModel.BusinessId)
.Select(x => new
{
x.CustomerNo,
x.SaleId,
x.OperatorId,
x.CustomerService,
x.Doc,
x.CarrierId,
x.ForwarderId,
x.YardId,
x.TruckerId,
x.CustomerId
}).FirstAsync();
if (order == null)
return DataResult<MailDraft>.Failed($"未能获取订单({templateModel.BusinessId})数据");
//设置发件人
if (mailConfig.Sender == null)
return DataResult<MailDraft>.Failed("未设置发件人");
long senderId = 0;
if (mailConfig.Sender.IsSale)
senderId = order.SaleId;
else if (mailConfig.Sender.IsOperator)
senderId = order.OperatorId;
else if (mailConfig.Sender.IsCustomerService)
senderId = order.CustomerService;
else if (mailConfig.Sender.IsVouchingClerk)
senderId = order.Doc;
templateModel.Sender = await Db.Queryable<SysUser>().Where(x => x.Id == senderId).Select(x => new MailSender
{
DisplayName = x.UserName,
Phone = x.Phone,
SignatureHtml = x.SignatureHtml
}).FirstAsync();
if (templateModel.Sender == null)
return DataResult<MailDraft>.Failed("邮件模板未设置发件人");
var senderConfig = await TenantDb.Queryable<CodeUserEmail>().FirstAsync(x => x.CreateBy == senderId);
var query = TenantDb.UnionAll(
//-----发送人-----//
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailSender>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsSale).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "Sender"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.OperatorId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailSender>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsOperator).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "Sender"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.CustomerService == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailSender>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsCustomerService).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "Sender"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.Doc == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailSender>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsVouchingClerk).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "Sender"
}),
//-----抄送人-----//
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.SaleId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailCC>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsSale).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "CC"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.OperatorId == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailCC>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsOperator).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "CC"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.CustomerService == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailCC>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsCustomerService).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "CC"
}),
TenantDb.Queryable<SeaExport>()
.InnerJoin<SysUser>((s, u) => s.Doc == u.Id, "shippingweb8_dev.sys_user")
.Where((s, u) => s.Id == task.BusinessId && SqlFunc.Subqueryable<BusinessTaskMailCC>()
.Where(tms => tms.TaskMailId == mailConfig.Id && tms.IsVouchingClerk).Any())
.LeftJoin<CodeUserEmail>((s, u, um) => u.Id == um.CreateBy)
.Select((s, u, um) => new
{
u.Id,
u.UserName,
u.Phone,
u.Email,
u.SignatureHtml,
um.ShowName,
um.MailAccount,
um.Password,
um.SmtpServer,
um.SmtpPort,
um.SmtpSSL,
UserType = "CC"
})
);
var userList = await query.GroupBy(x => new { x.Id, x.UserType }).ToListAsync();
var senderConfig = userList.Find(x => x.UserType == "Sender");
if (senderConfig == null)
return DataResult<MailDraft>.Failed($"发件人用户:{templateModel.Sender.DisplayName} 未设置SMTP发件信息");
return DataResult<MailDraft>.Failed("邮件模板未设置发件人");
templateModel.Sender.MailAddress = senderConfig.MailAccount;
List<string> ccList = [];
if (mailConfig.CC != null)
templateModel.Sender = new MailSender
{
//设置抄送人
List<long> ccIds = [];
if (mailConfig.CC.IsSale)
ccIds.Add(order.SaleId);
else if (mailConfig.CC.IsOperator)
ccIds.Add(order.OperatorId);
else if (mailConfig.CC.IsCustomerService)
ccIds.Add(order.CustomerService);
else if (mailConfig.CC.IsVouchingClerk)
ccIds.Add(order.Doc);
if (ccIds.Count > 0)
ccList = await Db.Queryable<SysUser>().Where(x => ccIds.Contains(x.Id)).Select(x => x.Email).ToListAsync();
}
//设置收件人
if (mailConfig.Receivers == null || mailConfig.Receivers.Count == 0)
return DataResult<MailDraft>.Failed("邮件模板未设置收件人");
DisplayName = senderConfig.UserName,
Phone = senderConfig.Phone,
SignatureHtml = senderConfig.SignatureHtml,
MailAddress = senderConfig.MailAccount
};
List<string> ccList = userList.Where(x => x.UserType == "CC").Select(x => x.Email).ToList();
var receiverTypes = mailConfig.Receivers.Select(x => x.Value);
templateModel.Receivers = await TenantDb.Queryable<BusinessOrderContact>().Where(x => receiverTypes.Contains(x.CustomerType)
&& x.Email != null && x.Email != string.Empty && x.BusinessId == templateModel.BusinessId && x.BusinessType == templateModel.BusinessType)
.Select(x => new MailReceiver { DisplayName = x.Name, MailAddress = x.Email }).ToListAsync();
templateModel.Receivers = await TenantDb.Queryable<BusinessOrderContact>()
.Where(oc => SqlFunc.Subqueryable<BusinessTaskMailReceiver>().InnerJoin<BusinessTaskMail>(
(tmr, tm) => tmr.TaskMailId == tm.Id).Where((tmr, tm) => tmr.Value == oc.CustomerType).Any() &&
!SqlFunc.IsNullOrEmpty(oc.Email) && oc.BusinessId == templateModel.BusinessId && oc.BusinessType == templateModel.BusinessType)
.Select(oc => new MailReceiver
{
DisplayName = oc.Name,
MailAddress = oc.Email
}).ToListAsync();
if (templateModel.Receivers.Count == 0)
return DataResult<MailDraft>.Failed("邮件模板未设置收件人或订单未填写对应联系人");
string title, content = string.Empty;
var razorEngine = new RazorEngine();
@ -700,6 +835,8 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
//插入发件人签名
if (!string.IsNullOrEmpty(templateModel.Sender.SignatureHtml))
{
var htmlDoc = new HtmlDocument();
htmlDoc.LoadHtml(content);
var node = htmlDoc.GetElementbyId("sign");
@ -712,25 +849,33 @@ namespace DS.WMS.Core.TaskInteraction.Method
content = writer.ToString();
writer.Close();
}
}
List<MailDraftAttachment> attaches = mailConfig.Attachments == null ? [] : new List<MailDraftAttachment>(mailConfig.Attachments.Count);
try
{
//生成模板附件
if (mailConfig.Attachments?.Count > 0)
{
object? obj = templateModel.GetPropertyValue(nameof(MailTemplateModel.Primary), Flags.InstancePublic);
string json = JsonConvert.SerializeObject(obj ??= new { Id = templateModel.BusinessId });
if (json != "{}" && json != "[]")
{
if (Api.DefaultHeaders.Contains("Authorization"))
Api.DefaultHeaders.Remove("Authorization");
Api.DefaultHeaders.Add("Authorization", "Bearer " + User.GetToken());
string requestUrl = config["TaskMail:PrintBaseUrl"] + config["TaskMail:SQLPrint"];
long tenantId = long.Parse(User.TenantId);
string requestUrl = config["TaskMail:FileBaseUrl"] + config["TaskMail:SQLPrint"];
object? obj = templateModel.GetPropertyValue(nameof(MailTemplateModel.Primary), Flags.InstancePublic);
string json = JsonConvert.SerializeObject(obj ??= new { Id = templateModel.BusinessId });
foreach (var item in mailConfig.Attachments)
{
if (!string.IsNullOrEmpty(item.Condition))
{
var condition = JsonConvert.DeserializeObject<ConditionContent>(item.Condition);
if (!actionService.Value.IsMatch(templateModel.Primary, condition))
continue;
}
var req = new OpenPrintReq
{
ParamJsonStr = json,
@ -739,13 +884,13 @@ namespace DS.WMS.Core.TaskInteraction.Method
TenantId = tenantId
};
var reqResult = await Api.PostAsync<PrintResult>(requestUrl, req);
if (!reqResult.Data.Succeeded)
return DataResult<MailDraft>.Failed($"未能获取打印API生成的文件,请求地址:{requestUrl}");
if (reqResult.Data == null || !reqResult.Data.Succeeded)
return DataResult<MailDraft>.Failed($"未能获取打印模板生成的文件,请求地址:{requestUrl}");
string url = config["TaskMail:FileBaseUrl"] + @"/PrintTempFile/" + reqResult.Data.Data;
string url = config["TaskMail:PrintBaseUrl"] + @"/PrintTempFile/" + reqResult.Data.Data;
var response = await Api.SendRequestAsync(HttpMethod.Get, url);
if (!response.IsSuccessStatusCode)
return DataResult<MailDraft>.Failed($"未能获取打印API生成的文件,附件地址:{url}");
return DataResult<MailDraft>.Failed($"未能获取打印模板生成的文件,附件地址:{url}");
string? fileName = Path.GetFileName(reqResult.Data.Data);
if (!string.IsNullOrEmpty(item.FileName)) //设置了文件名
@ -765,6 +910,9 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
var byteArray = await response.Content.ReadAsByteArrayAsync();
//if (byteArray.Length == 0)
// return DataResult<MailDraft>.Failed($"打印模板返回了空文件,附件地址:{url}");
attaches.Add(new MailDraftAttachment
{
AttachName = fileName,
@ -772,6 +920,7 @@ namespace DS.WMS.Core.TaskInteraction.Method
});
}
}
}
var draft = new MailDraft
{
@ -783,9 +932,9 @@ namespace DS.WMS.Core.TaskInteraction.Method
Body = content,
CCTo = string.Join(";", ccList),
ShowName = string.IsNullOrEmpty(senderConfig.ShowName) ? templateModel.Sender.DisplayName : senderConfig.ShowName,
Account = senderConfig.MailAccount,
Password = senderConfig.Password,
Server = senderConfig.SmtpServer,
Account = senderConfig.MailAccount ?? string.Empty,
Password = senderConfig.Password ?? string.Empty,
Server = senderConfig.SmtpServer ?? string.Empty,
Port = senderConfig.SmtpPort.GetValueOrDefault(),
UseSSL = senderConfig.SmtpSSL.GetValueOrDefault(),
Attaches = attaches
@ -802,5 +951,91 @@ namespace DS.WMS.Core.TaskInteraction.Method
return DataResult<MailDraft>.FailedWithDesc(nameof(MultiLanguageConst.HttpRequestFailed));
}
}
/// <summary>
/// 保存邮件草稿
/// </summary>
/// <param name="draft">邮件草稿</param>
/// <param name="sendAfterSave">是否保存后发送</param>
/// <returns></returns>
public async Task<DataResult> SaveDraftAsync(MailDraft draft, bool sendAfterSave)
{
if (draft.Id != 0 && await TenantDb.Queryable<MailDraft>().AnyAsync(x => x.Id == draft.Id && x.HasSent))
return DataResult.Failed("禁止编辑已发送的邮件");
await TenantDb.Updateable(draft).IgnoreColumns(x => new
{
x.CreateBy,
x.CreateTime,
x.HasSent,
}).ExecuteCommandAsync();
return sendAfterSave ? await SendAsync(draft) : DataResult.Success;
}
/// <summary>
/// 获取数据提供程序的数据输出
/// </summary>
/// <param name="providerId">提供程序ID</param>
/// <param name="businessId">业务ID</param>
/// <returns></returns>
public async Task<DataResult<dynamic?>> GetProviderDataAsync(long providerId, long? businessId = null)
{
var provider = await TenantDb.Queryable<BusinessDataProvider>().FirstAsync(x => x.Id == providerId);
if (provider == null)
return DataResult<dynamic?>.FailedWithDesc(nameof(MultiLanguageConst.EmptyData));
if (businessId == null)
{
switch (provider.BusinessType)
{
case BusinessType.OceanShippingExport:
businessId = await TenantDb.Queryable<SeaExport>().Take(1).OrderBy(x => SqlFunc.GetRandom()).Select(x => x.Id).FirstAsync();
break;
case BusinessType.OceanShippingImport:
break;
default:
return DataResult<dynamic?>.FailedWithDesc(string.Format(
MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.BusinessNotSupported)),
provider.BusinessType.GetDescription()));
}
}
Type? type = Type.GetType(provider.TypeName, true);
var dataProvider = ConstructorExtensions.CreateInstance(type) as IDataProvider;
if (dataProvider == null)
return DataResult<dynamic?>.FailedWithDesc("创建数据提供程序失败:" + type.FullName);
var context = new DataFetchContext
{
BusinessId = businessId,
BusinessType = provider.BusinessType,
Content = provider.Content,
Db = Db,
TenantDb = TenantDb
};
await dataProvider.FetchDataAsync(context);
return DataResult<dynamic?>.Success(context.Data);
}
/// <summary>
/// 预览邮件模板
/// </summary>
/// <param name="id">邮件模板ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="number">编号</param>
/// <returns></returns>
[HttpGet, Route("Preview")]
public async Task<DataResult<MailDraft>> PreviewAsync(long id, BusinessType businessType, string number)
{
var mailConfig = (await GetAsync(id))?.Data;
if (mailConfig == null)
return DataResult<MailDraft>.Failed($"邮件模板ID{id} 无效");
BusinessTask task = new BusinessTask { BusinessType = businessType };
task.BusinessId = await TenantDb.Queryable<SeaExport>().Where(x => x.CustomerNo == number || x.MBLNO == number)
.Select(x => x.Id).FirstAsync();
return await CreateDraftAsync(task, mailConfig, saveDraft: false);
}
}
}

@ -1,11 +1,14 @@
using DS.Module.Core;
using DS.WMS.Core.Op.Dtos;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.TaskInteraction.Dtos;
using DS.WMS.Core.TaskInteraction.Entity;
using DS.WMS.Core.TaskInteraction.Interface;
using DS.WMS.Core.TaskPlat.Dtos;
using Masuit.Tools.Systems;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;
namespace DS.WMS.Core.TaskInteraction.Method
{
@ -16,6 +19,7 @@ namespace DS.WMS.Core.TaskInteraction.Method
{
IMailTemplateService mailTemplateService;
ITaskLogService logService;
IConfiguration config;
/// <summary>
/// 初始化
@ -25,6 +29,29 @@ namespace DS.WMS.Core.TaskInteraction.Method
{
mailTemplateService = provider.GetRequiredService<IMailTemplateService>();
logService = provider.GetRequiredService<ITaskLogService>();
config = provider.GetRequiredService<IConfiguration>();
}
/// <summary>
/// 创建PA任务
/// </summary>
/// <param name="businessType">业务类型</param>
/// <param name="etd">出港日期</param>
/// <returns></returns>
public async Task<DataResult> CreateTaskAsync(BusinessType businessType, DateTime etd)
{
var ids = await TenantDb.Queryable<SeaExport>().Where(x => SqlFunc.DateIsSame(x.ETD, etd))
.Select(x => x.Id).ToArrayAsync();
if (ids.Length == 0)
return DataResult.Success;
TaskCreationRequest request = new()
{
BusinessType = businessType,
Ids = ids,
TaskTypeName = TaskBaseTypeEnum.PRE_ALERT.ToString()
};
return await CreateMultipleTaskAsync(request);
}
/// <summary>
@ -61,7 +88,6 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
DataResult result = DataResult.Success;
DateTime dtNow = DateTime.Now;
List<BusinessTask> taskList = [];
List<BusinessTask> subTaskList = [];
@ -70,31 +96,45 @@ namespace DS.WMS.Core.TaskInteraction.Method
try
{
var orders = await TenantDb.Queryable<SeaExport>().Where(s => request.Ids.Contains(s.Id))
.Select((s) => new
{
s.Id,
s.CustomerNo,
s.CustomerId,
s.BLConfirmationId, //提单确认方
s.AgentId, //国外代理
s.IssueType,
s.IssuingWay,
s.MBLNO,
s.ETD,
s.Vessel,
s.Voyno,
s.SourceName,
s.SourceDetailName
.Select(s => new SeaExport
{
Id = s.Id,
CustomerNo = s.CustomerNo,
CustomerId = s.CustomerId,
BLConfirmationId = s.BLConfirmationId, //提单确认方
//s.AgentId, //国外代理
IssueType = s.IssueType,
IssuingWay = s.IssuingWay,
MBLNO = s.MBLNO,
ETD = s.ETD,
Vessel = s.Vessel,
Voyno = s.Voyno,
SourceName = s.SourceName,
SourceDetailName = s.SourceDetailName
}).ToListAsync();
var bills = await TenantDb.Queryable<SeaExportBillManage>().Where(x => request.Ids.Contains(x.BusinessId))
.Select(x => new SeaExportBillManage
{
Id = x.Id,
BusinessId = x.BusinessId,
HBLNO = x.HBLNO,
BillType = x.BillType,
Vessel = x.Vessel,
Voyno = x.Voyno,
ETD = x.ETD,
//x.BLConfirmationId,
BLIssueStatus = x.BLIssueStatus,
IssueType = x.IssueType
}).ToListAsync();
for (int i = 0; i < request.Ids.Length; i++)
{
request.BusinessId = request.Ids[i];
var order = orders.Find(s => s.Id == request.BusinessId);
if (order == null)
return DataResult.FailedWithDesc(nameof(MultiLanguageConst.BusinessNotFound));
request.BusinessId = request.Ids[i];
request.TaskTitle = $"【{request.TaskType.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD}|{order.Vessel}|{order.Voyno}";
request.TaskTitle = $"【{request.TaskType.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD?.ToString("yyyy-MM-dd")} {order.Vessel}|{order.Voyno}";
TaskManageOrderMessageInfo message = await CreateMessageAsync(request);
//根据配置获取默认接收人
message.Main.RecvUserInfoList = await GetRecvUsersAsync(request.BusinessId, request.BusinessType, request.TaskType);
@ -109,23 +149,12 @@ namespace DS.WMS.Core.TaskInteraction.Method
TaskType = request.TaskType,
TaskStatus = TaskStatusEnum.Create,
RecvUsers = string.Join(',', message.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty,
CreateBy = UserId,
CreateTime = dtNow
CreateBy = UserId
});
}
await TenantDb.Fastest<BusinessTask>().BulkCopyAsync(taskList);//保存主任务
await TenantDb.Insertable(taskList).ExecuteCommandAsync();//保存主任务
var bills = await TenantDb.Queryable<SeaExportBillManage>().Where(x => request.Ids.Contains(x.BusinessId))
.Select(x => new
{
x.Id,
x.HBLNO,
x.BillType,
x.Vessel,
x.Voyno,
x.ETD,
}).ToListAsync();
foreach (var task in taskList)
foreach (var task in taskList) //创建PA子任务
{
var order = orders.Find(s => s.Id == task.BusinessId);
var request2 = new TaskCreationRequest //委托单位PA
@ -133,8 +162,10 @@ namespace DS.WMS.Core.TaskInteraction.Method
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
ParentId = task.Id,
ParentBusinessId = task.BusinessId,
ParentTaskTypeName = task.TaskType.ToString(),
TaskTypeName = TaskBaseTypeEnum.CUSTOMER_PA.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.CUSTOMER_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD}|{order.Vessel}|{order.Voyno}"
TaskTitle = $"【{TaskBaseTypeEnum.CUSTOMER_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD?.ToString("yyyy-MM-dd")}|{order.Vessel}|{order.Voyno}"
};
TaskManageOrderMessageInfo message2 = await CreateMessageAsync(request2);
//根据配置获取默认接收人
@ -145,44 +176,50 @@ namespace DS.WMS.Core.TaskInteraction.Method
subTaskList.Add(new BusinessTask
{
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
TaskType = request.TaskType,
BusinessId = request2.BusinessId,
BusinessType = request2.BusinessType,
TaskType = request2.TaskType,
ParentBusinessId = request2.ParentBusinessId,
ParentId = task.Id,
TaskStatus = TaskStatusEnum.Create,
RecvUsers = string.Join(',', message2.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty,
CreateBy = UserId,
CreateTime = dtNow
CreateBy = UserId
});
if ((order.SourceName != "CIF-自揽货" && order.SourceDetailName != "WSL Member") || (order.SourceName != "CIF-自揽货" && order.SourceDetailName != "国外同行"))
//订单来源不等于WSL指定货且来源明细不等于WSL Member/国外同行则生成【国外代理PA】任务
if ((order.SourceName != "WSL指定货" && order.SourceDetailName != "WSL Member") || (order.SourceName != "CIF-自揽货" && order.SourceDetailName != "国外同行"))
{
var request3 = new TaskCreationRequest //国外代理PA
{
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
ParentId = task.Id,
ParentBusinessId = task.BusinessId,
ParentTaskTypeName = task.TaskType.ToString(),
TaskTypeName = TaskBaseTypeEnum.FOREIGN_AGENCY_PA.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.FOREIGN_AGENCY_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD}|{order.Vessel}|{order.Voyno}"
TaskTitle = $"【{TaskBaseTypeEnum.FOREIGN_AGENCY_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD?.ToString("yyyy-MM-dd")} {order.Vessel}|{order.Voyno}"
};
TaskManageOrderMessageInfo message3 = await CreateMessageAsync(request3);
//根据配置获取默认接收人
message2.Main.RecvUserInfoList = await GetRecvUsersAsync(request3.BusinessId, request3.BusinessType, request3.TaskType);
message3.Main.RecvUserInfoList = await GetRecvUsersAsync(request3.BusinessId, request3.BusinessType, request3.TaskType);
result = await ManagerService.InitTaskJob(message3);
if (!result.Succeeded)
return result;
subTaskList.Add(new BusinessTask
{
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
TaskType = request.TaskType,
BusinessId = request3.BusinessId,
BusinessType = request3.BusinessType,
TaskType = request3.TaskType,
ParentBusinessId = request3.ParentBusinessId,
ParentId = task.Id,
TaskStatus = TaskStatusEnum.Create,
RecvUsers = string.Join(',', message3.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty,
CreateBy = UserId,
CreateTime = dtNow
CreateBy = UserId
});
}
//订单签单方式=直单且委托单位不等于提单确认人则生成【提单确认方PA】任务
if (order.IssuingWay == "zd" && order.CustomerId != order.BLConfirmationId)
{
var request4 = new TaskCreationRequest //提单确认方PA
@ -190,44 +227,80 @@ namespace DS.WMS.Core.TaskInteraction.Method
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
ParentId = task.Id,
ParentBusinessId = task.BusinessId,
ParentTaskTypeName = task.TaskType.ToString(),
TaskTypeName = TaskBaseTypeEnum.BL_CONFIRMOR_PA.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.BL_CONFIRMOR_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD}|{order.Vessel}|{order.Voyno}"
TaskTitle = $"【{TaskBaseTypeEnum.BL_CONFIRMOR_PA.GetDescription()}】{order.CustomerNo} {order.MBLNO} {order.ETD?.ToString("yyyy-MM-dd")}|{order.Vessel}|{order.Voyno}"
};
TaskManageOrderMessageInfo message4 = await CreateMessageAsync(request4);
//根据配置获取默认接收人
message2.Main.RecvUserInfoList = await GetRecvUsersAsync(request4.BusinessId, request4.BusinessType, request4.TaskType);
message4.Main.RecvUserInfoList = await GetRecvUsersAsync(request4.BusinessId, request4.BusinessType, request4.TaskType);
result = await ManagerService.InitTaskJob(message4);
if (!result.Succeeded)
return result;
subTaskList.Add(new BusinessTask
{
BusinessId = task.BusinessId,
BusinessType = task.BusinessType,
TaskType = request.TaskType,
BusinessId = request4.BusinessId,
BusinessType = request4.BusinessType,
TaskType = request4.TaskType,
ParentBusinessId = request4.ParentBusinessId,
ParentId = task.Id,
TaskStatus = TaskStatusEnum.Create,
RecvUsers = string.Join(',', message4.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty,
CreateBy = UserId
});
}
var subBills = bills.FindAll(x => x.BusinessId == order.Id);
foreach (var bill in subBills)
{
var billRequest = new TaskCreationRequest //分单提单确认方PA
{
BusinessId = bill.Id,
BusinessType = task.BusinessType,
ParentId = task.Id,
ParentBusinessId = task.BusinessId,
ParentTaskTypeName = task.TaskType.ToString(),
TaskTypeName = TaskBaseTypeEnum.SUB_BL_CONFIRMOR_PA.ToString(),
TaskTitle = $"【{TaskBaseTypeEnum.SUB_BL_CONFIRMOR_PA.GetDescription()}】{bill.HBLNO} {bill.BillType} {bill.ETD?.ToString("yyyy-MM-dd")}|{bill.Vessel}|{bill.Voyno}"
};
TaskManageOrderMessageInfo billMessage = await CreateMessageAsync(billRequest);
//根据配置获取默认接收人
billMessage.Main.RecvUserInfoList = await GetRecvUsersAsync(billRequest.BusinessId, billRequest.BusinessType, billRequest.TaskType);
result = await ManagerService.InitTaskJob(billMessage);
if (!result.Succeeded)
return result;
subTaskList.Add(new BusinessTask
{
BusinessId = billRequest.BusinessId,
BusinessType = billRequest.BusinessType,
TaskType = billRequest.TaskType,
ParentBusinessId = billRequest.ParentBusinessId,
ParentId = task.Id,
TaskStatus = TaskStatusEnum.Create,
RecvUsers = string.Join(',', billMessage.Main.RecvUserInfoList.Select(x => x.RecvUserId)) ?? string.Empty,
CreateBy = UserId,
CreateTime = dtNow
});
}
}
await TenantDb.Fastest<BusinessTask>().BulkCopyAsync(subTaskList);//保存子任务
await TenantDb.Insertable(subTaskList).ExecuteCommandAsync();//保存子任务
var allTasks = taskList.Union(subTaskList).ToArray();
await logService.WriteLogAsync(string.Empty, allTasks);
await logService.WriteLogAsync(string.Empty, allTasks); //写入日志
var unionIds = orders.Select(x => x.Id).Union(bills.Select(x => x.Id)); //合并订单ID与分单ID
var opFiles = await TenantDb.Queryable<OpFile>().Where(x => unionIds.Contains(x.LinkId))
.Select(x => new
var opFiles = await TenantDb.Queryable<OpFile>().Where(x => unionIds.Contains(x.LinkId)) //拉取附件
.Select(x => new OpFile
{
x.LinkId,
x.TypeCode,
x.FileName,
x.FilePath
LinkId = x.LinkId,
TypeCode = x.TypeCode,
FileName = x.FileName,
FilePath = x.FilePath
}).ToListAsync();
List<MailDraft> mailDrafts = [];
foreach (var task in subTaskList)
foreach (var task in subTaskList) //添加子任务附件
{
var draftResult = await mailTemplateService.CreateDraftAsync(task);
if (!draftResult.Succeeded)
@ -236,29 +309,51 @@ namespace DS.WMS.Core.TaskInteraction.Method
continue;
}
draftResult.Data.TaskId = task.Id;
var order = orders.Find(x => x.Id == task.BusinessId);
string typeCode = string.Empty;
switch (order.IssueType)
var billList = bills.FindAll(x => x.Id == task.BusinessId);
draftResult.Data.TaskId = task.Id;
if (task.TaskType == TaskBaseTypeEnum.SUB_BL_CONFIRMOR_PA) //分单子任务
{
case "正本":
typeCode = "OringinalBill_Mbl"; //正本提单扫描件
break;
case "SWB":
typeCode = "swb";
break;
case "EBL":
typeCode = "OringinalBill_Mbl";
break;
result = AddHBLAttachments(opFiles, billList, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
}
var opFile = opFiles.Find(x => x.LinkId == order.Id && x.TypeCode == typeCode);
opFile ??= opFiles.Find(x => x.LinkId == order.Id && x.TypeCode == "format_sheet");
if (opFile == null) //获取不到订单附件则记录错误日志
else //主单子任务
{
//订单类型为分单时,只需要有分单格式件和账单;为直单且来源明细为“国外同行、国外直客”时,需要有主单格式件和账单,其余情况生成账单
if (task.TaskType == TaskBaseTypeEnum.CUSTOMER_PA)
{
if (order.IssuingWay == "fd")
{
result = AddHBLAttachments(opFiles, billList, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
}
else if (order.IssuingWay == "zd" && (order.SourceDetailName == "国外同行" || order.SourceDetailName == "国外直客"))
{
result = AddMBLAttachment(opFiles, order, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
}
}
//主单格式件+分单格式件(有分单时)+账单
else if (task.TaskType == TaskBaseTypeEnum.FOREIGN_AGENCY_PA)
{
result = AddMBLAttachment(opFiles, order, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
result = AddHBLAttachments(opFiles, billList, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
}
//主单格式单和账单
else if (task.TaskType == TaskBaseTypeEnum.BL_CONFIRMOR_PA)
{
result = AddMBLAttachment(opFiles, order, draftResult.Data);
if (!result.Succeeded)
await logService.WriteLogAsync(task, result.Message);
}
}
mailDrafts.Add(draftResult.Data);
@ -282,5 +377,70 @@ namespace DS.WMS.Core.TaskInteraction.Method
}
}
//添加MBL格式件
static DataResult AddMBLAttachment(List<OpFile> opFiles, SeaExport order, MailDraft draft)
{
string? typeCode = null;
switch (order.IssueType)
{
case "正本":
typeCode = "OringinalBill_Mbl"; //正本提单扫描件
break;
case "SWB":
typeCode = "swb";
break;
case "EBL":
typeCode = "OringinalBill_Mbl";
break;
}
var opFile = opFiles.Find(x => x.LinkId == order.Id && x.TypeCode == typeCode);
if (opFile == null)
{
typeCode = "format_sheet";
opFile = opFiles.Find(x => x.LinkId == order.Id && x.TypeCode == typeCode);
}
if (opFile == null) //获取不到订单附件则记录错误日志
return DataResult.Failed($"未能从订单 {order.CustomerNo} 中获取类型为 {typeCode} 的附件");
MailDraftAttachment attachment = new()
{
DraftId = draft.Id,
AttachName = opFile.FileName,
UrlPath = opFile.FilePath
};
draft.Attaches.Add(attachment);
return DataResult.Success;
}
//添加HBL格式件
static DataResult AddHBLAttachments(List<OpFile> opFiles, List<SeaExportBillManage> bills, MailDraft draft)
{
string? typeCode = null;
foreach (var bill in bills)
{
if (bill.BLIssueStatus == (int)BLIssueStatusEnum.CheckOut) //已签发
{
if (bill.IssueType != "正本" && bill.IssueType != "电放" && bill.IssueType != "SWB" && bill.IssueType != "EBL")
typeCode = "OriginalHBL)";
}
var opFile = opFiles.Find(x => x.LinkId == bill.Id && x.TypeCode == typeCode);
if (opFile != null)
{
MailDraftAttachment attachment = new()
{
DraftId = draft.Id,
AttachName = opFile.FileName,
UrlPath = opFile.FilePath
};
draft.Attaches.Add(attachment);
}
}
return DataResult.Success;
}
}
}

@ -129,7 +129,14 @@ namespace DS.WMS.Core.TaskInteraction.Method
{
ArgumentNullException.ThrowIfNull(logs, nameof(logs));
if (logs.Length > 100)
{
await TenantDb.Fastest<BusinessTaskLog>().BulkCopyAsync([.. logs]);
}
else
{
await TenantDb.Insertable(logs).ExecuteCommandAsync();
}
}
}
}

@ -288,5 +288,20 @@ namespace DS.WMS.OpApi.Controllers
await taskService.RunJobAsync();
return StatusCode((int)HttpStatusCode.NoContent);
}
/// <summary>
/// 创建PA任务
/// </summary>
/// <param name="paTaskService"></param>
/// <param name="businessType">业务类型</param>
/// <param name="etd">出港日期</param>
/// <returns></returns>
[HttpPost, Route("CreatePATask")]
public async Task<DataResult> CreatePATaskAsync([FromServices] IPreAlertTaskService paTaskService,
BusinessType businessType = BusinessType.OceanShippingExport, DateTime? etd = null)
{
etd ??= DateTime.Now.Date;
return await paTaskService.CreateTaskAsync(businessType, etd.Value);
}
}
}

@ -1,5 +1,6 @@
using DS.Module.Core;
using DS.Module.Core.Data;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.TaskInteraction.Entity;
using DS.WMS.Core.TaskInteraction.Interface;
using Microsoft.AspNetCore.Mvc;
@ -22,35 +23,18 @@ namespace DS.WMS.OpApi.Controllers
this.service = service;
}
///// <summary>
///// 预览邮件模板
///// </summary>
///// <param name="host"></param>
///// <param name="customerNO">委托编号</param>
///// <param name="docType">单据类型</param>
///// <param name="templateName">模板自定义名称</param>
///// <returns></returns>
//[HttpGet, Route("Preview")]
//public async Task<IActionResult> PreviewAsync([FromServices] IHostEnvironment host, string customerNO, DocumentType docType, string? templateName = null)
//{
// var result = await service.GetMailContentAsync(customerNO, docType, templateName);
// if (!result.Succeeded)
// return Content(result.Message);
// //string dir = Path.Combine(host.ContentRootPath, "wwwroot", "content-preview");
// //if (!Directory.Exists(dir))
// // Directory.CreateDirectory(dir);
// //string path = Path.Combine(dir, customerNO + ".htm");
// //FileStream fs = new FileStream(path, FileMode.Create);
// //StreamWriter writer = new StreamWriter(fs, Encoding.UTF8);
// //await writer.WriteAsync(result.Data.Item2);
// //await fs.FlushAsync();
// //return PhysicalFile(path, "application/octet-stream");
// return File(Encoding.UTF8.GetBytes(result.Data.Item2), "application/octet-stream", customerNO + ".htm");
//}
/// <summary>
/// 预览邮件模板
/// </summary>
/// <param name="id">邮件模板ID</param>
/// <param name="businessType">业务类型</param>
/// <param name="number">委托编号/提单号</param>
/// <returns></returns>
[HttpGet, Route("Preview")]
public async Task<DataResult<MailDraft>> PreviewAsync([FromQuery] long id, [FromQuery] BusinessType businessType, [FromQuery] string number)
{
return await service.PreviewAsync(id, businessType, number);
}
/// <summary>
/// 获取数据提供程序
@ -63,6 +47,18 @@ namespace DS.WMS.OpApi.Controllers
return await service.GetProvidersAsync(queryKey);
}
/// <summary>
/// 获取数据提供程序的数据输出
/// </summary>
/// <param name="providerId">提供程序ID</param>
/// <param name="businessId">业务ID不指定此参数则随机获取</param>
/// <returns></returns>
[HttpGet, Route("GetProviderData")]
public async Task<DataResult<dynamic?>> GetProviderData([FromQuery] long providerId, [FromQuery] long? businessId = null)
{
return await service.GetProviderDataAsync(providerId, businessId);
}
/// <summary>
/// 获取分页列表
/// </summary>

@ -107,14 +107,17 @@
"ResultUrl": "http://47.104.73.97:7115/api/TaskShippingOrderCompare/CompareResult"
},
"TaskMail": {
"FileBaseUrl": "http://localhost:5030",
"PrintBaseUrl": "http://118.190.144.189:3008",
"JsonPrint": "/printApi/OpenPrint/GetOpenJsonPrintInfoAsync",
"JsonPrintByCode": "/printApi/OpenPrint/GetOpenJsonPrintInfoByTemplateCode",
"SQLPrint": "/printApi/OpenPrint/GetOpenSqlPrintInfo",
"OpBaseUrl": "http://localhost:5030",
"RunJob": "/opApi/SeaExportTask/RunJob",
"PATask": "/opApi/SeaExportTask/CreatePATask",
"LinkAttach": "http://118.190.144.189:3008/LinkAttach",
"MailApiUrl": "http://47.104.73.97:8801/mail/send",
"DefaultSetting": {
"ETDDays": 3,
"Tenant": 1750335377144680448,
"Account": "lyle@yyts.fun",
"Password": "15275215387jZ",
@ -146,8 +149,8 @@
"FileType": [ ".xls", ".xlsx" ]
},
"JobConfig": {
"WSLReportJob": "0 0 17 * * ?",
//"WSLWeeklyReportJob": "0 30 16 * * ?" ,
//"WSLReportJob": "0 0 17 * * ?",
"PATaskJob": "0 30 5 * * ?",
"WSLWeeklyReportJob": "0 */1 * * * ?"
}
}

Loading…
Cancel
Save