diff --git a/web/Djy.Common/Const/BusinessType.cs b/web/Djy.Common/Const/BusinessType.cs index 679632f..e7f1039 100644 --- a/web/Djy.Common/Const/BusinessType.cs +++ b/web/Djy.Common/Const/BusinessType.cs @@ -23,6 +23,6 @@ /// /// AFR申报代码;名称为“AFR申报” /// - public const int AFR_REPORT = 999; + public const int AFR_REPORT = 27; } } diff --git a/web/djy.IService/Afr/IAfrService.cs b/web/djy.IService/Afr/IAfrService.cs index 8ae8d3e..150e4ed 100644 --- a/web/djy.IService/Afr/IAfrService.cs +++ b/web/djy.IService/Afr/IAfrService.cs @@ -23,7 +23,7 @@ namespace djy.IService.Afr Task Get(string gid); Task SaveInfo(AFRMaster input); Task Delete(string ids); - Task Send(string ids, string hids, int sendType); + Task<(bool isSuccess, string message)> Send(string ids, string hids, int sendType); Task SaveReceipt(AFRReceiptDto input); Task> GetHistory(string mid, string hid); } diff --git a/web/djy.Model/AFR/AFRBaseModel.cs b/web/djy.Model/AFR/AFRBaseModel.cs index ea8e537..0b5583d 100644 --- a/web/djy.Model/AFR/AFRBaseModel.cs +++ b/web/djy.Model/AFR/AFRBaseModel.cs @@ -14,7 +14,7 @@ namespace djy.Model.AFR /// /// 最后修改时间 /// - [Column(CanInsert = false)] + //[Column(CanInsert = false)] public DateTime? LastUpdate { get; set; } /// diff --git a/web/djy.Model/AFR/AFRHouse.cs b/web/djy.Model/AFR/AFRHouse.cs index 598045c..eece4e3 100644 --- a/web/djy.Model/AFR/AFRHouse.cs +++ b/web/djy.Model/AFR/AFRHouse.cs @@ -22,6 +22,17 @@ namespace djy.Model.Afr /// 是否删除 /// public bool IsDel { get; set; } + + /// + /// 最后更新的用户ID + /// + public string LastUpdateUserID { get; set; } + + /// + /// 最后更新的用户名称 + /// + public string LastUpdateUserName { get; set; } + ///// ///// 货代提单号唯一编号 同货代提单号,原始修改删除重发报文,该值要一致 @@ -42,6 +53,11 @@ namespace djy.Model.Afr /// public bool StateIsSend { get; set; } + /// + /// 状态:是否已经删除发送 + /// + public bool StateIsDelete { get; set; } + /// /// 状态:海关是否接收 /// diff --git a/web/djy.Model/AFR/AFRMaster.cs b/web/djy.Model/AFR/AFRMaster.cs index caafc5c..4445bab 100644 --- a/web/djy.Model/AFR/AFRMaster.cs +++ b/web/djy.Model/AFR/AFRMaster.cs @@ -21,6 +21,15 @@ namespace djy.Model.Afr /// 用户名称 /// public string UserName { get; set; } + /// + /// 最后更新的用户ID + /// + public string LastUpdateUserID { get; set; } + + /// + /// 最后更新的用户名称 + /// + public string LastUpdateUserName { get; set; } /// /// 公司ID @@ -145,7 +154,7 @@ namespace djy.Model.Afr /// /// 操作历史 /// - [Navigate(nameof(AFRMasterHistory.MID))] + [Navigate(nameof(AFRMasterHistory.PID))] public AFRMasterHistory History { get; set; } #endregion diff --git a/web/djy.Model/AFR/AFRMasterHistory.cs b/web/djy.Model/AFR/AFRMasterHistory.cs index 5481bac..0dcb2e1 100644 --- a/web/djy.Model/AFR/AFRMasterHistory.cs +++ b/web/djy.Model/AFR/AFRMasterHistory.cs @@ -22,7 +22,7 @@ namespace djy.Model.Afr /// /// Master表主键 /// - public string MID { get; set; } + public string PID { get; set; } /// /// House表主键 /// diff --git a/web/djy.Model/AFRDto/AFRReceiptDto.cs b/web/djy.Model/AFRDto/AFRReceiptDto.cs index 9d661fc..61679b1 100644 --- a/web/djy.Model/AFRDto/AFRReceiptDto.cs +++ b/web/djy.Model/AFRDto/AFRReceiptDto.cs @@ -6,7 +6,7 @@ public string content { get; set; } public string houseBillNo { get; set; } /// - /// 值有:Accept、Warning、Reject、Matched、Hold、Cancel + /// 值有:Accept海关接收、Warning警告、Reject海关拒绝、Matched已匹配、Hold海关监控、Cancel海关删除 /// public string status { get; set; } } diff --git a/web/djy.Service/Afr/AfrService.cs b/web/djy.Service/Afr/AfrService.cs index 7846875..45a981c 100644 --- a/web/djy.Service/Afr/AfrService.cs +++ b/web/djy.Service/Afr/AfrService.cs @@ -3,6 +3,7 @@ using Common; using Common.Const; using Common.DJYModel; using Common.Extensions; +using Common.Helpers; using Common.Tools; using Common.Utilities; using djy.IService.Afr; @@ -21,6 +22,7 @@ using Newtonsoft.Json.Linq; using NPOI.SS.Formula.Functions; using Org.BouncyCastle.Ocsp; using System; +using System.Collections; using System.Collections.Generic; using System.Data.Common; using System.Linq; @@ -211,66 +213,109 @@ namespace djy.Service.AFR public async Task> Load(AFRMasterInputDto input) { - ISelect select = null; - - bool isSend = false; - - // 查询草稿箱列表,草稿箱列表采用一主单内含有多个分单的显示模式 + List result = null; if (input.Type == 1) { - select = DbAMS.Select(); - isSend = false; - } - // 查询已发送列表,已发送列表采用按分单的显示方式,所以采用内连接的方式 - else if (input.Type == 2) - { - select = DbAMS.Select() - .InnerJoin((m, h) => m.GID == h.PID); - isSend = true; - } + ISelect select = DbAMS.Select(); - select.Where((m) => m.IsDel == false && m.StateIsSend == isSend) + select.Where((m) => m.IsDel == false && m.StateIsSend == false) //下面两个是Controller中传来的条件 - .WhereIf(!string.IsNullOrEmpty(input.CompanyId), m => m.CompID == input.CompanyId) - .WhereIf(!string.IsNullOrEmpty(input.UserId), m => m.UserID == input.UserId) + .WhereIf(!string.IsNullOrEmpty(input.CompanyId), (m) => m.CompID == input.CompanyId) + .WhereIf(!string.IsNullOrEmpty(input.UserId), (m) => m.UserID == input.UserId) // 下面是前端传来的条件 - .WhereIf(!string.IsNullOrEmpty(input.MBLNO), m => m.MBLNO.Contains(input.MBLNO)) - .WhereIf(!string.IsNullOrEmpty(input.UserName), m => m.UserName.Contains(input.UserName)) - .WhereIf(!string.IsNullOrEmpty(input.DischargeHarbour), m => m.DischargeHarbour.Contains(input.DischargeHarbour)) - .WhereIf(!string.IsNullOrEmpty(input.ShipCompanyName), m => m.ShipCompanyName.Contains(input.ShipCompanyName)) - .WhereIf(input.CreateTimeStart != null, m => m.CreateTime >= input.CreateTimeStart) - .WhereIf(input.CreateTimeEnd != null, m => m.CreateTime <= input.CreateTimeEnd); - - // 分单上的查询条件在这里,实现方式:查出分单中的PID,作为主单的筛选条件 - if (!string.IsNullOrEmpty(input.HouseBillNo)) + .WhereIf(!string.IsNullOrEmpty(input.MBLNO), (m) => m.MBLNO.Contains(input.MBLNO)) + .WhereIf(!string.IsNullOrEmpty(input.UserName), (m) => m.UserName.Contains(input.UserName)) + .WhereIf(!string.IsNullOrEmpty(input.DischargeHarbour), (m) => m.DischargeHarbour.Contains(input.DischargeHarbour)) + .WhereIf(!string.IsNullOrEmpty(input.ShipCompanyName), (m) => m.ShipCompanyName.Contains(input.ShipCompanyName)) + .WhereIf(input.CreateTimeStart != null, (m) => m.CreateTime >= input.CreateTimeStart) + .WhereIf(input.CreateTimeEnd != null, (m) => m.CreateTime <= input.CreateTimeEnd); + + // 分单上的查询条件在这里,实现方式:查出分单中的PID,作为主单的筛选条件 + if (!string.IsNullOrEmpty(input.HouseBillNo)) + { + var pids = await DbAMS.Select() + .Where(h => h.HouseBillNo.Contains(input.HouseBillNo)) + .ToListAsync(h => h.PID); + select.Where((m) => pids.Contains(m.GID)); + } + + result = await select.IncludeMany(m => m.HouseList, then => then.Where(h => h.IsDel == false)) + .Page(input) + .OrderByDescending(m => m.CreateTime) + .ToListAsync(); + } + else if (input.Type == 2) { - var pids = await DbAMS.Select() - .Where(h => h.HouseBillNo.Contains(input.HouseBillNo)) - .ToListAsync(h => h.PID); - select.Where(m => pids.Contains(m.GID)); + var select = DbAMS.Select() + .InnerJoin((m, h) => m.GID == h.PID); + + select.Where((m, h) => m.IsDel == false && m.StateIsSend == true) + //下面两个是Controller中传来的条件 + .WhereIf(!string.IsNullOrEmpty(input.CompanyId), (m, h) => m.CompID == input.CompanyId) + .WhereIf(!string.IsNullOrEmpty(input.UserId), (m, h) => m.UserID == input.UserId) + // 下面是前端传来的条件 + .WhereIf(!string.IsNullOrEmpty(input.MBLNO), (m, h) => m.MBLNO.Contains(input.MBLNO)) + .WhereIf(!string.IsNullOrEmpty(input.UserName), (m, h) => m.UserName.Contains(input.UserName)) + .WhereIf(!string.IsNullOrEmpty(input.DischargeHarbour), (m, h) => m.DischargeHarbour.Contains(input.DischargeHarbour)) + .WhereIf(!string.IsNullOrEmpty(input.ShipCompanyName), (m, h) => m.ShipCompanyName.Contains(input.ShipCompanyName)) + .WhereIf(input.CreateTimeStart != null, (m, h) => m.CreateTime >= input.CreateTimeStart) + .WhereIf(input.CreateTimeEnd != null, (m, h) => m.CreateTime <= input.CreateTimeEnd) + .WhereIf(!string.IsNullOrEmpty(input.HouseBillNo), (m, h) => h.HouseBillNo.Contains(input.HouseBillNo)); + + var dtoList = await select.Page(input) + .OrderByDescending((m, h) => m.CreateTime) + .ToListAsync((m, h) => new { AFRMaster = m, AFRHouse = h }); + + + result = new List(); + dtoList.ForEach(d => + { + d.AFRMaster.HouseList = new List() { d.AFRHouse }; + result.Add(d.AFRMaster); + }); } - List result = await select.IncludeMany(m => m.HouseList, then => then.Where(h => h.IsDel == false)) - .Page(input) - .OrderByDescending(m => m.CreateTime) - .ToListAsync(); - // 查询操作历史(只查询最新的一条人为操作历史) if (result.Count > 0) { // FreeSql版本较低,无法使用嵌套查询,所以采用拼sql的方式 - var pids = result.Select(m => m.GID); - var pidStr = string.Join("','", pids); - var histories = await DbAMS.Select() - .WithSql(@$"SELECT * FROM ( - SELECT *,row_number() over ( PARTITION BY Pid ORDER BY {nameof(AFRMasterHistory.CreateTime)} DESC ) AS row_num FROM AFR_MasterHistory WHERE {nameof(AFRMasterHistory.Type)} = 0 AND pid IN ('{pidStr}')) t WHERE row_num =1") - .ToListAsync(); - histories.ForEach(item => + if (input.Type == 1) { - var master = result.FirstOrDefault(m => m.GID == item.MID); - if (master == null) return; - master.History = item; - }); + var pids = result.Select(m => m.GID); + var pidStr = string.Join("','", pids); + List histories = await DbAMS.Select() + .WithSql(@$"SELECT * FROM ( + SELECT *,row_number() over ( PARTITION BY PID ORDER BY CreateTime DESC ) AS row_num FROM AFR_MasterHistory WHERE Type = 0 and PID IN ('{pidStr}')) t WHERE row_num =1") + .ToListAsync(); + + result.ForEach(master => + { + master.History = histories.FirstOrDefault(h => h.PID == master.GID); + }); + } + else if (input.Type == 2) + { + var pids = result.SelectMany(m => m.HouseList).Select(h => h.GID); + if (pids.Any()) + { + var pidStr = string.Join("','", pids); + + List histories = await DbAMS.Select() + .WithSql(@$"SELECT * FROM ( + SELECT *,row_number() over ( PARTITION BY HID ORDER BY CreateTime DESC ) AS row_num FROM AFR_MasterHistory WHERE Type = 0 and HID IN ('{pidStr}')) t WHERE row_num =1") + .ToListAsync(); + + result.ForEach(master => + { + if (master.HouseList?.Any() ?? false) + { + master.History = histories.FirstOrDefault(h => h.HID == master.HouseList[0].GID); + } + }); + } + } + + } return new PageModel(input.PageNumber, @@ -303,6 +348,9 @@ namespace djy.Service.AFR oldMaster = await Get(input.GID); type = oldMaster == null ? 0 : 1; } + input.LastUpdate = nowTime; + input.LastUpdateUserID = User.GID; + input.LastUpdateUserName = User.ShowName; // 新增 if (type == 0) { @@ -319,7 +367,10 @@ namespace djy.Service.AFR { house.GID = Guid.NewGuid().ToString(); house.PID = input.GID; + house.LastUpdateUserID = User.GID; + house.LastUpdateUserName = User.ShowName; house.CreateTime = nowTime; + house.LastUpdate = nowTime; house.MBLNO = input.MBLNO; // 将null换为空对象,避免后续处理null值麻烦 @@ -329,6 +380,7 @@ namespace djy.Service.AFR cntr.GID = Guid.NewGuid().ToString(); cntr.PID = house.GID; cntr.CreateTime = nowTime; + cntr.LastUpdate = nowTime; cntr.HouseBillNo = house.HouseBillNo; }); }); @@ -339,7 +391,7 @@ namespace djy.Service.AFR await DbAMS.Insert(input.HouseList).WithTransaction(tran).ExecuteAffrowsAsync(); await DbAMS.Insert(input.HouseList.SelectMany(h => h.CntrList)).WithTransaction(tran).ExecuteAffrowsAsync(); - await BuildAFRMasterHistoryAsync(input.GID, input.HouseList.Select(h => h.GID), "新增", 0, "创建了单据", tran); + await SaveHistoryAsync(input.GID, input.HouseList, "新增", 0, "创建了单据", tran); tran.Commit(); } @@ -350,7 +402,6 @@ namespace djy.Service.AFR // 1. 将null换为空对象,避免后续处理null值麻烦 // 2. 如果主键为null,说明是新加的数据,这时候需要手动给GID、PID、CreateTime赋值 // 3. 如果主键不为null,说明是修改的数据,这时候需要手动给LastUpdate赋值 - input.LastUpdate = nowTime; if (input.HouseList == null) { input.HouseList = new List(); @@ -359,6 +410,9 @@ namespace djy.Service.AFR { input.HouseList.ForEach(house => { + house.LastUpdate = nowTime; + house.LastUpdateUserID = User.GID; + house.LastUpdateUserName = User.ShowName; if (string.IsNullOrEmpty(house.GID)) { house.GID = Guid.NewGuid().ToString(); @@ -366,10 +420,6 @@ namespace djy.Service.AFR house.CreateTime = nowTime; house.MBLNO = input.MBLNO; } - else - { - house.LastUpdate = nowTime; - } if (house.CntrList == null) { @@ -379,6 +429,7 @@ namespace djy.Service.AFR { house.CntrList.ForEach(cntr => { + cntr.LastUpdate = nowTime; if (string.IsNullOrEmpty(cntr.GID)) { cntr.GID = Guid.NewGuid().ToString(); @@ -386,10 +437,6 @@ namespace djy.Service.AFR cntr.CreateTime = nowTime; cntr.HouseBillNo = house.HouseBillNo; } - else - { - cntr.LastUpdate = nowTime; - } }); } }); @@ -453,7 +500,7 @@ namespace djy.Service.AFR { await DbAMS.Insert(waitInsertHouseList).WithTransaction(tran).ExecuteAffrowsAsync(); - await BuildAFRMasterHistoryAsync(input.GID, waitInsertHouseGids, "新增", 0, "新增了单据", tran); + await SaveHistoryAsync(input.GID, waitInsertHouseList, "新增", 0, "新增了单据", tran); } if (waitDeleteHouseGids.Any()) { @@ -464,17 +511,17 @@ namespace djy.Service.AFR .Where(h => waitDeleteHouseGids.Contains(h.GID)) .ExecuteAffrowsAsync(); - await BuildAFRMasterHistoryAsync(input.GID, waitDeleteHouseGids, "删除", 0, "删除了单据", tran); + await SaveHistoryAsync(input.GID, waitDeleteHouseGids.Select(gid => new AFRHouse() { GID = gid }), "删除", 0, "删除了单据", tran); } if (waitUpdateHouseGids.Any()) { await DbAMS.Update() .SetSource(waitUpdateHouseList) - .IgnoreColumns(h => new { h.StateIsMatched, h.StateIsAccept, h.StateIsSend, h.NewNotice }) + .IgnoreColumns(h => new { h.StateIsMatched, h.StateIsAccept, h.StateIsSend, h.StateIsDelete, h.NewNotice }) .WithTransaction(tran) .ExecuteAffrowsAsync(); - await BuildAFRMasterHistoryAsync(input.GID, waitUpdateHouseGids, "修改", 0, "修改了单据", tran); + await SaveHistoryAsync(input.GID, waitUpdateHouseList, "修改", 0, "修改了单据", tran); } // 箱子执行 @@ -504,33 +551,7 @@ namespace djy.Service.AFR return await Get(input.GID); } - private async Task BuildAFRMasterHistoryAsync(string mid, IEnumerable hids, string state, int type, string remark, DbTransaction tran = null) - { - List list = new(hids.Count()); - foreach (string hid in hids) - { - var history = new AFRMasterHistory() - { - GID = Guid.NewGuid().ToString(), - CreateTime = DateTime.Now, - Operator = User.ShowName, - MID = mid, - HID = hid, - State = state, - Type = Convert.ToByte(type) - }; - history.Remark = $"{history.Operator}于{(DateTime)history.CreateTime:yyyy-MM-dd HH:mm:ss}{remark}"; - list.Add(history); - } - if (tran == null) - { - await DbAMS.Insert(list).ExecuteAffrowsAsync(); - } - else - { - await DbAMS.Insert(list).WithTransaction(tran).ExecuteAffrowsAsync(); - } - } + public async Task Delete(string ids) { @@ -563,9 +584,18 @@ namespace djy.Service.AFR } } - public async Task Send(string ids, string hids, int sendType) + /// + /// 发送 + /// + /// + /// + /// 1:原始发送 2:重发 3:修改 4:删除 + public async Task<(bool, string)> Send(string ids, string hids, int sendType) { - //return "发送成功"; + //return (true,"发送成功"); + // 是否全部成功 + bool isFullSuccess = true; + #region 查询电子口岸的请求参数 var paramSets = await DbBus.Get(DbList.djydb).Select().Where(x => x.PARAMNAME == "AFRURL" || x.PARAMNAME == "AFRAccount" || x.PARAMNAME == "AFRKey").ToListAsync(p => new { @@ -622,30 +652,17 @@ namespace djy.Service.AFR var housePids = houseAll.Select(h => h.PID); masterAll = await DbAMS.Select().Where(m => m.IsDel == false && housePids.Contains(m.GID)).ToListAsync(); } - //else if (sendType is 4) - //{ - // if (string.IsNullOrEmpty(hids)) - // { - // throw new ArgumentNullException(nameof(hids)); - // } - // var hidArr = hids.Split(','); - - // houseAll = await DbAMS.Select().Where(h => h.IsDel == false && hidArr.Contains(h.GID)).ToListAsync(); - // if (houseAll.Count != hidArr.Length) - // { - // throw new Exception("所选记录有些已被删除,请刷新页面后重试"); - // } - //} #endregion StringBuilder messageBuilder = new(); #region 构建请求参数 - // 原始发送 + // 原始发送 重发 修改 if (sendType is 1 or 2 or 3) { foreach (AFRMaster masterItem in masterAll) { + // 待处理的分单 List houseList = null; try @@ -654,29 +671,39 @@ namespace djy.Service.AFR // 判断是否有分单数据 houseList = houseAll.Where(h => h.PID == masterItem.GID).ToList() ?? throw new Exception("分单为空"); - // 验证接口要求 + // 验证数据是否符合接口要求 if (masterItem.FilingType == "Tranship" && (string.IsNullOrWhiteSpace(masterItem.LastForeignHarbourCode) || string.IsNullOrWhiteSpace(masterItem.LastForeignHarbour))) { throw new Exception("当【申报运输类型】为【Tranship】时,【交货地全称】与【交货地五字码】为必填项"); } + // 如果操作类型为“重发”,则待发送的记录必须都为“已删除”,因为宁波接口要求记录状态是“删除成功”的,才可以重发 + if (sendType is 2) + { + if (houseList.Any(h => h.StateIsDelete == false)) + { + string tip = string.Join("、", houseList.Where(h => h.StateIsDelete == false).Select(h => h.HouseBillNo).ToList()); + throw new Exception($"所选分单中存在未“删除发送”的记录,分单号:【{tip}】,请重新选择(只有“删除发送”成功的,才能进行“重发”操作)"); + } + } + // 如果操作类型为“修改”,则待发送记录不能有“已删除”的 + if (sendType is 3) + { + if (houseList.Any(h => h.StateIsDelete)) + { + string tip = string.Join("、", houseList.Where(h => h.StateIsDelete).Select(h => h.HouseBillNo).ToList()); + throw new Exception($"所选分单中存在已经“删除发送”的记录,分单号:【{tip}】,请重新选择(已“删除发送”的记录需要先进行“重发”,才能进行 “修改发送”或“删除发送” 操作)"); + } + } + // 记录状态是回执成功的,才可以修改(暂不校验) + // 验证账户余额是否充足 var fin = new FinanceService(); var getfinrs = fin.CheckBalance(new CustFee { - CARRIER = masterItem.ShipCompanyCode, - //ETD = null, - //VOYNO = masterItem.Voyno, - //VESSEL = masterItem.Vessel, - //HBLNO = item.HBLNo, - //SENDUSERID = user.GID, LURURENID = User.GID, - //CtnrCount = , - //CtnrInfo = string.Empty, BSTYPE = BusinessType.AFR_REPORT, SENDTYPE = sendType switch { 1 or 2 => 0, 3 => 1, _ => throw new NotImplementedException() }, - //BSNO = oid.ToString(), - //MBLNO = master.MBLNO.ToString(), }, houseList.Count); if (!getfinrs.Status) @@ -781,14 +808,14 @@ namespace djy.Service.AFR } // 开始请求电子口岸的接口 - var masterBillInfoStr = JsonConvert.SerializeObject(requestDto); + var masterBillInfoStr = JsonHelper.Instance.Serialize(requestDto); var businessParam = new Dictionary() { { "masterBillInfo", masterBillInfoStr } }; var param = BuildRequestParam(sendType, afrAccount, afrKey, businessParam); - var request = JsonConvert.SerializeObject(param); + var request = JsonHelper.Instance.Serialize(param); var logGuid = Guid.NewGuid().ToString(); logger.LogInformation($"请求宁波电子口岸API,{logGuid},入参:{request}"); @@ -826,7 +853,7 @@ namespace djy.Service.AFR HBLNO = dealHouseItem.HouseBillNo, SENDUSERID = User.GID, LURURENID = User.GID, - CtnrCount = dealHouseItem.CntrList?.Count ?? 0, + CtnrCount = 1, CtnrInfo = string.Empty, BSTYPE = BusinessType.AFR_REPORT, SENDTYPE = sendType switch { 1 or 2 => 0, 3 => 1, _ => -1 }, @@ -844,18 +871,31 @@ namespace djy.Service.AFR logger.LogError($"执行扣费时发生未知异常,{logGuid},异常信息:{ex.Message}"); } - // 保存状态 } - - // 记录历史 + #endregion + var pidList = houseList.Select(h => h.PID); var state = sendType switch { 1 => "新增发送成功", 2 => "重发成功", 3 => "修改发送成功", _ => "" }; - await BuildAFRMasterHistoryAsync(masterItem.GID, houseList.Select(h => h.GID), state, 0, "发送了单据"); - #endregion + // 保存状态 + var hidList = houseList.Select(h => h.GID); + await DbAMS.Update() + .Set(h => h.StateIsSend == true) + .Set(h => h.NewNotice == state) + .SetIf(sendType == 2, h => h.StateIsDelete == false) //如果操作为重发,则把"删除发送"状态重置为“未删除发送”,这样后续才能重新 修改、删除 + .Where(h => hidList.Contains(h.GID)) + .ExecuteAffrowsAsync(); + + await DbAMS.Update() + .Set(h => h.StateIsSend == true) // 把主单的状态改为“已发送”,这样主单才会在“已发送”页面显示 + .Where(h => pidList.Contains(h.GID)) + .ExecuteAffrowsAsync(); + + // 记录历史 + await SaveHistoryAsync(masterItem.GID, houseList, state, 0, "发送了单据"); } else { - throw new Exception($"电子口岸接口返回失败结果(code:{code2},msg:{msg})"); + throw new Exception($"电子口岸接口返回失败结果(code:{code2},msg:{msg2})"); } } else @@ -865,11 +905,17 @@ namespace djy.Service.AFR } catch (Exception ex) { + // 记录历史 var state = sendType switch { 1 => "新增发送失败", 2 => "重发失败", 3 => "修改发送失败", _ => "" }; - await BuildAFRMasterHistoryAsync(masterItem.GID, houseList.Select(h => h.GID), state, 0, $"发送单据失败,失败原因:{ex.Message}"); + await SaveHistoryAsync(masterItem.GID, houseList, state, 0, $"发送单据失败,失败原因:{ex.Message}"); // 保存状态 + var hidList = houseList.Select(h => h.GID); + await DbAMS.Update() + .Set(h => h.NewNotice == state) + .Where(h => hidList.Contains(h.GID)) + .ExecuteAffrowsAsync(); // 构建响应 string tip; @@ -883,6 +929,8 @@ namespace djy.Service.AFR } messageBuilder.Append(tip); + isFullSuccess = false; + // 继续处理下一单 continue; } @@ -894,6 +942,13 @@ namespace djy.Service.AFR try { #region 发送前的各项校验 + // 待发送记录不能有“已删除”的 + if (houseAll.Any(h => h.StateIsDelete)) + { + string tip = string.Join("、", houseAll.Where(h => h.StateIsDelete).Select(h => h.HouseBillNo).ToList()); + throw new Exception($"所选分单中存在已经“删除发送”的记录,分单号:【{tip}】,请重新选择(已“删除发送”的记录需要先进行“重发”,才能进行 “修改发送”或“删除发送” 操作)"); + } + // 验证账户余额是否充足 var fin = new FinanceService(); var getfinrs = fin.CheckBalance(new CustFee @@ -917,7 +972,7 @@ namespace djy.Service.AFR }; var param = BuildRequestParam(sendType, afrAccount, afrKey, businessParam); - var request = JsonConvert.SerializeObject(param); + var request = JsonHelper.Instance.Serialize(param); var logGuid = Guid.NewGuid().ToString(); logger.LogInformation($"请求宁波电子口岸API,{logGuid},入参:{request}"); @@ -976,17 +1031,23 @@ namespace djy.Service.AFR continue; } // 记录历史 - await BuildAFRMasterHistoryAsync(expendMasterItem.GID, new string[] { expendHouseItem.GID }, "删除发送成功", 0, "删除发送了单据"); - - // 保存状态 + await SaveHistoryAsync(expendMasterItem.GID, new AFRHouse[] { expendHouseItem }, "删除发送成功", 0, "删除发送了单据"); } - + // 保存状态 + var hidList = houseAll.Select(h => h.GID); + await DbAMS.Update() + .Set(h => h.NewNotice == "删除发送成功") + .Set(h => h.StateIsDelete == true) + .Set(h => h.StateIsAccept == false) //@铁幕 何工,如果有一票我这收到了回执推送,然后我 删除 并 重发 了这票,那后续我还会再收到完整的回执推送吗? ————会的 + .Set(h => h.StateIsMatched == false) //@铁幕 何工,如果有一票我这收到了回执推送,然后我 删除 并 重发 了这票,那后续我还会再收到完整的回执推送吗? ————会的 + .Where(h => hidList.Contains(h.GID)) + .ExecuteAffrowsAsync(); #endregion } else { - throw new Exception($"电子口岸接口返回失败结果(code:{code2},msg:{msg})"); + throw new Exception($"电子口岸接口返回失败结果(code:{code2},msg:{msg2})"); } } else @@ -1003,44 +1064,86 @@ namespace djy.Service.AFR // 记录历史 houseAll.ForEach(async h => { - await BuildAFRMasterHistoryAsync(h.PID, new string[] { h.GID }, "删除发送失败", 0, $"删除发送单据失败,原因:{ex.Message}"); + await SaveHistoryAsync(h.PID, new AFRHouse[] { h }, "删除发送失败", 0, $"删除发送单据失败,原因:{ex.Message}"); }); // 保存状态 + var hidList = houseAll.Select(h => h.GID); + await DbAMS.Update() + .Set(h => h.NewNotice == "删除发送失败") + .Where(h => hidList.Contains(h.GID)) + .ExecuteAffrowsAsync(); + + isFullSuccess = false; } } #endregion + return (isFullSuccess, messageBuilder.ToString()); + } - return messageBuilder.ToString(); - - //var method = sendType switch - //{ - // 1 => "eportyun.manifest.afr.sendBill", - // 2 => "eportyun.manifest.afr.resendBill", - // 3 => "eportyun.manifest.afr.modifyBill", - // 4 => "eportyun.manifest.afr.deleteBill", - // _ => throw new Exception() - //}; - - //// 原始重发 || 修改发送 - //else if (sendType == 2 || sendType == 3) - //{ - // foreach (string id in idArr) - // { - // var param = new - // { - // method = sendType switch - // { - // 2 => "eportyun.manifest.afr.sendBill", - // 3 => "eportyun.manifest.afr.modifyBill", - // _ => throw new Exception() - // }, - // masterBillInfo = "", - // }; - // } - //} + public async Task SaveReceipt(AFRReceiptDto input) + { + if (string.IsNullOrEmpty(input.businessId)) + { + logger.LogError("接收到回执中,businessId为空"); + return; + } + var house = await DbAMS.Select() + .Where(h => h.HouseBillNo == input.businessId && h.IsDel == false) + .FirstAsync(); + + if (house == null) + { + logger.LogError("接收到回执后,根据businessId未查到AFRHouse"); + return; + } + + var update = DbAMS.Update() + .Set(h => h.NewNotice == input.content); + + // Accept、Warning、Reject、Matched、Hold、Cancel + string statusDesc = string.Empty; + switch (input.status) + { + case "Accept": + update.Set(h => h.StateIsAccept == true); + statusDesc = "海关接收"; + break; + case "Matched": + update.Set(h => h.StateIsMatched == true); + statusDesc = "已匹配"; + break; + case "Warning": statusDesc = "警告"; break; + case "Reject": statusDesc = "海关拒绝"; break; + case "Hold": statusDesc = "海关监控"; break; + case "Cancel": statusDesc = "海关删除"; break; + default: break; + } + + // 更新AFRHouse的状态 + await update.Where(h => h.GID == house.GID) + .ExecuteAffrowsAsync(); + + // 记录历史 + await SaveHistoryAsync(house.PID, new AFRHouse[] { house }, statusDesc, 1, input.content); + + return; + } + + public async Task> GetHistory(string id, string hid) + { + var data = await DbAMS.Select() + .WhereIf(!string.IsNullOrEmpty(id), h => h.PID == id) + .WhereIf(!string.IsNullOrEmpty(hid), h => h.HID == hid) + .OrderByDescending(h => h.CreateTime) + .ToListAsync(); + return data; } + + + + private SortedDictionary BuildRequestParam(int sendType, string afrAccount, string afrKey, Dictionary businessParam) { string timestamp = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"); @@ -1078,18 +1181,49 @@ namespace djy.Service.AFR param.Add("sign", builder.ToString().ToMd5().ToUpper()); return param; } - public Task SaveReceipt(AFRReceiptDto input) - { - throw new NotImplementedException(); - } - - public async Task> GetHistory(string mid, string hid) + /// + /// 保存操作历史或回执接收历史 + /// + /// 主单主键 + /// 分单主键列表 + /// 状态 + /// 0:人工操作历史;1:回执接收历史 + /// + /// + /// + private async Task SaveHistoryAsync(string mid, IEnumerable houseList, string state, int type, string remark, DbTransaction tran = null) { - var data = await DbAMS.Select() - .WhereIf(!string.IsNullOrEmpty(mid), h => h.MID == mid) - .WhereIf(!string.IsNullOrEmpty(hid), h => h.HID == hid) - .ToListAsync(); - return data; + List list = new(houseList.Count()); + foreach (AFRHouse item in houseList) + { + var history = new AFRMasterHistory() + { + GID = Guid.NewGuid().ToString(), + CreateTime = DateTime.Now, + Operator = type == 0 ? User.ShowName : "系统管理员", + PID = mid, + HID = item.GID, + State = state, + Type = Convert.ToByte(type) + }; + if (type == 0) + { + history.Remark = $"{history.Operator} 于 {(DateTime)history.CreateTime:yyyy-MM-dd HH:mm:ss} {remark}"; + } + else if (type == 1) + { + history.Remark = $"您的单据 {item.HouseBillNo} 于 {(DateTime)history.CreateTime:yyyy-MM-dd HH:mm:ss} 接收到回执:{remark}"; + } + list.Add(history); + } + if (tran == null) + { + await DbAMS.Insert(list).ExecuteAffrowsAsync(); + } + else + { + await DbAMS.Insert(list).WithTransaction(tran).ExecuteAffrowsAsync(); + } } } } diff --git a/web/djy.Service/DjyService/AspNetUser.cs b/web/djy.Service/DjyService/AspNetUser.cs index 3977a6f..79ac9b8 100644 --- a/web/djy.Service/DjyService/AspNetUser.cs +++ b/web/djy.Service/DjyService/AspNetUser.cs @@ -2,7 +2,6 @@ using djy.IService.Djy; using djy.Model; using Microsoft.AspNetCore.Http; -using System; using System.Linq; namespace djy.Service.DjyService @@ -34,13 +33,13 @@ namespace djy.Service.DjyService accessor.HttpContext.Items["CurrentUser"] = userTemp.Data; return userTemp.Data; } - - throw new Exception("登录失效"); + return null; + //throw new Exception("未登录"); } } - public string CompId => CurrentUser.CompId; - public string CompName => CurrentUser.COMNAME; - public string GID => CurrentUser.GID; - public string ShowName => CurrentUser.SHOWNAME; + public string CompId => CurrentUser?.CompId; + public string CompName => CurrentUser?.COMNAME; + public string GID => CurrentUser?.GID; + public string ShowName => CurrentUser?.SHOWNAME; } } \ No newline at end of file diff --git a/web/djy_AfrApi/Controllers/AfrController.cs b/web/djy_AfrApi/Controllers/AfrController.cs index 7f5fd61..48c2e18 100644 --- a/web/djy_AfrApi/Controllers/AfrController.cs +++ b/web/djy_AfrApi/Controllers/AfrController.cs @@ -105,8 +105,15 @@ namespace djy_AfrApi.Controllers [HttpGet("Send")] public async Task Send(string ids, string hids, [Required] int sendType) { - string message = await _afrService.Send(ids, hids, sendType); - return SuccessResp(message); + var result = await _afrService.Send(ids, hids, sendType); + if (result.isSuccess) + { + return SuccessResp(result.message); + } + else + { + return FaildResp(result.message); + } } /// @@ -126,13 +133,13 @@ namespace djy_AfrApi.Controllers /// /// 获取历史记录 /// - /// 主单主键 + /// 主单主键 /// 分单主键 /// 历史记录列表 [HttpGet("GetHistory")] - public async Task>> GetHistory([Required] string mid, string hid) + public async Task>> GetHistory(string id, string hid) { - var data = await _afrService.GetHistory(mid, hid); + var data = await _afrService.GetHistory(id, hid); return SuccessResp(data); } diff --git a/web/djy_AfrApi/Controllers/ApiBase.cs b/web/djy_AfrApi/Controllers/ApiBase.cs index ee7764f..0a30055 100644 --- a/web/djy_AfrApi/Controllers/ApiBase.cs +++ b/web/djy_AfrApi/Controllers/ApiBase.cs @@ -55,6 +55,15 @@ namespace djy_AfrApi.Controllers Result = result }; } + [NonAction] + protected Response FaildResp(string message = "操作失败") + { + return new Response() + { + Code = 500, + Message = message, + }; + } #endregion diff --git a/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs b/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs index 503ff65..5e3a1b0 100644 --- a/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs +++ b/web/djy_AfrApi/Filter/GlobalExceptionsFilter.cs @@ -34,9 +34,9 @@ namespace djy_AfrApi.Filter res.Content = JsonConvert.SerializeObject(json); context.Result = res; - + //进行错误日志记录 - _logger.LogError(WriteLog("GlobalExceptionsFilter中捕获的全局异常", context.Exception)); + _logger.LogError(WriteLog($"GlobalExceptionsFilter中捕获的全局异常(TraceIdentifier:{context.HttpContext?.TraceIdentifier})", context.Exception)); } /// diff --git a/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs b/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs index 8bed011..3cc3708 100644 --- a/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs +++ b/web/djy_AfrApi/Milldlewares/ExceptionHandlerMiddleware.cs @@ -37,7 +37,7 @@ namespace djy_AfrApi.Middlewares { if (e == null) return; - logger.LogError(WriteLog("ExceptionHandlerMiddleware中捕获的全局异常", e)); + logger.LogError(WriteLog($"ExceptionHandlerMiddleware中捕获的全局异常(TraceIdentifier:{context.TraceIdentifier})", e)); await WriteExceptionAsync(context, e).ConfigureAwait(false); } diff --git a/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs b/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs index 17e12ac..ddb2f02 100644 --- a/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs +++ b/web/djy_AfrApi/Milldlewares/NextAuthorizationMiddleware.cs @@ -28,13 +28,6 @@ namespace djy_AfrApi.Milldlewares var endpoint = context.GetEndpoint(); if (endpoint?.Metadata.GetMetadata() == null && context.Request.Path.Value.ToLower().Contains("/api/afr")) { - // 因为ISF/AMS这步验证始终都无效,所以这里先不做验证 - //if (context.Request.Path.Value.Contains("/Load")) - //{ - // var userId = context.User?.Claims?.FirstOrDefault(c => c.Type == "loginid")?.Value; - // var aut = _userService.GetUserAuthority(Guid.Parse(userId), "modAfrList"); - //} - var userId = context.User?.Claims?.FirstOrDefault(c => c.Type == "loginid")?.Value; var user = _userService.GetUserInfo(userId); if (user.Data == null) diff --git a/web/djy_AfrApi/Properties/PublishProfiles/FolderProfile-Windows.pubxml.user b/web/djy_AfrApi/Properties/PublishProfiles/FolderProfile-Windows.pubxml.user index fa9222e..a1bbbcb 100644 --- a/web/djy_AfrApi/Properties/PublishProfiles/FolderProfile-Windows.pubxml.user +++ b/web/djy_AfrApi/Properties/PublishProfiles/FolderProfile-Windows.pubxml.user @@ -6,7 +6,7 @@ <_PublishTargetUrl>D:\DJY\Code\djyweb_ams\web\djy_AfrApi\bin\Release\net5.0\publish-windows\ - True|2024-01-03T13:54:40.3579096Z;True|2024-01-03T21:52:09.6604718+08:00;True|2024-01-03T16:04:13.6208067+08:00;True|2024-01-03T15:07:08.9376581+08:00;True|2024-01-02T10:57:59.7067270+08:00;True|2024-01-02T10:28:44.8223638+08:00;True|2023-12-29T17:26:12.9612280+08:00; + True|2024-01-05T03:59:09.7697688Z;True|2024-01-05T11:33:19.2093394+08:00;True|2024-01-05T11:27:31.2454199+08:00;True|2024-01-05T11:20:20.5464568+08:00;True|2024-01-04T18:36:38.8259124+08:00;True|2024-01-04T15:54:57.9348895+08:00;True|2024-01-04T15:44:34.6535493+08:00;False|2024-01-04T15:44:20.9673752+08:00;True|2024-01-04T11:14:33.4379160+08:00;True|2024-01-03T21:54:40.3579096+08:00;True|2024-01-03T21:52:09.6604718+08:00;True|2024-01-03T16:04:13.6208067+08:00;True|2024-01-03T15:07:08.9376581+08:00;True|2024-01-02T10:57:59.7067270+08:00;True|2024-01-02T10:28:44.8223638+08:00;True|2023-12-29T17:26:12.9612280+08:00; \ No newline at end of file diff --git a/web/djy_AfrApi/Startup.cs b/web/djy_AfrApi/Startup.cs index d2bbbb5..9149390 100644 --- a/web/djy_AfrApi/Startup.cs +++ b/web/djy_AfrApi/Startup.cs @@ -139,12 +139,12 @@ namespace djy_AfrApi app.UseRouting(); - //DefaultFilesOptions defaultFiles = new DefaultFilesOptions(); - //defaultFiles.DefaultFileNames.Clear(); - //defaultFiles.DefaultFileNames.Add("index.html"); - //app.UseDefaultFiles(defaultFiles); - - //app.UseStaticFiles(); + DefaultFilesOptions defaultFiles = new DefaultFilesOptions(); + defaultFiles.DefaultFileNames.Clear(); + defaultFiles.DefaultFileNames.Add("index.html"); + app.UseDefaultFiles(defaultFiles); + + app.UseStaticFiles(); // app.UseCors(builder => diff --git a/web/djy_AfrApi/appsettings.Development.json b/web/djy_AfrApi/appsettings.Development.json index 41d33cf..fc80e45 100644 --- a/web/djy_AfrApi/appsettings.Development.json +++ b/web/djy_AfrApi/appsettings.Development.json @@ -30,7 +30,7 @@ "Rbmq_Password": "admin", "Rbmq_Sqlhost": "Data Source =60.209.125.238; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=Djy@Sql2022.test;pooling=true", "DapperDbString": "Data Source =60.209.125.238,32009; Initial Catalog=DevAMS; Persist Security Info=True; User ID =sa; Password=Djy@Sql2022.test;pooling=true;", - "requesterDea": "HWCDDS", + "requesterDea": "SHBR", "DataConnList": [ { "SysKey": "AMS", diff --git a/web/djy_AfrApi/appsettings.Production.json b/web/djy_AfrApi/appsettings.Production.json index 5f9e01f..565515e 100644 --- a/web/djy_AfrApi/appsettings.Production.json +++ b/web/djy_AfrApi/appsettings.Production.json @@ -23,14 +23,14 @@ "ConnName": "sqldb", "cache_time": 10, "WebHostUrl": "https://zh-userapi.jingyiji.net", - "Redis": "172.31.85.169:6379,defaultDatabase=待配置,", - "RedisDb": "待配置", + "Redis": "172.31.85.169:6379,defaultDatabase=待配置待配置待配置待配置待配置待配置待配置待配置,", + "RedisDb": "待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置", "Rbmq_Host": "172.31.85.169:13866", "Rbmq_UserName": "ams", "Rbmq_Password": "djy_ams", "Rbmq_Sqlhost": "Data Source =172.31.85.154; Initial Catalog=djy_logs; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true", "DapperDbString": "Data Source =172.31.85.161; Initial Catalog=AMS; Persist Security Info=True; User ID =sa; Password=QDdjy#2020*;pooling=true;", - "requesterDea": "HWCDDS", + "requesterDea": "待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置待配置", "DataConnList": [ { "SysKey": "AMS", diff --git a/web/djy_AfrApi/appsettings.Staging.json b/web/djy_AfrApi/appsettings.Staging.json index b629a9a..a978378 100644 --- a/web/djy_AfrApi/appsettings.Staging.json +++ b/web/djy_AfrApi/appsettings.Staging.json @@ -23,7 +23,7 @@ "ConnName": "sqldb", "cache_time": 10, "WebHostUrl": "http://127.0.0.1:30818", - "Redis": "192.168.1.83:6379,password=,defaultDatabase=12", + "Redis": "127.0.0.1:6379,password=,defaultDatabase=12", "RedisDb": "12", "Rbmq_Host": "", "Rbmq_UserName": "admin",