using Furion; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.FriendlyException; using Mapster; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.ConfigOption; using Myshipping.Application.Entity; using Myshipping.Core; using Myshipping.Core.Service; using System; using System.Collections.Generic; using System.IO; using System.Linq; using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; namespace Myshipping.Application.Service.TaskManagePlat { /// /// 任务分享链接服务 /// [ApiDescriptionSettings("Application", Name = "TaskManageShareLink", Order = 10)] public class TaskManageShareLinkService : ITaskManageShareLinkService, IDynamicApiController { private readonly ISysCacheService _cache; private readonly ILogger _logger; private readonly SqlSugarRepository _taskShareLinkInfoRepository; private readonly SqlSugarRepository _taskShareLinkDynamicDataInfoRepository; private readonly SqlSugarRepository _taskBaseRepository; private readonly SqlSugarRepository _taskRollingNominationInfoRepository; private readonly SqlSugarRepository _taskRollingNominationDispatchInfoRepository; private readonly SqlSugarRepository _taskRollingNominationShipInfoRepository; private readonly SqlSugarRepository _taskRollingNominationDetailInfoRepository; private const string SHARE_LINK_CACHE_KEY_TEMPLATE = "ShareLinkKeyIncrement"; public TaskManageShareLinkService(ISysCacheService cache, ILogger logger, SqlSugarRepository taskShareLinkInfoRepository, SqlSugarRepository taskShareLinkDynamicDataInfoRepository, SqlSugarRepository taskBaseRepository, SqlSugarRepository taskRollingNominationInfoRepository, SqlSugarRepository taskRollingNominationDispatchInfoRepository, SqlSugarRepository taskRollingNominationShipInfoRepository, SqlSugarRepository taskRollingNominationDetailInfoRepository) { _cache = cache; _logger = logger; _taskShareLinkInfoRepository = taskShareLinkInfoRepository; _taskShareLinkDynamicDataInfoRepository = taskShareLinkDynamicDataInfoRepository; _taskBaseRepository = taskBaseRepository; _taskRollingNominationInfoRepository = taskRollingNominationInfoRepository; _taskRollingNominationDispatchInfoRepository = taskRollingNominationDispatchInfoRepository; _taskRollingNominationShipInfoRepository = taskRollingNominationShipInfoRepository; _taskRollingNominationDetailInfoRepository = taskRollingNominationDetailInfoRepository; } #region 生成访问链接 /// /// 生成访问链接 /// /// 创建分享链接请求 /// 返回回执 [HttpPost("/TaskManageShareLink/CreateShareLink")] public async Task CreateShareLink([FromBody] ShareLinkRequestDto model) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); /* 1、判断当前businessId是否有STATUS=ACTIVE的记录,如果有记录不允许重复分享。 2、创建分享记录,并生成动态数据(预甩需要生成免责声明) 3、判断有效期是否小于等于当前日期,如果是不能生成分享。 4、创建完记录后,把生成后的SHARE_LINK_KEY写入redis并用expireDate作为失效日期。 5、返回SHARE_LINK_KEY。 */ try { string shareKey = string.Empty; if (string.IsNullOrWhiteSpace(model.businessId)) throw Oops.Oh($"业务ID不能为空"); if (string.IsNullOrWhiteSpace(model.taskType)) throw Oops.Oh($"任务类型不能为空"); if (string.IsNullOrWhiteSpace(model.businessType)) throw Oops.Oh($"业务类型不能为空"); if (string.IsNullOrWhiteSpace(model.expireDate)) throw Oops.Oh($"失效时间不能为空"); DateTime expireDateTime = DateTime.MinValue; if(!DateTime.TryParse(model.expireDate, out expireDateTime)) throw Oops.Oh($"失效时间格式错误,请参考格式yyyy-MM-dd HH:mm:ss"); if(expireDateTime <= DateTime.Now) throw Oops.Oh($"失效时间不能早于或等于当前时间"); if (!model.businessType.Equals("NOMI_DISPATCH", StringComparison.OrdinalIgnoreCase)) { throw Oops.Oh($"当前只支持预甩调度分享"); } var nomDispatchList = _taskRollingNominationDispatchInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((dispatch,detail) => dispatch.DETAIL_ID == detail.PK_ID) .Where((dispatch,detail) => dispatch.BATCH_ID == model.businessId && detail.IsDeleted == false && dispatch.IsDeleted == false) .Select((dispatch, detail) => new { Detail = detail, Dispatch = dispatch }).ToList(); if (nomDispatchList.Count == 0) throw Oops.Oh($"预甩调度获取详情失败,已删除或不存在"); if (nomDispatchList.Any(a => !string.IsNullOrWhiteSpace(a.Dispatch.USER_OPINION))) throw Oops.Oh($"当前预甩调度已有用户反馈,不能创建分享"); var shareInfo = _taskShareLinkInfoRepository.AsQueryable() .First(a => a.BUSI_ID == model.businessId && a.TASK_TYPE == model.taskType && a.STATUS == "ACTIVE"); if (shareInfo != null) { throw Oops.Oh($"已有分享记录不能重复生成"); } DateTime nowDate = DateTime.Now; TaskShareLinkInfo taskShareLinkInfo = new TaskShareLinkInfo { EXPIRE_DATE = expireDateTime, STATUS = "ACTIVE", BUSI_ID = model.businessId, IS_MANUAL = false, CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, TASK_TYPE = model.taskType, IS_USER_FEEDBACK = model.isUserFeedBack, }; //写入分享记录 await _taskShareLinkInfoRepository.InsertAsync(taskShareLinkInfo); _logger.LogInformation($"写入分享记录表完成,id={taskShareLinkInfo.Id}"); var autoIncrementKey = RedisHelper.IncrBy(SHARE_LINK_CACHE_KEY_TEMPLATE); //生成分享KEY SuperShortLinkHelper codeHelper = new SuperShortLinkHelper(); shareKey = codeHelper.Confuse(autoIncrementKey); _logger.LogInformation($"生成分享KEY完成,id={taskShareLinkInfo.Id} shareKey={shareKey}"); //更新分享表 var shareEntity = _taskShareLinkInfoRepository.AsQueryable().First(a => a.Id == taskShareLinkInfo.Id); string taskId = nomDispatchList.FirstOrDefault().Dispatch.TASK_ID; var taskInfo = _taskBaseRepository.AsQueryable().First(a => a.PK_ID == taskId); //写入redis缓存 string cacheVal = $"{taskInfo.TASK_TYPE}_{taskInfo.TASK_NO}_{nomDispatchList.FirstOrDefault().Detail.SHIPMENT}_{taskInfo.TenantName}_{model.expireDate}"; var expireTimeSpan = expireDateTime.Subtract(nowDate).Duration(); //new DateTimeOffset(expireDateTime).ToUnixTimeSeconds(); if (_cache.Exists(shareKey)) { shareEntity.SHARE_LINK_KEY = shareKey; shareEntity.STATUS = "REPEAT_KEY";//REPEAT_KEY-重复KEY被取消 shareEntity.INCREMENT_KEY = autoIncrementKey; await _taskShareLinkInfoRepository.AsUpdateable(shareEntity) .UpdateColumns(it => new { it.SHARE_LINK_KEY, it.STATUS, it.INCREMENT_KEY }).ExecuteCommandAsync(); _logger.LogInformation($"分享KEY存在重复终止生成分享(cache),id={taskShareLinkInfo.Id} shareKey={shareKey} cache={_cache.Get(shareKey)}"); throw Oops.Oh($"已有分享记录不能重复生成"); } else { shareEntity.SHARE_LINK_KEY = shareKey; shareEntity.INCREMENT_KEY = autoIncrementKey; await _taskShareLinkInfoRepository.AsUpdateable(shareEntity) .UpdateColumns(it => new { it.SHARE_LINK_KEY, it.INCREMENT_KEY }).ExecuteCommandAsync(); await _cache.SetTimeoutAsync(shareKey, cacheVal, expireTimeSpan); _logger.LogInformation($"分享KEY写入cache完成,id={taskShareLinkInfo.Id} shareKey={shareKey} cache={cacheVal}"); //这里生成动态数据 if (taskInfo.TASK_BASE_TYPE == TaskBaseTypeEnum.ROLLING_NOMINATION.ToString() || taskInfo.TASK_BASE_TYPE == TaskBaseTypeEnum.TRANSFER_NOMINATION.ToString()) { var shareTxt = GenerateShareDynamicData(expireDateTime, taskInfo.TenantName); TaskShareLinkDynamicDataInfo dataInfo = new TaskShareLinkDynamicDataInfo { SHARE_ID = shareEntity.Id, DATA_TYPE = "DISCLAIMERS", DATA_MSG = shareTxt, DATA_MSG_TYPE = "HTML", CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = UserManager.UserId, CreatedUserName = UserManager.Name, }; await _taskShareLinkDynamicDataInfoRepository.InsertAsync(dataInfo); } } result.succ = true; result.ext = shareKey; } catch (Exception ex) { _logger.LogError($"获取预甩详情异常,原因:{ex.Message}"); result.succ = false; result.msg = $"获取预甩详情异常,原因:{ex.Message}"; } return result; } #endregion #region 取消访问链接 /// /// 取消访问链接 /// /// 访问链接主键 /// 返回回执 public async Task CancelShareLink(string pkId) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); return result; } #endregion #region 校验成访问链接 /// /// 校验成访问链接 /// /// 请求链接 /// 返回回执 public async Task ValidateShareLink(string Url) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); return result; } #endregion #region 访问链接 /// /// 访问链接 /// /// 请求链接 /// 返回回执 public async Task QueryShareLink(string Url) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); return result; } #endregion #region 获取分享详情 /// /// 获取分享详情 /// /// 链接分享KEY /// 返回回执 [AllowAnonymous,HttpGet("/TaskManageShareLink/GetInfo")] public async Task GetInfo([FromQuery] string shareKey) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); /* 1、先验证shareKey是否在缓存存在,存在继续,不存在返回错误。 2、获取分享的表记录,根据任务类型调取不同的接口,返回详情数据。 3、查看是否存在分享的声明内容,如果存在,需要返回。 */ try { if (string.IsNullOrWhiteSpace(shareKey)) throw Oops.Oh($"链接分享KEY不能为空"); if (!_cache.Exists(shareKey)) throw Oops.Oh($"链接分享已失效"); var shareEntity = _taskShareLinkInfoRepository.AsQueryable() .First(a => a.SHARE_LINK_KEY == shareKey); if (shareEntity == null) throw Oops.Oh($"链接分享不存在"); var showModel = await InnerGetInfo(shareEntity.BUSI_ID); var list = _taskShareLinkDynamicDataInfoRepository.AsQueryable() .Where(a => a.SHARE_ID == shareEntity.Id).ToList(); if (list.Count > 0) { var dyData = list.FirstOrDefault(a => a.DATA_TYPE == "DISCLAIMERS"); if (dyData != null) showModel.DynamicData = dyData.DATA_MSG; } result.succ = true; result.ext = showModel; } catch(Exception ex) { _logger.LogError($"获取分享详情异常,原因:{ex.Message}"); result.succ = false; result.msg = $"获取分享详情异常,原因:{ex.Message}"; } return result; } #endregion #region 生成分享的动态数据 /// /// 生成分享的动态数据 /// /// 最后失效时间 /// 企业名称 /// 返回填充模板后文本 private string GenerateShareDynamicData(DateTime expireDateTime,string TenantCompanyName) { string result = string.Empty; try { //调取模板 string templatePath = App.Configuration["EmailTemplateFilePath"]; var opt = App.GetOptions(); var dirAbs = opt.basePath; if (string.IsNullOrEmpty(dirAbs)) { dirAbs = App.WebHostEnvironment.WebRootPath; } templatePath = $"{dirAbs}{templatePath}\\NominationShareTemplate.html"; result = File.ReadAllText(templatePath); if (!string.IsNullOrWhiteSpace(result)) { if (Regex.IsMatch(result, "#DeadLineDate#")) result = Regex.Replace(result, "#DeadLineDate#", expireDateTime.ToString("yyyy-MM-dd HH:mm:ss")); if (Regex.IsMatch(result, "#TenantCompanyName#")) result = Regex.Replace(result, "#TenantCompanyName#", TenantCompanyName); } } catch (Exception ex) { _logger.LogError($"生成分享动态数据异常,原因:{ex.Message}"); } return result; } #endregion #region 获取预甩详情 /// /// 获取预甩详情 /// /// 预甩货详调度批次号 /// 返回详情 private async Task InnerGetInfo(string dispatchBatchId) { TaskRollingNominationShowDto model = null; try { List rollingPlanList = new List(); var list = _taskRollingNominationInfoRepository.AsQueryable().Filter(null, true) .InnerJoin((nom, dispatch) => nom.PK_ID == dispatch.NOM_ID) .InnerJoin((nom, dispatch, detail) => dispatch.DETAIL_ID == detail.PK_ID) .Where((nom, dispatch, detail) => dispatch.BATCH_ID == dispatchBatchId && nom.IsDeleted == false && detail.IsDeleted == false && dispatch.IsDeleted == false) .Select((nom, dispatch, detail) => new { Nom = nom, Detail = detail, Dispatch = dispatch }).ToList(); var rollModel = list.FirstOrDefault().Nom; if (!string.IsNullOrWhiteSpace(rollModel.PLAN_TXT)) { rollingPlanList = rollModel.PLAN_TXT.Split("\\n").ToList(); } model = new TaskRollingNominationShowDto { PlanType = rollModel.PLAN_TYPE, CarrierId = rollModel.CARRIERID, Carrier = rollModel.CARRIER, ConfirmDeadLine = rollModel.CONFIRM_DEAD_LINE, CreateTime = rollModel.READ_CREATE_TIME, RollingTouchDoubleRollRemark = rollModel.ROLL_DOUBLE_REMARK, LoadDetailList = new List(), RollingPlanList = rollingPlanList, PreBillList = new List() }; var shipList = _taskRollingNominationShipInfoRepository.AsQueryable() .Where(a => a.NOM_ID == rollModel.PK_ID && a.GROUP_INDX == 1 && !a.IsDeleted).ToList(); var fromEntity = shipList.FirstOrDefault(a => a.SHIP_TYPE.Equals("From", StringComparison.OrdinalIgnoreCase)); if (fromEntity != null) model.From = fromEntity.Adapt(); var toEntity = shipList.FirstOrDefault(a => a.SHIP_TYPE.Equals("To", StringComparison.OrdinalIgnoreCase)); if (toEntity != null) model.To = toEntity.Adapt(); List> tuples = new List>(); model.LoadDetailList = list.Select(a => a.Detail.Adapt()).ToList(); if (model.LoadDetailList.Count > 0) { model.TotalLoadCtnStat = string.Join(",", model.LoadDetailList.GroupBy(x => x.CtnAll) .Select(x => $"{x.Key}*{x.Sum(t => t.CtnNum)}").ToArray()); } } catch (Exception ex) { _logger.LogError($"获取预甩详情异常,原因:{ex.Message}"); throw ex; } return model; } #endregion } }