You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
BookingHeChuan/Myshipping.Application/Service/TaskManagePlat/TaskManageDRAFTService.cs

674 lines
26 KiB
C#

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

using Furion;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.JsonSerialization;
using Furion.RemoteRequest.Extensions;
using HtmlAgilityPack;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Myshipping.Application.ConfigOption;
using Myshipping.Application.Entity;
using Myshipping.Core;
using Myshipping.Core.Entity;
using Myshipping.Core.Service;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Net.Http;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Web;
namespace Myshipping.Application
{
/// <summary>
/// DRAFT任务
/// </summary>
[ApiDescriptionSettings("Application", Name = "TaskManageDRAFT", Order = 10)]
public class TaskManageDRAFTService: ITaskManageDRAFTService, IDynamicApiController
{
private readonly ISysCacheService _cache;
private readonly ILogger<TaskManageDRAFTService> _logger;
private readonly SqlSugarRepository<TaskBaseInfo> _taskBaseRepository;
private readonly SqlSugarRepository<TaskFileInfo> _taskFileRepository;
private readonly SqlSugarRepository<TaskDraftInfo> _taskDraftInfoRepository;
private readonly SqlSugarRepository<DjyUserMailAccount> _djyUserMailAccount;
private readonly SqlSugarRepository<BookingOrderContact> _bookingOrderContactRepository;
private readonly SqlSugarRepository<BookingOrder> _bookingOrderRepository;
private readonly SqlSugarRepository<SysUser> _sysUserRepository;
private readonly IDjyCustomerService _djyCustomerService;
public TaskManageDRAFTService(SqlSugarRepository<TaskBaseInfo> taskBaseRepository,
SqlSugarRepository<TaskFileInfo> taskFileRepository,
SqlSugarRepository<TaskDraftInfo> taskDraftInfoRepository,
SqlSugarRepository<DjyUserMailAccount> djyUserMailAccount,
SqlSugarRepository<BookingOrderContact> bookingOrderContactRepository,
SqlSugarRepository<BookingOrder> bookingOrderRepository,
SqlSugarRepository<SysUser> sysUserRepository,
IDjyCustomerService djyCustomerService,
ISysCacheService cache,
ILogger<TaskManageDRAFTService> logger)
{
_taskBaseRepository = taskBaseRepository;
_taskFileRepository = taskFileRepository;
_taskDraftInfoRepository = taskDraftInfoRepository;
_bookingOrderRepository = bookingOrderRepository;
_bookingOrderContactRepository = bookingOrderContactRepository;
_djyUserMailAccount = djyUserMailAccount;
_djyCustomerService = djyCustomerService;
_sysUserRepository = sysUserRepository;
_logger = logger;
_cache = cache;
}
#region 获取DRAFT详情
/// <summary>
/// 获取DRAFT详情
/// </summary>
/// <param name="pkId">DRAFT主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageDRAFT/GetInfo")]
public async Task<TaskDraftShowDto> GetInfo(string pkId)
{
TaskDraftShowDto dto = new TaskDraftShowDto();
var draft = _taskDraftInfoRepository.AsQueryable().First(a => a.PK_ID == pkId);
if (draft == null)
throw Oops.Oh($"DRAFT主键{pkId}无法获取业务信息");
var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == draft.TASK_ID);
if (taskBase == null)
throw Oops.Oh($"任务主键无法获取业务信息");
dto = new TaskDraftShowDto
{
PKId = draft.PK_ID,
TaskId = draft.TASK_ID,
Carrier = draft.CARRIER,
BookingId = draft.BOOKING_ID,
MBlNo = draft.MBL_NO,
NoticeDate = draft.NOTICE_DATE,
};
if (dto != null)
{
dto.IsComplete = taskBase.IS_COMPLETE == 1 ? true : false;
dto.CompleteTime = taskBase.COMPLETE_DATE;
}
return dto;
}
#endregion
#region 通过任务主键获取DRAFT详情
/// <summary>
/// 通过任务主键获取DRAFT详情
/// </summary>
/// <param name="taskPkId">DRAFT任务主键</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageDRAFT/GetInfoByTaskId")]
public async Task<TaskDraftShowDto> GetInfoByTaskId(string taskPkId)
{
TaskDraftShowDto dto = new TaskDraftShowDto();
var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId);
if (taskBase == null)
throw Oops.Oh($"任务主键{taskPkId}无法获取业务信息");
var draft = _taskDraftInfoRepository.AsQueryable().First(a => a.TASK_ID == taskBase.PK_ID);
if (draft == null)
throw Oops.Oh($"DRAFT主键{taskPkId}无法获取业务信息");
dto = new TaskDraftShowDto
{
PKId = draft.PK_ID,
TaskId = draft.TASK_ID,
Carrier = draft.CARRIER,
BookingId = draft.BOOKING_ID,
MBlNo = draft.MBL_NO,
NoticeDate = draft.NOTICE_DATE,
};
if (dto != null)
{
dto.IsComplete = taskBase.IS_COMPLETE == 1 ? true : false;
dto.CompleteTime = taskBase.COMPLETE_DATE;
}
return dto;
}
#endregion
#region 任务ID下载附件
/// <summary>
/// 任务ID下载附件
/// </summary>
/// <param name="taskPKId">DRAFT任务主键</param>
/// <param name="fileCategory">附件分类代码</param>
/// <returns>返回数据流</returns>
[HttpGet("/TaskManageDRAFT/DownloadFile")]
public async Task<IActionResult> DownloadFile([FromQuery] string taskPKId, [FromQuery] string fileCategory = "DRAFT")
{
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
TaskFileCategoryEnum fileCategoryEnum = TaskFileCategoryEnum.NONE;
System.Enum.TryParse(fileCategory, out fileCategoryEnum);
if (fileCategoryEnum == TaskFileCategoryEnum.NONE)
{
throw Oops.Oh($"附件分类代码错误,请提供正确的分类代码");
}
string name = fileCategoryEnum.ToString();
var fileInfo = await _taskFileRepository.AsQueryable().FirstAsync(u => u.TASK_PKID == taskPKId && u.FILE_CATEGORY == name);
if (fileInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}没有可下载的附件");
}
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
var fileFullPath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
if (!File.Exists(fileFullPath))
{
throw Oops.Oh($"任务主键{taskPKId} 附件下载请求失败,请确认文件是否存在");
}
_logger.LogInformation($"taskPKId={taskPKId} 下载文件完整路径 fileFullPath={fileFullPath}");
var fileName = HttpUtility.UrlEncode(fileInfo.FILE_NAME, Encoding.GetEncoding("UTF-8"));
var result = new FileStreamResult(new FileStream(fileFullPath, FileMode.Open), "application/octet-stream") { FileDownloadName = fileName };
return result;
}
#endregion
#region 发送邮件
/// <summary>
/// 发送邮件
/// </summary>
/// <param name="taskPKId">DRAFT任务主键</param>
/// <param name="usePersonalEmailSend">是否使用个人邮箱发送</param>
/// <returns>返回回执</returns>
[HttpGet("/TaskManageDRAFT/SendEmail")]
public async Task<TaskManageOrderResultDto> SendEmail(string taskPKId, bool usePersonalEmailSend = false)
{
if (string.IsNullOrWhiteSpace(taskPKId))
throw Oops.Oh($"DRAFT任务主键不能为空");
var bcTaskInfo = await _taskBaseRepository.AsQueryable().FirstAsync(u => u.PK_ID == taskPKId);
if (bcTaskInfo == null)
{
throw Oops.Oh($"任务主键{taskPKId}无法获取业务信息");
}
var draft = _taskDraftInfoRepository.AsQueryable().First(a => a.TASK_ID == bcTaskInfo.PK_ID);
if (draft == null)
throw Oops.Oh($"任务主键{taskPKId}无法获取DRAFT业务信息");
return await GenerateSendEmail(draft, usePersonalEmailSend);
}
#endregion
#region 生成并推送邮件
/// <summary>
/// 生成并推送邮件
/// </summary>
/// <param name="taskDraftInfo">DRAFT任务详情</param>
/// <returns>返回回执</returns>
private async Task<TaskManageOrderResultDto> GenerateSendEmail(TaskDraftInfo taskDraftInfo, bool usePersonalEmailSend = false)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
try
{
/*
1、提取邮件接收人、通过订舱的委托客户获取联系人信息提取联系人中备注是BCNotice的邮箱
2、提取当票订舱对应的操作人邮箱、通过订舱的委托客户获取操作OP的邮箱
3、读取用户邮箱配置主要提取显示名称BCNotice的邮箱用来作为公共邮箱来发送邮件。
4、读取邮件模板填充详情。
5、推送邮件给邮件接收人
*/
//读取订舱数据
var bookingOrderEntity = _bookingOrderRepository.AsQueryable()
.First(a => a.Id == taskDraftInfo.BOOKING_ID);
if (bookingOrderEntity == null)
{
var checkInfo = _bookingOrderRepository.AsQueryable().Filter(null, true)
.First(a => a.MBLNO == taskDraftInfo.MBL_NO && a.IsDeleted == false && (a.ParentId == null || a.ParentId == 0));
if (checkInfo != null)
{
throw Oops.Oh($"订舱详情获取失败用提单号能检索到但是没有ID关系需要人工看看问题");
}
else
{
throw Oops.Oh($"订舱详情获取失败,请确认订舱是否存在或已删除");
}
}
_logger.LogInformation($"获取订舱详情完成bookid={bookingOrderEntity.Id}");
if (!bookingOrderEntity.CUSTOMERID.HasValue || (bookingOrderEntity.CUSTOMERID.HasValue && bookingOrderEntity.CUSTOMERID.Value == 0))
{
throw Oops.Oh($"订舱的委托客户不能为空");
}
var djyCustomerInfo = _djyCustomerService.Detail(new GetDjyCustomerInput { Id = bookingOrderEntity.CUSTOMERID.Value })
.GetAwaiter().GetResult();
if (djyCustomerInfo == null)
{
throw Oops.Oh($"委托单位详情获取失败,请确认委托单位是否存在或已删除");
}
_logger.LogInformation($"获取委托单位详情完成djyCustomerId={djyCustomerInfo.Id}");
//DjyCustomerContactOutput djyCustomerContactMan = null;
//TO 邮件接收人
string toEmail = string.Empty;
//订舱OP的邮箱
string opEmail = string.Empty;
var bookingContactList = _bookingOrderContactRepository.AsQueryable()
.Where(a => a.BookingId == taskDraftInfo.BOOKING_ID).ToList();
if (bookingContactList == null || bookingContactList.Count == 0)
{
_logger.LogInformation($"当前订舱未指定的联系人toEmail={toEmail}");
}
toEmail = string.Join(";", bookingContactList.Select(x => x.Email.Trim()).Distinct().ToArray());
//获取操作OP的邮箱
if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID))
{
var opId = long.Parse(bookingOrderEntity.OPID);
var opUser = _sysUserRepository.AsQueryable().First(a => a.Id == opId);
if (opUser != null && !string.IsNullOrWhiteSpace(opUser.Email))
{
opEmail = opUser.Email.Trim();
_logger.LogInformation($"获取操作OP的邮箱opEmail={opEmail} id={opId} name={opUser.Name}");
}
}
//提取当前公共邮箱的配置
var publicMailAccount = _djyUserMailAccount.FirstOrDefault(x => x.CreatedUserId == UserManager.UserId
&& x.SmtpPort > 0 && x.SmtpServer != null && x.SmtpServer != "");
if (publicMailAccount == null)
{
throw Oops.Oh($"提取公共邮箱配置失败请在用户邮箱账号管理增加配置显示名为BCNotice");
}
_logger.LogInformation($"提取当前公共邮箱的配置完成id={publicMailAccount.Id}");
string emailTitle = $"Draft : {taskDraftInfo.MBL_NO}";
string filePath = string.Empty;
SysUser opUserInfo = null;
if (!string.IsNullOrWhiteSpace(bookingOrderEntity.OPID) && Regex.IsMatch(bookingOrderEntity.OPID, "[0-9]+"))
opUserInfo = _sysUserRepository.AsQueryable().First(u => u.Id == long.Parse(bookingOrderEntity.OPID));
//读取邮件模板并填充数据
string emailHtml = GenerateSendEmailHtml(taskDraftInfo, opUserInfo, UserManager.TENANT_NAME).GetAwaiter().GetResult();
_logger.LogInformation($"生成邮件BODY结果{emailHtml}");
var fileInfo = _taskFileRepository.AsQueryable().Where(a => a.TASK_PKID == taskDraftInfo.TASK_ID && a.FILE_CATEGORY.Contains("draft_notice"))
.OrderByDescending(a => a.CreatedTime).First();
if (fileInfo == null)
{
throw Oops.Oh($"提取DRAFT的文件失败不能发送邮件");
}
_logger.LogInformation($"获取订舱附件地址,结果:{fileInfo.FILE_PATH}");
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
filePath = Path.Combine(dirAbs, fileInfo.FILE_PATH);
EmailApiUserDefinedDto emailApiUserDefinedDto = new EmailApiUserDefinedDto
{
SendTo = toEmail,
//CCTo = opEmail,
Title = emailTitle,
Body = emailHtml,
Account = publicMailAccount.MailAccount?.Trim(),
Password = publicMailAccount.Password?.Trim(),
Server = publicMailAccount.SmtpServer?.Trim(),
Port = publicMailAccount.SmtpPort.HasValue ? publicMailAccount.SmtpPort.Value : 465,
UseSSL = publicMailAccount.SmtpSSL.HasValue ? publicMailAccount.SmtpSSL.Value : true,
Attaches = new List<AttachesInfo>()
};
_logger.LogInformation($"生成请求邮件参数,结果:{JSON.Serialize(emailApiUserDefinedDto)}");
//推送邮件
var emailRlt = await PushEmail(emailApiUserDefinedDto, filePath);
_logger.LogInformation($"推送邮件完成,结果:{JSON.Serialize(emailRlt)}");
result.succ = true;
result.msg = "成功";
}
catch (Exception ex)
{
_logger.LogInformation($"推送邮件失败,异常:{ex.Message}");
result.succ = false;
result.msg = $"推送邮件失败,{ex.Message}";
}
return result;
}
#endregion
#region 通过邮件模板生成HTML
/// <summary>
/// 通过邮件模板生成HTML
/// </summary>
/// <param name="taskBCInfo">BC任务详情</param>
/// <param name="opUserInfo">订舱OP详情</param>
/// <param name="tenantName">当前租户全称</param>
/// <returns>返回生成的HTML</returns>
private async Task<string> GenerateSendEmailHtml(TaskDraftInfo taskDraftInfo, SysUser opUserInfo, string tenantName)
{
string result = string.Empty;
/*
1、加载模板文件读取HTML
2、读取main、conta的tr行替换业务数据
3、返回HTML的文本信息。
*/
try
{
string templatePath = App.Configuration["EmailTemplateFilePath"];
var opt = App.GetOptions<BookingAttachOptions>();
var dirAbs = opt.basePath;
if (string.IsNullOrEmpty(dirAbs))
{
dirAbs = App.WebHostEnvironment.WebRootPath;
}
templatePath = $"{dirAbs}{templatePath}\\DraftEmailTemplate.html";
string baseHtml = File.ReadAllText(templatePath);
if (string.IsNullOrWhiteSpace(baseHtml))
throw Oops.Oh($"读取邮件模板失败");
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Name))
{
baseHtml = baseHtml.Replace("#opname#", opUserInfo.Name);
}
else
{
baseHtml = baseHtml.Replace("#opname#", "操作");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Email))
{
baseHtml = baseHtml.Replace("#opemail#", opUserInfo.Email);
}
else
{
baseHtml = baseHtml.Replace("#opemail#", "");
}
if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Phone))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Phone);
}
else if (opUserInfo != null && !string.IsNullOrWhiteSpace(opUserInfo.Tel))
{
baseHtml = baseHtml.Replace("#optel#", opUserInfo.Tel);
}
else
{
baseHtml = baseHtml.Replace("#optel#", "");
}
HtmlDocument html = new HtmlDocument();
html.LoadHtml(baseHtml);
HtmlNode baseTable = html.DocumentNode.SelectNodes("//table[@class='base-table']").FirstOrDefault();
if (baseTable == null)
throw Oops.Oh($"读取邮件模板格式错误定位base-table失败");
var baseTrList = baseTable.SelectNodes(".//tr");
foreach (var baseTr in baseTrList)
{
var tdList = baseTr.SelectNodes(".//td");
foreach (var baseTd in tdList)
{
if (baseTd.Attributes["class"].Value == "billno-val")
{
baseTd.InnerHtml = taskDraftInfo.MBL_NO;
}
else if (baseTd.Attributes["class"].Value == "carrier-val")
{
baseTd.InnerHtml = taskDraftInfo.CARRIER;
}
}
}
var noreplyTr = html.DocumentNode.SelectNodes("//tr[@class='email-noreply']").FirstOrDefault();
if (noreplyTr != null)
{
var currTd = noreplyTr.SelectNodes(".//td").FirstOrDefault();
if (currTd != null)
{
var currPList = currTd.SelectNodes(".//p");
foreach (var baseP in currPList)
{
if (baseP.Attributes["class"].Value == "notice-comp-val")
{
baseP.InnerHtml = tenantName;
}
}
}
}
result = html.DocumentNode.OuterHtml;
}
catch (Exception ex)
{
_logger.LogInformation($"通过邮件模板生成HTML异常原因={ex.Message}");
throw ex;
}
return result;
}
#endregion
#region 推送邮件
/// <summary>
/// 推送邮件
/// </summary>
/// <param name="emailApiUserDefinedDto">自定义邮件详情</param>
/// <param name="filePath">文件路径</param>
/// <returns>返回回执</returns>
private async Task<CommonWebApiResult> PushEmail(EmailApiUserDefinedDto emailApiUserDefinedDto, string filePath)
{
CommonWebApiResult result = new CommonWebApiResult { succ = true };
List<EmailApiUserDefinedDto> emailList = new List<EmailApiUserDefinedDto>();
var emailUrl = _cache.GetAllDictData().GetAwaiter().GetResult()
.FirstOrDefault(x => x.TypeCode == "url_set" && x.Code == "email_api_url")?.Value;
if (emailUrl == null)
throw Oops.Bah("字典未配置 url_set->email_api_url 请联系管理员");
System.IO.FileStream file = new System.IO.FileStream(filePath, FileMode.Open, FileAccess.Read);
int SplitSize = 5242880;//5M分片长度
int index = 1; //序号 第几片
long StartPosition = 5242880 * (index - 1);
long lastLens = file.Length - StartPosition;//真不知道怎么起命了,就这样吧
if (lastLens < 5242880)
{
SplitSize = (int)lastLens;
}
byte[] heByte = new byte[SplitSize];
file.Seek(StartPosition, SeekOrigin.Begin);
//第一个参数是 起始位置
file.Read(heByte, 0, SplitSize);
//第三个参数是 读取长度(剩余长度)
file.Close();
string base64Str = Convert.ToBase64String(heByte);
emailApiUserDefinedDto.Attaches.Add(new AttachesInfo
{
AttachName = Path.GetFileName(filePath),
AttachContent = base64Str
});
emailList.Add(emailApiUserDefinedDto);
string strJoin = System.IO.File.ReadAllText(filePath);
DateTime bDate = DateTime.Now;
HttpResponseMessage res = null;
try
{
res = await emailUrl.SetBody(emailList, "application/json").PostAsync();
}
catch (Exception ex)
{
_logger.LogInformation($"发送邮件异常:{ex.Message}");
}
DateTime eDate = DateTime.Now;
TimeSpan ts = eDate.Subtract(bDate);
var timeDiff = ts.TotalMilliseconds;
_logger.LogInformation($"邮件上传完成 上传文件大小:{heByte.Length} 用时:{timeDiff}ms.,{strJoin}");
_logger.LogInformation($"发送邮件返回:{JSON.Serialize(res)}");
if (res != null && res.StatusCode == System.Net.HttpStatusCode.OK)
{
var userResult = await res.Content.ReadAsStringAsync();
var respObj = JsonConvert.DeserializeAnonymousType(userResult, new
{
Success = false,
Message = string.Empty,
Code = -9999,
});
result.succ = respObj.Success;
result.msg = respObj.Message;
}
return result;
}
#endregion
#region 重新处理DRAFT任务
/// <summary>
/// 重新处理DRAFT任务
/// 对未匹配订舱订单的任务记录重新对应订舱订单
/// </summary>
/// <param name="taskPkId">DRAFT任务主键</param>
/// <returns></returns>
[HttpGet("/TaskManageDRAFT/SearchAndConnectBookingInfo")]
public async Task<TaskManageOrderResultDto> SearchAndConnectBookingInfo(string taskPkId)
{
TaskManageOrderResultDto result = new TaskManageOrderResultDto();
var taskBase = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskPkId);
if (taskBase == null)
throw Oops.Oh($"任务主键{taskPkId}无法获取业务信息");
var draft = _taskDraftInfoRepository.AsQueryable().First(a => a.TASK_ID == taskBase.PK_ID);
if (draft == null)
throw Oops.Oh($"DRAFT主键{taskPkId}无法获取业务信息");
if (draft.BOOKING_ID.HasValue)
throw Oops.Oh($"当前DRAFT已有匹配的订舱订单");
string mblNo = draft.MBL_NO.ToUpper().Trim();
var bookingInfo = _bookingOrderRepository.AsQueryable().Filter(null, true)
.First(a => a.MBLNO == mblNo && a.IsDeleted == false && (a.ParentId == null || a.ParentId == 0));
if (bookingInfo == null)
throw Oops.Oh($"提单号{mblNo}未提取有效的订舱订单");
draft.BOOKING_ID = bookingInfo.Id;
draft.UpdatedUserId = UserManager.UserId;
draft.UpdatedUserName = UserManager.Name;
//更新任务
await _taskDraftInfoRepository.AsUpdateable(draft).UpdateColumns(it => new
{
it.BOOKING_ID,
it.UpdatedTime,
it.UpdatedUserId,
it.UpdatedUserName
}).ExecuteCommandAsync();
result.succ = true;
result.msg = "成功";
return result;
}
#endregion
}
}