diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/INVOICE_SERVICE.cs b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/INVOICE_SERVICE.cs index e51bf76a..aeb41ea9 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/INVOICE_SERVICE.cs +++ b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/INVOICE_SERVICE.cs @@ -222,6 +222,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ inv.Amt = Math.Round(itm.Qty * first.PRICE, 2); inv.Extend1 = itm.ContractDocID; inv.BussiessType = EnumBusinessType.MaiDanJianBBAC; + if (invoiceGrp.Site == "1046") { inv.Extend2 = "CC16"; @@ -252,6 +253,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ InvoiceNetAmount = invoiceGrpDetail.Amt, InvoiceTaxAmount = Math.Round(invoiceGrpDetail.Amt * 0.13m, 2), TaxRate = 0.13m, + Remark = string.IsNullOrEmpty(invoiceGrp.FileName) ? string.Empty : invoiceGrp.FileName, Location = invoiceGrpDetail.Extend2, begintime = invoiceGrpDetail.BeginDate, endtime = invoiceGrpDetail.EndDate, @@ -268,19 +270,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ var invdiff = invbefore - invafter; //invoiceGrp.RealAmt + invoiceGrp.TaxDiff-invafter; - - tedsaInvs1.FirstOrDefault().InvoiceTaxAmount += invdiff; - - - //var a = tedsaInvs1.Sum(p => p.InvoiceNetAmount); - //var b= tedsaInvs1.Sum(p => p.InvoiceTaxAmount); - //var invafter = a + b; - //var invdiff = invbefore - invafter; - //var line1 = tedsaInvs1.FirstOrDefault(p => p.LINE == "1"); - //var line2 = tedsaInvs1.FirstOrDefault(p => p.LINE == "2"); - //line2.InvoiceTaxAmount += invdiff; - //line1.InvoiceTaxAmount += invoiceGrp.TaxDiff; //红冲发票提交QAD if (!string.IsNullOrEmpty(invoiceGrp.ParentInvbillNum)) { @@ -292,17 +282,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ await _repository.DbContext.BulkUpdateAsync(new List() { invoiceGrp }); await BindInvoiceSyncQadAsync(teaTaskSub, invoiceGrp.RealnvBillNum, invoiceGrp.InvbillNum, invoiceGrp.ClientCode, invoiceGrp.CreationTime).ConfigureAwait(false); - //await _exChangeCenterDbContext.SaveChangesAsync().ConfigureAwait(false); - // var retryPolicyAsync = Policy.Handle().WaitAndRetryAsync(new[] { - // TimeSpan.FromSeconds(1), - // TimeSpan.FromSeconds(5), - // TimeSpan.FromSeconds(5), - // TimeSpan.FromSeconds(5) - //}, (exception, timeSpan, retryCount, context) => - //{ - // _logger.LogError($"提交到QAD,修改发票状态执行失败,第 {retryCount} 次重试"); - //}); - // await retryPolicyAsync.ExecuteAsync(async () => await _settleAccountDbContext.SaveChangesAsync().ConfigureAwait(false)).ConfigureAwait(false); + } } else @@ -311,6 +291,16 @@ namespace Win.Sfs.SettleAccount.Entities.BQ var invoiceGrpDetails = _settleAccountDbContext.Set() .Where(t => t.InvbillNum == invbillNum) .ToList(); + + + + + + + + + + if (invoiceGrpDetails.Any()) { var tedSaInvs = new List(); @@ -387,8 +377,10 @@ namespace Win.Sfs.SettleAccount.Entities.BQ var invafter = tedSaInvs.Sum(p => p.InvoiceTaxAmount); var invdiff = invbefore - invafter; + tedSaInvs.FirstOrDefault().InvoiceTaxAmount += invdiff; + //tedSaInvs.FirstOrDefault().InvoiceTaxAmount += invoiceGrp.TaxDiff; //红冲发票提交QAD if (!string.IsNullOrEmpty(invoiceGrp.ParentInvbillNum)) @@ -499,6 +491,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ Location = invoiceGrpDetail.GetProperty("ErpToLoc", string.Empty), begintime = invoiceGrpDetail.BeginDate, endtime = invoiceGrpDetail.EndDate, + Remark = string.IsNullOrEmpty(invoiceGrp.FileName) ? string.Empty : invoiceGrp.FileName, domain = "BJBMPT", LINE = (i + 1).ToString() }); diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs index bb126bed..0717ef53 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs +++ b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs @@ -68,13 +68,19 @@ namespace Win.Sfs.SettleAccount.Entities.BQ } public static List ServiceTypes { get; private set; } + + // Jobs字段使用ConcurrentDictionary来存储JobItem、Tuple类型的数据 public ConcurrentDictionary> Jobs { get; } = new(); + // 用于添加服务到IServiceCollection中 public static void AddService(IServiceCollection services) { + // 获取所有实现了IJobService接口的类,并将其添加到ServiceTypes列表中 ServiceTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes()) .Where(o => o.IsClass && !o.IsAbstract && o.IsAssignableTo(typeof(IJobService))) .ToList(); + + // 遍历ServiceTypes列表中的类型,并分别向services中注册IJobService接口和实现类 ServiceTypes.ForEach(o => { services.AddTransient(typeof(IJobService), o); @@ -82,28 +88,37 @@ namespace Win.Sfs.SettleAccount.Entities.BQ }); } + public void AddJob(JobItem job) { + // 使用锁确保线程安全 lock (_lockObj) { + // 检查是否存在相同Id的作业 if (!Jobs.Keys.Any(o => o.Id == job.Id)) { + // 尝试创建新作业的线程 try { + // 创建取消标记和线程 var source = new CancellationTokenSource(); var token = source.Token; var thread = new Thread(async () => { try { + // 解析Cron表达式 var expression = CronExpression.Parse(job.Cron, job.Cron.Split(' ').Length == 5 ? CronFormat.Standard : CronFormat.IncludeSeconds); + // 获取作业服务类型 var serviceType = ServiceTypes.FirstOrDefault(o => o.FullName == job.Service); if (serviceType != null) { + // 循环执行作业直到取消标记被设置 while (!_stoppingToken.IsCancellationRequested && !token.IsCancellationRequested) { try { + // 计算下一次执行时间间隔 var now = DateTime.UtcNow; var nextUtc = expression.GetNextOccurrence(now); var span = nextUtc - now; @@ -117,6 +132,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ await Task.Delay(currentDelay).ConfigureAwait(false); } Debug.WriteLine($"{job.Name} 定时任务开始执行"); + // 创建作用域并获取作业服务实例 using var scope = this._serviceProvider.CreateScope(); if (scope.ServiceProvider.GetService(serviceType) is IJobService jobService) { @@ -124,6 +140,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ { var jobItem = this.GetJobItem(job.Id); Guid? jobLogId = null; + // 如果作业正在运行并且心跳超过20秒,则停止作业 if (jobItem.IsRunning && (DateTime.Now - jobItem.HeartBeat.Value).TotalSeconds > 20) { JobItemStop(jobItem.Id); @@ -131,13 +148,16 @@ namespace Win.Sfs.SettleAccount.Entities.BQ if (!jobItem.IsRunning) { jobLogId = this.JobItemStart(job.Id); + // 创建定时器以发送心跳 using var timer = new System.Timers.Timer(10000); if (jobLogId.HasValue) { try { + // 执行作业服务方法 timer.Elapsed += (s, e) => JobItemHeartBeat(job.Id); timer.Start(); + // 通知客户端作业状态变化 scope.ServiceProvider.GetRequiredService>().Clients.All.ServerToClient("JobItem", "refresh", ""); await jobService.Invoke(scope.ServiceProvider).ConfigureAwait(false); this.JobItemSuccess(job.Id, jobLogId.Value); @@ -152,6 +172,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ finally { timer.Stop(); + // 通知客户端作业状态变化 scope.ServiceProvider.GetRequiredService>().Clients.All.ServerToClient("JobItem", "refresh", ""); } } @@ -179,6 +200,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ { IsBackground = true }; + // 尝试将作业添加到字典中并启动线程 if (this.Jobs.TryAdd(job, new Tuple(source, thread))) { thread.Start(); @@ -193,17 +215,26 @@ namespace Win.Sfs.SettleAccount.Entities.BQ } } + private void JobItemFaild(Guid id, Guid jobLogId, Exception ex) { + // 获取连接字符串 var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); + // 使用连接字符串创建数据库连接 using var connection = new SqlConnection(connectionString); + // 配置数据库上下文选项 var options = new DbContextOptionsBuilder().UseSqlServer(connection).Options; + // 使用数据库上下文实例化数据库上下文 using var db = new SettleAccountDbContext(options); + // 获取指定 id 对应的 JobItem 实体 var entity = db.Set().FirstOrDefault(o => o.Id == id); if (entity != null) { + // 更新实体的 IsRunning 属性,并保存到数据库 entity.IsRunning = false; + // 获取指定 jobLogId 对应的 JobLog 实体 var log = db.Set().FirstOrDefault(o => o.Id == jobLogId); + // 更新 JobLog 实体的 End、Success 和 Exception 属性,并保存到数据库 log.End = DateTime.Now; log.Success = false; log.Exception = ex.ToString(); @@ -211,61 +242,103 @@ namespace Win.Sfs.SettleAccount.Entities.BQ } } + + // 在指定的作业项成功时更新数据库 private void JobItemSuccess(Guid id, Guid jobLogId) { var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); using var connection = new SqlConnection(connectionString); var options = new DbContextOptionsBuilder().UseSqlServer(connection).Options; using var db = new SettleAccountDbContext(options); + + // 通过id查找作业项实体 var entity = db.Set().FirstOrDefault(o => o.Id == id); if (entity != null) { + // 设置作业项为非运行状态 entity.IsRunning = false; + + // 通过jobLogId查找作业日志实体 var log = db.Set().FirstOrDefault(o => o.Id == jobLogId); if (log != null) { + // 更新作业日志的结束时间和成功状态 log.End = DateTime.Now; log.Success = true; } + // 保存更改到数据库 db.SaveChanges(); } } + + // 停止作业项的方法 private void JobItemStop(Guid id) { + // 获取数据库连接字符串 var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); + + // 使用数据库连接 using var connection = new SqlConnection(connectionString); + + // 配置数据库上下文选项 var options = new DbContextOptionsBuilder().UseSqlServer(connection).Options; + + // 使用结算账户数据库上下文 using var db = new SettleAccountDbContext(options); + + // 从数据库中获取指定id的作业项实体 var entity = db.Set().FirstOrDefault(o => o.Id == id); if (entity != null) { + // 将作业项的运行状态设置为false entity.IsRunning = false; + + // 获取指定作业id且结束时间为空的作业日志,按开始时间降序排序 var log = db.Set().Where(o => o.JobId == id && o.End == null).OrderByDescending(o => o.Start).FirstOrDefault(); + + // 设置作业日志的执行状态为失败,并记录异常描述 log.Success = false; log.Exception = "心跳超时,自动停止"; + + // 保存数据库更改 db.SaveChanges(); } } + + // 通过作业项的ID启动作业项 private Guid? JobItemStart(Guid id) { + // 获取数据库连接字符串 var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); + // 使用连接字符串创建SqlConnection对象 using var connection = new SqlConnection(connectionString); + // 使用连接字符串创建SettleAccountDbContext的DbContextOptions var options = new DbContextOptionsBuilder().UseSqlServer(connection).Options; + // 使用DbContextOptions创建SettleAccountDbContext using var db = new SettleAccountDbContext(options); + // 从数据库中查询指定ID的作业项实体 var entity = db.Set().FirstOrDefault(o => o.Id == id); + // 如果作业项实体存在 if (entity != null) { + // 设置作业项为运行状态,并更新心跳时间为当前时间 entity.IsRunning = true; entity.HeartBeat = DateTime.Now; + // 添加作业日志 var log = db.Set().Add(new JobLog { Start = DateTime.Now, JobId = entity.Id, Host = Dns.GetHostName() }); + // 保存作业日志到数据库 db.SaveChanges(); + // 返回新增作业日志的ID return log.Entity.Id; } + // 作业项不存在时返回空 return null; } + + // 从数据库中获取指定id的JobItem对象 private JobItem GetJobItem(Guid id) { var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); @@ -276,6 +349,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ return repo.FirstOrDefault(o => o.Id == id); } + // 更新指定id的JobItem对象的心跳时间为当前时间 private void JobItemHeartBeat(Guid id) { var connectionString = this._serviceProvider.GetRequiredService().GetConnectionString("SettleAccountService"); @@ -290,6 +364,7 @@ namespace Win.Sfs.SettleAccount.Entities.BQ } } + public void RemoveJob(JobItem item) { lock (_lockObj) diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/Syncs/SeSyncExtendManager.cs b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/Syncs/SeSyncExtendManager.cs index e758c47e..08635d13 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/Syncs/SeSyncExtendManager.cs +++ b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/Syncs/SeSyncExtendManager.cs @@ -182,7 +182,6 @@ namespace Win.Sfs.SettleAccount.Entities.BQ.Syncs SettlementPartCode = (!string.IsNullOrEmpty(t.PN) ? t.PN : string.Empty) + (!string.IsNullOrEmpty(t.CustomerPartCodeNoSpace) ? t.CustomerPartCodeNoSpace : string.Empty) , - }).ToList(); var returnVmiLogList = returnSeDetails.Select(t => new VmiLog { diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs index b72dea8d..20a05d9c 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs +++ b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs @@ -239,12 +239,17 @@ namespace Win.Sfs.SettleAccount.Entities.BQ [HttpPost] public async Task> Balance(RequestDto input) { + + if (input != null && !string.IsNullOrEmpty(input.Sorting)) + { + input.Sorting= input.Sorting.ToUpper().Replace("BillTime".ToUpper(), "ReceiveTime".ToUpper()) + .Replace("Qty".ToUpper(), "SumQty".ToUpper()); + } var first = input.Filters.FirstOrDefault(p => p.Column == "BillTime"); if (first != null) { input.Filters.Remove(first); } - var entities = await _vmiBalanceSumDetailRepository.GetListByFilterAsync(input.Filters, input.Sorting, input.MaxResultCount, input.SkipCount, true).ConfigureAwait(false); var totalCount = await _vmiBalanceSumDetailRepository.GetCountByFilterAsync(input.Filters).ConfigureAwait(false); var dtos = _maper.Map, List>(entities); @@ -252,6 +257,13 @@ namespace Win.Sfs.SettleAccount.Entities.BQ return new PagedResultDto(totalCount, dtos); } + + + + + + + /// /// 1.1库存余额导出 /// diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContext.cs b/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContext.cs index 0488071b..8a07943f 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContext.cs +++ b/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContext.cs @@ -14,7 +14,7 @@ namespace Win.Sfs.SettleAccount public SettleAccountDbContext(DbContextOptions options) : base(options) { - this.Database.SetCommandTimeout(System.TimeSpan.FromMinutes(30)); + this.Database.SetCommandTimeout(System.TimeSpan.FromMinutes(120)); } protected override void OnModelCreating(ModelBuilder builder)