using Furion.DependencyInjection; using Furion.DistributedIDGenerator; using Furion.DynamicApiController; using Furion.FriendlyException; using Furion.JsonSerialization; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Logging; using Myshipping.Application.Entity; using Myshipping.Core; using Myshipping.Core.Entity; using NPOI.SS.Formula.Functions; using Org.BouncyCastle.Asn1.Tsp; using Org.BouncyCastle.Ocsp; using StackExchange.Profiling.Internal; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Myshipping.Application { /// /// 服务流程管理 /// [ApiDescriptionSettings("Application", Name = "ServiceWorkFlowManage", Order = 30)] public class ServiceWorkFlowManageService : IServiceWorkFlowManageService, IDynamicApiController, ITransient { private readonly ILogger _logger; private readonly SqlSugarRepository _serviceWorkFlowBaseRepository; private readonly SqlSugarRepository _serviceWorkFlowActivitiesInfoRepository; private readonly SqlSugarRepository _serviceWorkFlowProjectRelationRepository; private readonly SqlSugarRepository _serviceWorkFlowActivitiesRelationRepository; private readonly SqlSugarRepository _serviceWorkFlowActivitiesSubRelationRepository; private readonly SqlSugarRepository _serviceWorkFlowReleaseInfoRepository; private readonly SqlSugarRepository _serviceWorkFlowRunInfoRepository; private readonly SqlSugarRepository _serviceWorkFlowRunActivitiesInfoRepository; private readonly SqlSugarRepository _statusSkuBaseInfoRepository; private readonly SqlSugarRepository _sysUserRepository; private readonly IServiceWorkFlowBaseService _serviceWorkFlowBaseService; public ServiceWorkFlowManageService(SqlSugarRepository serviceWorkFlowBaseRepository, ILogger logger, SqlSugarRepository serviceWorkFlowActivitiesInfoRepository, SqlSugarRepository serviceWorkFlowProjectRelationRepository, SqlSugarRepository serviceWorkFlowActivitiesRelationRepository, SqlSugarRepository serviceWorkFlowActivitiesSubRelationRepository, SqlSugarRepository serviceWorkFlowReleaseInfoRepository, SqlSugarRepository serviceWorkFlowRunInfoRepository, SqlSugarRepository serviceWorkFlowRunActivitiesInfoRepository, SqlSugarRepository statusSkuBaseInfoRepository, IServiceWorkFlowBaseService serviceWorkFlowBaseService, SqlSugarRepository sysUserRepository) { _serviceWorkFlowBaseRepository = serviceWorkFlowBaseRepository; _serviceWorkFlowActivitiesInfoRepository = serviceWorkFlowActivitiesInfoRepository; _serviceWorkFlowProjectRelationRepository = serviceWorkFlowProjectRelationRepository; _serviceWorkFlowActivitiesRelationRepository = serviceWorkFlowActivitiesRelationRepository; _logger = logger; _serviceWorkFlowActivitiesSubRelationRepository = serviceWorkFlowActivitiesSubRelationRepository; _serviceWorkFlowReleaseInfoRepository = serviceWorkFlowReleaseInfoRepository; _serviceWorkFlowRunInfoRepository = serviceWorkFlowRunInfoRepository; _serviceWorkFlowRunActivitiesInfoRepository = serviceWorkFlowRunActivitiesInfoRepository; _statusSkuBaseInfoRepository = statusSkuBaseInfoRepository; _serviceWorkFlowBaseService = serviceWorkFlowBaseService; _sysUserRepository = sysUserRepository; } #region 推送状态 /// /// 推送状态 /// /// 服务流程详情 /// 返回回执 [HttpPost("/ServiceWorkFlowManage/PushStatus")] public async Task PushStatus([FromBody] TrackingMessageInfo info) { TaskManageOrderResultDto result = new TaskManageOrderResultDto(); string batchNo = IDGen.NextID().ToString(); _logger.LogInformation("批次={no} 接收推送状态 msg={msg}", batchNo, JSON.Serialize(info)); try { /* 1、首先判断业务的主键,如果存在则需要提取所有主键下的服务流程活动,来更新。 2、状态可以批量处理。 */ if (info.Main == null) { _logger.LogInformation("批次={no} 接收推送状态错误 报文Main不能为空", batchNo); throw Oops.Oh($"报文Main不能为空", typeof(InvalidOperationException)); } if (string.IsNullOrWhiteSpace(info.Main.BusiSystemCode)) { _logger.LogInformation("批次={no} 接收推送状态错误 报文Main的业务系统代码不能为空", batchNo); throw Oops.Oh($"报文Main的业务系统代码不能为空", typeof(InvalidOperationException)); } if (string.IsNullOrWhiteSpace(info.Main.BusiId)) { _logger.LogInformation("批次={no} 接收推送状态错误 报文Main的业务主键不能为空", batchNo); throw Oops.Oh($"报文Main的业务主键不能为空", typeof(InvalidOperationException)); } if (info.Main.StatusList == null || info.Main.StatusList.Count == 0) { _logger.LogInformation("批次={no} 接收推送状态错误 报文Main的状态列表不能为空,并且至少需要提供一个以上的状态信息", batchNo); throw Oops.Oh($"报文Main的状态列表不能为空,并且至少需要提供一个以上的状态信息", typeof(InvalidOperationException)); } //校验状态代码是否一致,不一致直接返回错误不允许推送 var statusArg = info.Main.StatusList.Select(a => a?.StatusCode.ToUpper()) .Where(a => !string.IsNullOrWhiteSpace(a)).Distinct().ToArray(); if (statusArg.Length == 0) { _logger.LogInformation("批次={no} 报文Main的状态列表至少需要提供一个以上的状态信息", batchNo); throw Oops.Oh($"报文Main的状态列表至少需要提供一个以上的状态信息", typeof(InvalidOperationException)); } UserTendDto userTendInfo = null; //如果大简云用户ID不为空,接收人为空时,通过大简云用户ID关联订舱人ID if (!string.IsNullOrWhiteSpace(info.Main.DJYRecvUserId)) { userTendInfo = GetUserTendInfoByDJYUserId(info.Main.DJYRecvUserId, info.Main.DJYRecvUserEmail); } else { userTendInfo = GetUserTendInfo(info.Main.RecvUserId); } //根据状态编号检索对应的状态和活动信息,如果已经提取到的状态数量与推送的不一致不能直接返回错误 var skuList = _statusSkuBaseInfoRepository.AsQueryable() .LeftJoin((sts, act) => sts.PK_ID == act.STATUS_SKU_ID) .Where((sts, act) => statusArg.Contains(sts.STATUS_SKU_CODE) && !sts.IsDeleted && sts.IS_ENABLE == 1 && !act.IsDeleted) .Select((sts, act) => new { Sku = sts, Act = act }).ToList(); var reqStatusList = info.Main.StatusList.GroupJoin(skuList, l => l.StatusCode, r => r.Sku.STATUS_SKU_CODE, (l, r) => { var currList = r.ToList(); if (currList.Count > 0) return new { Exists = true, Sku = currList.FirstOrDefault().Sku,Act = currList.FirstOrDefault().Act,Req = l }; return new { Exists = false, Sku = new StatusSkuBaseInfo(), Act = new ServiceWorkFlowActivitiesInfo(),Req = l }; }).ToList(); if (reqStatusList.Any(a=>!a.Exists)) { var errList = reqStatusList.Where(a => !a.Exists) .Select(a=>a.Req.StatusCode).ToArray(); string errMsg = $"以下状态不存在 {(string.Join(",", errList))} 不能入库"; _logger.LogInformation("批次={no} {msg}", batchNo, errMsg); throw Oops.Oh(errMsg, typeof(InvalidOperationException)); } //先从运行表按主键获取运行主表和活动表 var runList = _serviceWorkFlowRunInfoRepository.AsQueryable() .LeftJoin( (m, s) => m.PK_ID == s.RUN_ID) .Where((m, s) => m.BUSI_ID == info.Main.BusiId && m.BUSI_SYSTEM_CODE.Equals(info.Main.BusiSystemCode) && !m.IsDeleted && !s.IsDeleted) .Select((m, s) => new { main = m, sub = s }).ToList(); //如果有已经运行的业务主键则只补充对应的活动表 if(runList.Count > 0) { reqStatusList.ForEach(async reqMd => { var currRun = runList.FirstOrDefault(x => x.sub.PK_ID == reqMd.Act.PK_ID); var currRunAct = currRun.sub; currRunAct.ACT_DATE = reqMd.Req.StatusDate; currRunAct.IS_YIELD = 1; await _serviceWorkFlowRunActivitiesInfoRepository.AsUpdateable(currRunAct).UpdateColumns(it => new { it.ACT_DATE, it.IS_YIELD, }).ExecuteCommandAsync(); }); } else { var actArg = reqStatusList.Select(a => a.Act.PK_ID).ToArray(); //获取最后发布的服务流程写入运行表 var wfRlt = _serviceWorkFlowBaseService.GetServiceWorkFlowListByActivities(actArg).GetAwaiter().GetResult(); if(!wfRlt.succ) { string errMsg = $"获取服务流程失败,原因:{wfRlt.msg}"; _logger.LogInformation("批次={no} {msg}", batchNo, errMsg); throw Oops.Oh(errMsg, typeof(InvalidOperationException)); } DateTime nowDate = DateTime.Now; var list = JSON.Deserialize>(JSON.Serialize(wfRlt.ext)); list.ForEach(async wf => { ServiceWorkFlowRunInfo serviceWorkFlowRunInfo = new ServiceWorkFlowRunInfo { PK_ID = IDGen.NextID().ToString(), SERVICE_WF_ID = wf.PKId, BUSI_SYSTEM_CODE = info.Main.BusiSystemCode.ToUpper(), BUSI_ID = info.Main.BusiId.ToUpper(), MBL_NO = info.Main?.MBlNo.ToUpper(), VESSEL_VOYNO = info.Main?.VesselVoyno.ToUpper(), ORDER_NO = info.Main?.OrderNo, STATUS = TaskStatusEnum.Create.ToString(), RELEASE_VERSION = wf.ReleaseVersion, ACTIVITIES_NUM = wf.StatusNum, CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = userTendInfo.userId, CreatedUserName = userTendInfo.userName, TenantId = userTendInfo.tendId, TenantName = userTendInfo.tenantName }; await _serviceWorkFlowRunInfoRepository.InsertAsync(serviceWorkFlowRunInfo); int endNum = wf.StatusSkuList.Max(sku => sku.SortNo); string lastActId = string.Empty; wf.StatusSkuList.ForEach(async sku => { var currReq = reqStatusList.FirstOrDefault(x => x.Act.PK_ID == sku.PKId); ServiceWorkFlowRunActivitiesInfo activitiesRunInfo = new ServiceWorkFlowRunActivitiesInfo { PK_ID = IDGen.NextID().ToString(), RUN_ID = serviceWorkFlowRunInfo.PK_ID, EXEC_SORT_NO = sku.SortNo, IS_START = sku.SortNo == 1 ? 1 : 0, IS_END = sku.SortNo == endNum ? 1 : 0, ACT_ID = sku.PKId, ACT_DATE = currReq.Req.StatusDate, ACT_VAL = currReq.Req.StatusVal, STATUS_SKU_CODE = sku.statusSkuBase.StatusSKUCode, STATUS_SKU_ID = sku.StatusSKUId, SHOW_NAME = sku.ShowName, IS_SUB = 0, IS_SUB_JUST = 0, IS_YIELD = 1, CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = userTendInfo.userId, CreatedUserName = userTendInfo.userName, TenantId = userTendInfo.tendId, TenantName = userTendInfo.tenantName, IsDeleted = false, SOURCE_TYPE = "AUTO" }; if(!string.IsNullOrWhiteSpace(lastActId)) activitiesRunInfo.NEXT_ACT_ID = lastActId; await _serviceWorkFlowRunActivitiesInfoRepository.InsertAsync(activitiesRunInfo); lastActId = activitiesRunInfo.PK_ID; if (sku.IsContainsSub == 1) { string lastSubActId = string.Empty; sku.SubList.ForEach(async sub => { var currSubReq = reqStatusList.FirstOrDefault(x => x.Act.PK_ID == sub.PKId); ServiceWorkFlowRunActivitiesInfo activitiesSubRunInfo = new ServiceWorkFlowRunActivitiesInfo { PK_ID = IDGen.NextID().ToString(), RUN_ID = serviceWorkFlowRunInfo.PK_ID, EXEC_SORT_NO = sub.SortNo, IS_START = sub.SortNo == 1 ? 1 : 0, IS_END = sub.SortNo == endNum ? 1 : 0, ACT_ID = sub.PKId, ACT_DATE = currSubReq.Req.StatusDate, ACT_VAL = currSubReq.Req.StatusVal, STATUS_SKU_CODE = sub.statusSkuBase.StatusSKUCode, STATUS_SKU_ID = sub.StatusSKUId, SHOW_NAME = sub.ShowName, IS_SUB = 1, IS_SUB_JUST = 1, IS_YIELD = 1, CreatedTime = nowDate, UpdatedTime = nowDate, CreatedUserId = userTendInfo.userId, CreatedUserName = userTendInfo.userName, TenantId = userTendInfo.tendId, TenantName = userTendInfo.tenantName, IsDeleted = false, SOURCE_TYPE = "AUTO" }; if (!string.IsNullOrWhiteSpace(lastSubActId)) activitiesSubRunInfo.NEXT_ACT_ID = lastSubActId; await _serviceWorkFlowRunActivitiesInfoRepository.InsertAsync(activitiesSubRunInfo); lastSubActId = activitiesSubRunInfo.PK_ID; }); } }); }); } result.succ = true; result.msg = "推送成功"; } catch (Exception ex) { result.succ = false; result.msg = $"推送状态失败,原因:{ex.Message}"; } return result; } #endregion /// /// 查询单票业务单服务项目查询 /// /// 服务流程详情 /// 返回回执 [HttpPost("/ServiceWorkFlowManage/QuerySingleBusinessPerServiceProject")] public async Task QuerySingleBusinessPerServiceProject(ServiceWorkFlowBaseDto info) { return new TaskManageOrderResultDto(); } /// /// 查询单票所有相关服务项目查询 /// /// 服务流程详情 /// 返回回执 [HttpPost("/ServiceWorkFlowManage/QuerySingleBusinessAll")] public async Task QuerySingleBusinessAll(ServiceWorkFlowBaseDto info) { return new TaskManageOrderResultDto(); } #region 查询订舱表查询用户和租户信息 /// /// 查询订舱表查询用户和租户信息 /// /// 大简云用户ID /// 大简云用户邮箱 /// 返回用户和租户信息 private UserTendDto GetUserTendInfoByDJYUserId(string djyUserId, string djyUserEmail) { UserTendDto userTendDto = null; //这里因为接口是不做授权验证的,所以这里直接写的动态sql提取了用户和租户信息 var userTendInfo = _sysUserRepository.EntityContext.Queryable("user").AS("sys_user") .AddJoinInfo("sys_tenant", "ten", "user.TenantId=ten.Id") .Where("user.DjyUserId=@id and user.Email like '%" + djyUserEmail + "%'", new { id = djyUserId }) .Select("user.Id as UserId,user.Name as UserName,ten.Id as TendId,ten.Name as TendName").First(); if (userTendInfo == null || userTendInfo.TendId == null) throw Oops.Oh("当前用户详情获取失败,请确认{0}赋值是否准确", nameof(TaskManageOrderMessageInfo.Main.TaskUserId)); userTendDto = new UserTendDto { userId = long.Parse(userTendInfo.UserId.ToString()), userName = userTendInfo.UserName.ToString(), tendId = long.Parse(userTendInfo.TendId.ToString()), tenantName = userTendInfo.TendName.ToString() }; return userTendDto; } #endregion #region 查询订舱表查询用户和租户信息 /// /// 查询订舱表查询用户和租户信息 /// /// 用户ID /// 返回用户和租户信息 private UserTendDto GetUserTendInfo(string userId) { UserTendDto userTendDto = null; //这里因为接口是不做授权验证的,所以这里直接写的动态sql提取了用户和租户信息 var userTendInfo = _sysUserRepository.EntityContext.Queryable("user").AS("sys_user") .AddJoinInfo("sys_tenant", "ten", "user.TenantId=ten.Id") .Where("user.Id=@id", new { id = long.Parse(userId) }) .Select("user.Id as UserId,user.Name as UserName,ten.Id as TendId,ten.Name as TendName").First(); if (userTendInfo == null || userTendInfo.TendId == null) throw Oops.Oh("当前用户详情获取失败,请确认{0}赋值是否准确", nameof(TaskManageOrderMessageInfo.Main.TaskUserId)); userTendDto = new UserTendDto { userId = long.Parse(userTendInfo.UserId.ToString()), userName = userTendInfo.UserName.ToString(), tendId = long.Parse(userTendInfo.TendId.ToString()), tenantName = userTendInfo.TendName.ToString() }; return userTendDto; } #endregion } }