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.

484 lines
21 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 DS.Module.Core;
using DS.Module.Core.Data;
using DS.Module.SqlSugar;
using DS.Module.UserModule;
using DS.WMS.Core.Op.Entity;
using DS.WMS.Core.TaskPlat.Dtos;
using DS.WMS.Core.TaskPlat.Entity;
using DS.WMS.Core.TaskPlat.Interface;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using SqlSugar;
using System.Linq.Expressions;
using System.Runtime.InteropServices;
using System.Text;
using System.Web;
namespace DS.WMS.Core.TaskPlat.Method
{
/// <summary>
/// 任务模块业务类的基类,封装了一些常用的方法
/// </summary>
public class TaskManageBaseService<T> : ITaskManageBaseService
{
// 实例化时构建
protected readonly IUser user;
protected readonly ILogger<T> logger;
protected readonly ISaasDbService saasDbService;
protected readonly IServiceProvider serviceProvider;
protected readonly IWebHostEnvironment environment;
// 按需构建
private Lazy<ITaskAllocationService> allocationService;
public TaskManageBaseService(IUser user,
ILogger<T> logger,
ISaasDbService saasDbService,
IServiceProvider serviceProvider,
IWebHostEnvironment environment)
{
this.user = user;
this.logger = logger;
this.saasDbService = saasDbService;
this.serviceProvider = serviceProvider;
this.environment = environment;
allocationService = new Lazy<ITaskAllocationService>(serviceProvider.GetRequiredService<ITaskAllocationService>());
}
/// <summary>
/// 更新任务主表状态
/// </summary>
/// <param name="taskIds">任务主键数组</param>
/// <param name="columns">需要更新状态的列</param>
public async Task SetTaskStatus(long[] taskIds, params Expression<Func<TaskBaseInfo, bool>>[] columns)
{
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
//任务不考虑OrgId,这里去掉
tenantDb.QueryFilter.Clear<IOrgId>();
var updateable = tenantDb.Updateable<TaskBaseInfo>();
foreach (var item in columns)
{
updateable.SetColumns(item);
}
updateable.SetColumns(x => x.UpdateBy == long.Parse(user.UserId))
.SetColumns(x => x.UpdateTime == DateTime.Now)
.SetColumns(x => x.UpdateUserName == user.UserName);
await updateable.Where(x => taskIds.Contains(x.Id))
.ExecuteCommandAsync();
// 后面可以记录日志
}
/// <summary>
/// 设置任务处理人
/// </summary>
/// <param name="taskIds">任务主键数组</param>
/// <param name="userInfo">人员信息列表</param>
public async Task SetTaskOwner(long[] taskIds, List<RecvUserInfo> userInfo)
{
SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
//任务不考虑OrgId,这里去掉
tenantDb.QueryFilter.Clear<IOrgId>();
try
{
var taskList = await tenantDb.Queryable<TaskBaseInfo>().Where(x => taskIds.Contains(x.Id)).ToListAsync(x => new
{
x.Id,
x.STATUS,
x.STATUS_NAME
});
var taskIdList = taskList.Select(x => x.Id).ToList();
List<TaskBaseAllocation> allocationList = new();
foreach (var item in taskList)
{
allocationList.AddRange(userInfo.Select(x => new TaskBaseAllocation
{
TaskId = item.Id,
UserId = x.RecvUserId,
UserName = x.RecvUserName,
Status = item.STATUS,
StatusName = item.STATUS_NAME,
StatusTime = DateTime.Now
}));
}
await tenantDb.Ado.BeginTranAsync();
await tenantDb.Deleteable<TaskBaseAllocation>(x => taskIdList.Contains(x.TaskId)).ExecuteCommandAsync();
await tenantDb.Insertable(allocationList).ExecuteCommandAsync();
await tenantDb.Updateable<TaskBaseInfo>()
.SetColumns(x => x.IS_PUBLIC == 0)
.Where(x => taskIdList.Contains(x.Id))
.ExecuteCommandAsync();
await tenantDb.Ado.CommitTranAsync();
}
catch (Exception)
{
await tenantDb.Ado.RollbackTranAsync();
throw;
}
}
#region Tools
/// <summary>
/// 保存文件并返回文件完整路径
/// </summary>
/// <param name="fileDictKey">追加文件夹</param>
/// <param name="fileBytes">文件二进制流</param>
/// <param name="batchNo">批次号</param>
/// <param name="fileNameNoSuffix">无拓展名的文件名</param>
/// <param name="printFileType">文件类型</param>
/// <param name="attachFileType">附件类型 bcfiles-BC文件 sofile-订舱附件</param>
/// <returns>返回文件完整路径</returns>
protected async Task<string> SaveFile(string fileDictKey, byte[] fileBytes, string batchNo, string fileNameNoSuffix,
PrintFileTypeEnum printFileType, string attachFileType = "sofiles")
{
var basePath = AppSetting.app(new string[] { "FileSettings", "BasePath" });
var relativePath = AppSetting.app(new string[] { "FileSettings", "RelativePath" });
if (!string.IsNullOrWhiteSpace(attachFileType))
relativePath += $"\\{attachFileType}";
if (!string.IsNullOrWhiteSpace(fileDictKey))
relativePath += $"\\{fileDictKey}";
string? dirAbs;
if (string.IsNullOrEmpty(basePath))
{
dirAbs = Path.Combine(environment.WebRootPath ?? "", relativePath);
}
else
{
dirAbs = Path.Combine(basePath, relativePath);
}
if (!Directory.Exists(dirAbs))
Directory.CreateDirectory(dirAbs);
var fileType = string.Empty;
if (printFileType == PrintFileTypeEnum.PDF)
{
fileType = ".pdf";
}
else if (printFileType == PrintFileTypeEnum.XLSX)
{
fileType = ".xlsx";
}
else if (printFileType == PrintFileTypeEnum.DOCX)
{
fileType = ".docx";
}
else if (printFileType == PrintFileTypeEnum.XLS)
{
fileType = ".xls";
}
else if (printFileType == PrintFileTypeEnum.DOC)
{
fileType = ".doc";
}
string curFileName = fileNameNoSuffix;
//var id = SnowFlakeSingle.Instance.NextId();
var fileSaveName = $"{curFileName}{fileType}";
string fileRelaPath = Path.Combine(relativePath, fileSaveName);
string fileAbsPath = Path.Combine(dirAbs, fileSaveName);
if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{
relativePath = relativePath.Replace("\\", "/");
fileRelaPath = fileRelaPath.Replace("\\", "/");
fileAbsPath = fileAbsPath.Replace("\\", "/");
}
//logger.LogInformation("批次={no} 生成文件保存路径完成 路由={filePath} 服务器系统={system}", batchNo, fileRelaPath, RuntimeInformation.OSDescription);
await File.WriteAllBytesAsync(fileAbsPath, fileBytes);
//string bookFilePath;
//if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
//{
// bookFilePath = System.Text.RegularExpressions.Regex.Match(fileAbsPath, relativePath.Replace("/", "\\/") + ".*").Value;
//}
//else
//{
// bookFilePath = System.Text.RegularExpressions.Regex.Match(fileAbsPath, relativePath.Replace("\\", "\\\\") + ".*").Value;
//}
return fileAbsPath;
//return bookFilePath;
}
/// <summary>
/// 获取文件类型
/// </summary>
/// <param name="fileName">文件完成路径</param>
/// <returns>返回文件类型枚举</returns>
protected PrintFileTypeEnum GetFileType(string fileName)
{
PrintFileTypeEnum fileType = PrintFileTypeEnum.PDF;
switch (Path.GetExtension(fileName).ToLower())
{
case ".pdf":
fileType = PrintFileTypeEnum.PDF;
break;
case ".xlsx":
fileType = PrintFileTypeEnum.XLSX;
break;
case ".docx":
fileType = PrintFileTypeEnum.DOCX;
break;
case ".xls":
fileType = PrintFileTypeEnum.XLS;
break;
case ".doc":
fileType = PrintFileTypeEnum.DOC;
break;
}
return fileType;
}
#endregion
/// <summary>
/// 根据任务ID获取附件信息
/// </summary>
/// <param name="taskId">任务Id</param>
/// <param name="fileCategory">附件分类代码</param>
public async Task<(string fileFullPath, string fileName)> GetTaskFileInfo(long taskId, string fileCategory)
{
var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
//任务不考虑OrgId,这里去掉
tenantDb.QueryFilter.Clear<IOrgId>();
var bcTaskInfo = await tenantDb.Queryable<TaskBaseInfo>().Where(u => u.Id == taskId).FirstAsync();
if (bcTaskInfo == null)
{
throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.DataQueryNoData)));
}
TaskFileCategoryEnum fileCategoryEnum = TaskFileCategoryEnum.NONE;
System.Enum.TryParse(fileCategory, out fileCategoryEnum);
if (fileCategoryEnum == TaskFileCategoryEnum.NONE)
{
// 附件分类代码错误,请提供正确的分类代码
throw new Exception(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileCategoryError)));
}
string name = fileCategoryEnum.ToString();
var fileInfo = await tenantDb.Queryable<TaskFileInfo>().Where(u => u.TASK_PKID == taskId && u.FILE_CATEGORY == name).OrderByDescending(x => x.Id).FirstAsync();
if (fileInfo == null)
{
// 附件分类代码错误,请提供正确的分类代码
throw new Exception(
string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileEmpty)), taskId)
);
}
var basePath = AppSetting.app(new string[] { "FileSettings", "BasePath" });
string fileFullPath;
if (string.IsNullOrEmpty(basePath))
{
fileFullPath = Path.Combine(environment.WebRootPath ?? "", fileInfo.FILE_PATH);
}
else
{
fileFullPath = Path.Combine(basePath, fileInfo.FILE_PATH);
}
if (!File.Exists(fileFullPath))
{
logger.LogError("根据任务主键获取文件信息失败文件不存在fileFullPath={fileFullPath}", fileFullPath);
//任务主键{0} 附件下载请求失败,请确认文件是否存在
throw new Exception(
string.Format(MultiLanguageConst.GetDescription(nameof(MultiLanguageConst.TaskFileNotExists)), taskId)
);
}
var fileName = HttpUtility.UrlEncode(fileInfo.FILE_NAME, Encoding.GetEncoding("UTF-8"))!;
return (fileFullPath, fileName);
//return (new FileStream(fileFullPath, FileMode.Open), fileName);
}
/// <summary>
/// 根据订单及配置,将所有或指定的公共任务匹配到个人
/// </summary>
/// <param name="taskIdList">任务Id列表当传入时则只匹配列表中指定的任务</param>
/// <returns>涉及当前登陆人的匹配结果</returns>
public async Task<DataResult<MatchTaskResultDto>> MatchTask(List<long>? taskIdList = null)
{
MatchTaskResultDto result = new MatchTaskResultDto();
var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId);
//任务不考虑OrgId,这里去掉
tenantDb.QueryFilter.Clear<IOrgId>();
var taskList = await tenantDb.Queryable<TaskBaseInfo>()
.Where(x => x.IS_PUBLIC == 1
&& x.STATUS == TaskStatusEnum.Create.ToString()
&& !string.IsNullOrEmpty(x.MBL_NO))
.WhereIF(taskIdList != null && taskIdList.Count > 0, x => taskIdList!.Contains(x.Id))
.ToListAsync(x => new TaskBaseInfo
{
Id = x.Id,
MBL_NO = x.MBL_NO,
TASK_TYPE = x.TASK_TYPE,
TASK_TYPE_NAME = x.TASK_TYPE_NAME,
CARRIER_ID = x.CARRIER_ID,
});
var allotSetList = await allocationService.Value.GetAllList();
if (!allotSetList.Succeeded || allotSetList.Data?.Any() != true)
{
return DataResult<MatchTaskResultDto>.Success("操作成功!", result, MultiLanguageConst.DataUpdateSuccess);
}
// 需要查询的订单的提单号的集合,用于一次性将需要查询的订单查询出来;
HashSet<string> waitQuerySeaExportWithMblnoList = new();
// 后续如果需要查询舱位可以再补充字段如List<string> waitQuerySlotWithMblnoList = new();
List<(TaskBaseInfo task, TaskAllocationtSetDto allotSet, List<RecvUserInfo> allotUserList)> allotData = new();
foreach (var item in taskList)
{
// 查找配置规则
var targetAllotSet = allotSetList.Data.Where(x => x.TaskTypeCode == item.TASK_TYPE && x.CarrierId == item.CARRIER_ID).OrderBy(x => x.Id).FirstOrDefault();
if (targetAllotSet == null)
{
targetAllotSet = allotSetList.Data.Where(x => x.TaskTypeCode == item.TASK_TYPE).OrderBy(x => x.Id).FirstOrDefault();
// 如果某种任务没有配置接收对象,则该任务跳过分配
if (targetAllotSet == null) continue;
}
allotData.Add((item, targetAllotSet!, new List<RecvUserInfo>()));
// 如果配置的下面四种接收对象,则需要查询订单
if (targetAllotSet.IsAllotCustomerService
|| targetAllotSet.IsAllotSale
|| targetAllotSet.IsAllotOperator
|| targetAllotSet.IsAllotVouchingClerk)
{
waitQuerySeaExportWithMblnoList.Add(item.MBL_NO!);
}
}
// 查出涉及到的订单
var seaExportList = await tenantDb.Queryable<SeaExport>()
.Where(x => x.ParentId == 0
&& (
(x.SplitOrMergeFlag != 0 && waitQuerySeaExportWithMblnoList.Contains(x.MBLNO))
|| (x.SplitOrMergeFlag == 1 && waitQuerySeaExportWithMblnoList.Contains(x.BookingNo))
)).Select(x => new
{
x.Id,
x.MBLNO,
x.OperatorId,
x.OperatorName,
x.Doc,
x.DocName,
x.SaleId,
x.Sale,
x.CustomerService,
x.CustomerServiceName,
x.ForeignCustomerService,
x.ForeignCustomerServiceName
}).ToListAsync();
foreach (var item in allotData)
{
// 如果某条任务配置的接收目标为销售、操作、单证、客服中的一项,则需要查订单
if (item.allotSet.IsAllotCustomerService
|| item.allotSet.IsAllotOperator
|| item.allotSet.IsAllotSale
|| item.allotSet.IsAllotVouchingClerk)
{
var order = seaExportList.FirstOrDefault(x => x.MBLNO == item.task.MBL_NO);
if (order == null)
{
continue;
}
/*
* 操作Operator=订单里的OperatorId+OperatorName
* 单证VouchingClerk=订单里的Doc+DocName
* 销售Sale=订单里的SaleId+Sale
* 客服CustomerService=订单里的CustomerService+CustomerServiceName / ForeignCustomerService+ForeignCustomerServiceName
*/
if (item.allotSet.IsAllotCustomerService)
{
if (order.CustomerService != 0 && !string.IsNullOrEmpty(order.CustomerServiceName))
{
item.allotUserList.Add(new RecvUserInfo(order.CustomerService, order.CustomerServiceName));
}
if (order.ForeignCustomerService != 0 && !string.IsNullOrEmpty(order.ForeignCustomerServiceName))
{
item.allotUserList.Add(new RecvUserInfo(order.ForeignCustomerService, order.ForeignCustomerServiceName));
}
}
if (item.allotSet.IsAllotOperator
&& order.OperatorId != 0 && !string.IsNullOrEmpty(order.OperatorName))
{
item.allotUserList.Add(new RecvUserInfo(order.OperatorId, order.OperatorName));
}
if (item.allotSet.IsAllotSale
&& order.SaleId != 0 && !string.IsNullOrEmpty(order.Sale))
{
item.allotUserList.Add(new RecvUserInfo(order.SaleId, order.Sale));
}
if (item.allotSet.IsAllotVouchingClerk
&& order.Doc != 0 && !string.IsNullOrEmpty(order.DocName))
{
item.allotUserList.Add(new RecvUserInfo(order.Doc, order.DocName));
}
}
// 拓展:如果某条任务配置的接收目标为...中的一项,则需要查...
}
allotData = allotData.Where(x => x.allotUserList.Any()).ToList();
foreach (var item in allotData)
{
await SetTaskOwner([item.task.Id], item.allotUserList);
}
var userId = long.Parse(user.UserId);
var currentUserRelateTask = allotData.Where(x => x.allotUserList.Any(y => y.RecvUserId == userId))
.Select(x => x.task).ToList();
result.LoginUserMatchTaskIdList = currentUserRelateTask.Select(x => x.Id).ToList();
result.LoginUserMatchTaskList = currentUserRelateTask.GroupBy(x => new { x.TASK_TYPE, x.TASK_TYPE_NAME })
.Select(x => new MatchTaskClassifyDto()
{
TaskType = x.Key.TASK_TYPE,
TaskTypeName = x.Key.TASK_TYPE_NAME!,
Count = x.Count()
}).ToList();
return DataResult<MatchTaskResultDto>.Success(result, MultiLanguageConst.DataUpdateSuccess);
}
}
}