学 赵 1 year ago
parent
commit
e8a92cf272
  1. 11
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj
  2. 66
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/Startup.cs
  3. 3
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/appsettings.json
  4. 11
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/start.cmd
  5. 3
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd
  6. 18
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js
  7. 4
      code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/views/base-data/job-item.js
  8. 204
      code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs
  9. 140
      code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobItemAppService.cs
  10. 2
      code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs
  11. 3
      code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContextModelCreatingExtensions.cs

11
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj

@ -1,5 +1,4 @@

<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\common.props" />
@ -87,8 +86,12 @@
</ItemGroup>
<ItemGroup>
<Folder Include="wwwroot\" />
<Folder Include="wwwroot\files\vmi\" />
<None Update="start.cmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
<None Update="stop.cmd">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
<ProjectExtensions><VisualStudio><UserProperties appsettings_1json__JsonSchema="" /></VisualStudio></ProjectExtensions>

66
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/Startup.cs

@ -1,8 +1,4 @@
using System;
using System.Linq;
using System.Text.RegularExpressions;
using Coravel;
using Coravel.Invocable;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc.ApplicationModels;
@ -11,10 +7,9 @@ using Microsoft.AspNetCore.Server.Kestrel.Core;
using Microsoft.AspNetCore.StaticFiles;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using NUglify.Helpers;
using SettleAccount.Job.SignalR;
using Win.Sfs.SettleAccount.Entities.BQ;
using Win.Sfs.SettleAccount.Entities.BQ.Syncs;
using Win.Sfs.SettleAccount.Entities.BQ.Vmi;
namespace Win.Sfs.SettleAccount;
@ -23,14 +18,12 @@ public class Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddSignalR(o => o.EnableDetailedErrors = true);
AppDomain.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(o => o.IsClass && !o.IsAbstract && o.IsAssignableTo(typeof(IJobService)))
.ForEach(o => services.AddTransient(o));
services.AddScheduler();
JobHostdService.AddService(services);
services.AddSingleton<JobHostdService>();
services.AddHostedService(o => o.GetRequiredService<JobHostdService>());
services.AddTransient<HBPOSeSyncAppService>();
services.AddTransient<BBACSeSyncAppService>();
services.AddTransient<ZhiGongBBACSeSyncAppService>();
services.AddRouting(options => options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer));
services.AddMvc(options => options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer())));
services.AddApplication<SettleAccountHttpApiHostModule>();
@ -51,57 +44,6 @@ public class Startup
{
app.UseRouting();
app.UseEndpoints(endpoints => endpoints.MapHub<PageHub>("/api/hub"));
app.ApplicationServices.UseScheduler(scheduler =>
{
//scheduler.Schedule<HBPOSeSyncAppService>().EveryMinute();
scheduler.Schedule<ZhiGongBBACSeSyncAppService>().EveryMinute();
using var scope = app.ApplicationServices.CreateScope();
var jobs = scope.ServiceProvider.GetService<SettleAccountDbContext>().Set<JobItem>().ToList();
jobs?.ForEach(job =>
{
var jobId = job.Id;
using var scope = app.ApplicationServices.CreateScope();
var serviceType = AppDomain.CurrentDomain.GetAssemblies().SelectMany(a => a.GetTypes()).FirstOrDefault(o => o.FullName == job.Service);
if (serviceType != null)
{
if (scope.ServiceProvider.GetService(serviceType) is IJobService jobService)
{
scheduler.Schedule(() =>
{
using var scope = app.ApplicationServices.CreateScope();
var db = scope.ServiceProvider.GetService<SettleAccountDbContext>();
var jobItemRepository = db.Set<JobItem>();
var jobLogRepository = db.Set<JobLog>();
var jobItem = jobItemRepository.FirstOrDefault(o => o.Id == jobId);
if (!jobItem.IsDisabled)
{
jobItem.IsRunning = true;
db.SaveChanges();
var jobLog = new JobLog { JobId = jobId, Start = DateTime.Now };
try
{
jobService.Invoke();
jobLog.Success = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
jobLog.Exception = ex.ToString();
}
finally
{
jobLog.End = DateTime.Now;
jobLogRepository.Add(jobLog);
jobItem.IsRunning = false;
db.SaveChanges();
}
}
}).Cron(job.Cron);
}
}
});
});
var contentTypeProvider = new FileExtensionContentTypeProvider();
contentTypeProvider.Mappings.Add(".mjs", "text/javascript");
app.UseStaticFiles(new StaticFileOptions { ContentTypeProvider = contentTypeProvider });

3
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/appsettings.json

@ -69,8 +69,7 @@
"AuthServer": {
"Authority": "http://dev.ccwin-in.com:10580",
//"Authority": "http://localhost:10130",
"Authority": "http://*:10582",
"ClientId": "basic-web",
"ClientSecret": "1q2w3e*"
},

11
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/start.cmd

@ -0,0 +1,11 @@
@echo off
tasklist|find /i "SettleAccount.HttpApi.Host.exe"
if %errorlevel% == 0 (
  exit
)
%1 start mshta vbscript:createobject("wscript.shell").run("""%~0"" ::",0)(window.close)&&exit
start /b SettleAccount.HttpApi.Host.exe --urls http://dev.ccwin-in.com:10582

3
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd

@ -0,0 +1,3 @@
@ECHO OFF
taskkill /im SettleAccount.HttpApi.Host.exe /f /t

18
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js

@ -197,7 +197,7 @@ export default {
<el-scrollbar>
<app-list
v-if="subDrawer"
:query="subListQuery.query"
:query="subListQuery"
:buttons="subListQuery.buttons"
:config="subListQuery.config"
/>
@ -383,14 +383,13 @@ export default {
const columns = ref([]);
const filterDrawer = ref(false);
const subDrawer = ref(false);
const subListQuery = ref({});
const subListQuery = ref(props.query ?? {});
const tableLoading = ref(false);
const selectedRows = ref([]);
const dialogVisible = ref(false);
const route = useRoute();
const router = useRouter();
const { t } = useI18n();
const appStore = useAppStore();
// 注释一下代码暂停权限验证
// const buttons = ref(props.buttons ?? route.meta.children.filter((o) => o.meta.hasPermission));
// 添加下行代码暂停权限验证
@ -704,14 +703,11 @@ export default {
const showList = (value, nav, config) => {
if (!subDrawer.value) {
const targetRoute = router.getRoutes().find((o) => o.meta?.path === nav);
// subListQuery.value = {
// query: value,
// buttons: targetRoute.meta.children,
// config,
// };
subListQuery.value.query = value;
subListQuery.value.buttons = targetRoute.meta.children;
subListQuery.value.config = config;
subListQuery.value = {
query: value,
buttons: targetRoute.meta.children,
config,
};
subDrawer.value = true;
}
};

4
code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/views/base-data/job-item.js

@ -14,8 +14,8 @@ export default {
console.log(item.path, item, rows);
console.log(showList);
const config = useConfig2();
// config.query.schema.properties.filters.default[0].value = rows[0].id;
showList({ jobId: rows[0].id }, "/base-data/job-log", config);
config.query.schema.properties.filters.default[0].value = rows[0].id;
showList({ test: "test" }, "/base-data/job-log", config);
};
return { config, onCommand };
},

204
code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs

@ -0,0 +1,204 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Cronos;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Win.Sfs.SettleAccount.Entities.BQ.Vmi;
namespace Win.Sfs.SettleAccount.Entities.BQ;
public class JobHostdService : BackgroundService
{
private readonly object _lockObj = new object();
private readonly IServiceProvider _serviceProvider;
private CancellationToken _stoppingToken;
public JobHostdService(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public static List<Type> ServiceTypes { get; private set; }
public ConcurrentDictionary<JobItem, Tuple<CancellationTokenSource, Thread>> Jobs { get; } = new();
public static void AddService(IServiceCollection services)
{
ServiceTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(o => o.IsClass && !o.IsAbstract && o.IsAssignableTo(typeof(IJobService)))
.ToList();
ServiceTypes.ForEach(o =>
{
services.AddTransient(typeof(IJobService), o);
services.AddTransient(o);
});
}
public void AddJob(JobItem job)
{
lock (_lockObj)
{
if (!Jobs.Keys.Any(o => o.Id == job.Id))
{
try
{
var source = new CancellationTokenSource();
var token = source.Token;
var thread = new Thread(async () =>
{
try
{
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;
await Task.Delay(span.Value).ConfigureAwait(false);
Debug.WriteLine($"{job.Name} 定时任务开始执行");
using var scope = this._serviceProvider.CreateScope();
if (scope.ServiceProvider.GetService(serviceType) is IJobService jobService)
{
if (!job.IsDisabled)
{
var jobItem = this.GetJobItem(job.Id);
if (!jobItem.IsRunning)
{
this.JobItemStart(job.Id);
}
try
{
jobService.Invoke();
this.JobItemSuccess(job.Id);
Debug.WriteLine($"{job.Name} 定时任务执行成功");
}
catch (Exception ex)
{
Debug.WriteLine($"{job.Name} 定时任务执行失败:{ex.Message}");
Console.WriteLine(ex.ToString());
this.JobItemFaild(job.Id, ex);
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
});
thread.IsBackground = true;
if (this.Jobs.TryAdd(job, new Tuple<CancellationTokenSource, Thread>(source, thread)))
{
thread.Start();
Debug.WriteLine($"{job.Name} 定时任务线程启动");
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
private void JobItemFaild(Guid id, Exception ex)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var entity = db.Set<JobItem>().FirstOrDefault(o => o.Id == id);
if (entity != null)
{
entity.IsRunning = false;
var log = db.Set<JobLog>().FirstOrDefault(o => o.JobId == id);
log.End = DateTime.Now;
log.Success = false;
log.Exception = ex.ToString();
db.SaveChanges();
}
}
private void JobItemSuccess(Guid id)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var entity = db.Set<JobItem>().FirstOrDefault(o => o.Id == id);
if (entity != null)
{
entity.IsRunning = false;
var log = db.Set<JobLog>().FirstOrDefault(o => o.JobId == id);
log.End = DateTime.Now;
log.Success = true;
db.SaveChanges();
}
}
private void JobItemStart(Guid id)
{
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var entity = db.Set<JobItem>().FirstOrDefault(o => o.Id == id);
if (entity != null)
{
entity.IsRunning = true;
var log = db.Set<JobLog>().Add(new JobLog { Start = DateTime.Now, JobId = entity.Id });
db.SaveChanges();
}
}
private JobItem GetJobItem(Guid id)
{
using var scope = this._serviceProvider.CreateScope();
return scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>().Set<JobItem>().AsNoTracking().FirstOrDefault(o => o.Id == id);
}
public void RemoveJob(JobItem item)
{
lock (_lockObj)
{
var key = this.Jobs.Keys.FirstOrDefault(o => o.Id == item.Id);
if (key != null)
{
try
{
if (this.Jobs.TryRemove(key, out var value))
{
value.Item1.Cancel();
value.Item2.Interrupt();
value.Item2.Join();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
this._stoppingToken = stoppingToken;
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var jobs = db.Set<JobItem>().AsNoTracking().ToList();
jobs.ForEach(this.AddJob);
return Task.CompletedTask;
}
}

140
code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobItemAppService.cs

@ -1,6 +1,7 @@
using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Dynamic.Core;
using System.Threading;
@ -20,145 +21,6 @@ using Win.Sfs.Shared.RepositoryBase;
namespace Win.Sfs.SettleAccount.Entities.BQ;
public class JobHostdService : BackgroundService
{
private readonly object _lockObj = new object();
private readonly IServiceProvider _serviceProvider;
private CancellationToken _stoppingToken;
public JobHostdService(IServiceProvider serviceProvider)
{
this._serviceProvider = serviceProvider;
}
public ConcurrentDictionary<JobItem, Tuple<CancellationTokenSource, Thread>> Jobs { get; } = new();
public static List<Type> ServiceTypes { get; private set; }
public static void AddService(IServiceCollection services)
{
ServiceTypes = AppDomain.CurrentDomain.GetAssemblies().SelectMany(o => o.GetTypes())
.Where(o => o.IsClass && !o.IsAbstract && o.IsAssignableTo(typeof(IJobService)))
.ToList();
ServiceTypes.ForEach(o =>
{
services.AddTransient(typeof(IJobService), o);
services.AddTransient(o);
});
}
public void AddJob(JobItem job)
{
lock (_lockObj)
{
if (!Jobs.Keys.Any(o => o.Id == job.Id))
{
try
{
var source = new CancellationTokenSource();
var token = source.Token;
var thread = new Thread(async () =>
{
var expression = CronExpression.Parse(job.Cron);
using var scope1 = this._serviceProvider.CreateScope();
var serviceType = ServiceTypes.FirstOrDefault(o => o.FullName == job.Service);
if (serviceType != null)
{
while (!_stoppingToken.IsCancellationRequested && !token.IsCancellationRequested)
{
try
{
var nextUtc = expression.GetNextOccurrence(DateTime.UtcNow);
var span = nextUtc - DateTime.UtcNow;
await Task.Delay(span.Value).ConfigureAwait(false);
using var scope = this._serviceProvider.CreateScope();
if (scope.ServiceProvider.GetService(serviceType) is IJobService jobService)
{
var db = scope.ServiceProvider.GetService<SettleAccountDbContext>();
var jobItemRepository = db.Set<JobItem>();
var jobLogRepository = db.Set<JobLog>();
var jobItem = jobItemRepository.FirstOrDefault(o => o.Id == job.Id);
if (!jobItem.IsDisabled)
{
jobItem.IsRunning = true;
db.SaveChanges();
var jobLog = new JobLog { JobId = job.Id, Start = DateTime.Now };
try
{
jobService.Invoke();
jobLog.Success = true;
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
jobLog.Exception = ex.ToString();
}
finally
{
jobLog.End = DateTime.Now;
jobLogRepository.Add(jobLog);
jobItem.IsRunning = false;
db.SaveChanges();
}
}
}
}
catch (Exception)
{
throw;
}
}
}
});
thread.IsBackground = true;
if (this.Jobs.TryAdd(job, new Tuple<CancellationTokenSource, Thread>(source, thread)))
{
thread.Start();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
public void RemoveJob(JobItem item)
{
lock (_lockObj)
{
var key = this.Jobs.Keys.FirstOrDefault(o => o.Id == item.Id);
if (key != null)
{
try
{
if (this.Jobs.TryRemove(key, out var value))
{
value.Item1.Cancel();
value.Item2.Interrupt();
value.Item2.Join();
}
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
}
}
}
protected override Task ExecuteAsync(CancellationToken stoppingToken)
{
this._stoppingToken = stoppingToken;
using var scope = this._serviceProvider.CreateScope();
var db = scope.ServiceProvider.GetRequiredService<SettleAccountDbContext>();
var jobs = db.Set<JobItem>().AsNoTracking().ToList();
jobs.ForEach(this.AddJob);
return Task.CompletedTask;
}
}
[Route("api/settleaccount/[controller]/[action]")]
public class JobItemAppService : ApplicationService, ITransientDependency

2
code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/VmiAppService.cs

@ -75,7 +75,7 @@ public class VmiAppService : ApplicationService, IVmiService, IJobService, ITran
using var dbContext = new VmiSqliteContext(connectionString);
if (!dbContext.GetService<IRelationalDatabaseCreator>().Exists() && dbContext.Database.EnsureCreated())
{
var list = this._balanceRepository.AsNoTracking().ToList();
var list = this._balanceRepository.ToList();
foreach (var item in list)
{
dbContext.Set<VmiBalance>().Add(item);

3
code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContextModelCreatingExtensions.cs

@ -60,6 +60,7 @@ namespace Win.Sfs.SettleAccount
builder.ConfigureTB_RePartsRelationship(options);
//同步位置标记
builder.ConfigureSyncPositionFlag(options);
#endregion 基础数据
#region 北汽结算
@ -1281,7 +1282,7 @@ namespace Win.Sfs.SettleAccount
});
//seed
builder.Entity<JobItem>().HasData(new JobItem("vmi".ToGuid()) { Name = "库存快照", Cron = "0 0 8 26 *", Service = "Win.Sfs.SettleAccount.Entities.BQ.VmiService" });
builder.Entity<JobItem>().HasData(new JobItem("vmi".ToGuid()) { Name = "库存快照", Cron = "0 0 8 26 *", Service = "Win.Sfs.SettleAccount.Entities.BQ.VmiAppService" });
builder.Entity<VmiCategory>().HasData(new VmiCategory("发运入库".ToGuid()) { Type = VmiType.In, Name = "发运入库", Number = "100" });
builder.Entity<VmiCategory>().HasData(new VmiCategory("结算出库".ToGuid()) { Type = VmiType.Out, Name = "结算出库", Number = "200" });
builder.Entity<VmiCategory>().HasData(new VmiCategory("客户退货".ToGuid()) { Type = VmiType.Out, Name = "客户退货", Number = "300" });

Loading…
Cancel
Save