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 { /// /// 任务模块业务类的基类,封装了一些常用的方法 /// public class TaskManageBaseService : ITaskManageBaseService { // 实例化时构建 protected readonly IUser user; protected readonly ILogger logger; protected readonly ISaasDbService saasDbService; protected readonly IServiceProvider serviceProvider; protected readonly IWebHostEnvironment environment; // 按需构建 private Lazy allocationService; public TaskManageBaseService(IUser user, ILogger 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(serviceProvider.GetRequiredService()); } /// /// 更新任务主表状态 /// /// 任务主键数组 /// 需要更新状态的列 public async Task SetTaskStatus(long[] taskIds, params Expression>[] columns) { SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var updateable = tenantDb.Updateable(); 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(); // 后面可以记录日志 } /// /// 设置任务处理人 /// /// 任务主键数组 /// 人员信息列表 public async Task SetTaskOwner(long[] taskIds, List userInfo) { SqlSugarScopeProvider tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); try { var taskList = await tenantDb.Queryable().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 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(x => taskIdList.Contains(x.TaskId)).ExecuteCommandAsync(); await tenantDb.Insertable(allocationList).ExecuteCommandAsync(); await tenantDb.Updateable() .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 /// /// 保存文件并返回文件完整路径 /// /// 追加文件夹 /// 文件二进制流 /// 批次号 /// 无拓展名的文件名 /// 文件类型 /// 附件类型 bcfiles-BC文件 sofile-订舱附件 /// 返回文件完整路径 protected async Task 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; } /// /// 获取文件类型 /// /// 文件完成路径 /// 返回文件类型枚举 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 /// /// 根据任务ID获取附件信息 /// /// 任务Id /// 附件分类代码 public async Task<(string fileFullPath, string fileName)> GetTaskFileInfo(long taskId, string fileCategory) { var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var bcTaskInfo = await tenantDb.Queryable().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().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); } /// /// 根据订单及配置,将所有或指定的公共任务匹配到个人 /// /// 任务Id列表(当传入时,则只匹配列表中指定的任务) /// 涉及当前登陆人的匹配结果 public async Task> MatchTask(List? taskIdList = null) { MatchTaskResultDto result = new MatchTaskResultDto(); var tenantDb = saasDbService.GetBizDbScopeById(user.TenantId); //任务不考虑OrgId,这里去掉 tenantDb.QueryFilter.Clear(); var taskList = await tenantDb.Queryable() .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.Success("操作成功!", result, MultiLanguageConst.DataUpdateSuccess); } // 需要查询的订单的提单号的集合,用于一次性将需要查询的订单查询出来; HashSet waitQuerySeaExportWithMblnoList = new(); // 后续如果需要查询舱位,可以再补充字段如List waitQuerySlotWithMblnoList = new(); List<(TaskBaseInfo task, TaskAllocationtSetDto allotSet, List 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())); // 如果配置的下面四种接收对象,则需要查询订单 if (targetAllotSet.IsAllotCustomerService || targetAllotSet.IsAllotSale || targetAllotSet.IsAllotOperator || targetAllotSet.IsAllotVouchingClerk) { waitQuerySeaExportWithMblnoList.Add(item.MBL_NO!); } } // 查出涉及到的订单 var seaExportList = await tenantDb.Queryable() .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.Success(result, MultiLanguageConst.DataUpdateSuccess); } } }