From 7944312ae179b47e94a61fbfc8d1a9142442dd04 Mon Sep 17 00:00:00 2001 From: wanggang <76527413@qq.com> Date: Thu, 27 Jul 2023 17:46:31 +0800 Subject: [PATCH] update --- .../SettleAccount.HttpApi.Host.csproj | 11 +- .../SettleAccount.HttpApi.Host/Startup.cs | 66 +----- .../appsettings.json | 3 +- .../host/SettleAccount.HttpApi.Host/start.cmd | 11 + .../host/SettleAccount.HttpApi.Host/stop.cmd | 3 + .../wwwroot/components/list/index.js | 18 +- .../wwwroot/views/base-data/job-item.js | 4 +- .../Entities/BQ/JobHostdService.cs | 204 ++++++++++++++++++ .../Entities/BQ/JobItemAppService.cs | 140 +----------- .../Entities/BQ/VmiAppService.cs | 2 +- ...AccountDbContextModelCreatingExtensions.cs | 5 +- 11 files changed, 244 insertions(+), 223 deletions(-) create mode 100644 code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/start.cmd create mode 100644 code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd create mode 100644 code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobHostdService.cs diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj index e285db5f..d1964be3 100644 --- a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj +++ b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/SettleAccount.HttpApi.Host.csproj @@ -1,5 +1,4 @@ - - + @@ -87,8 +86,12 @@ - - + + PreserveNewest + + + PreserveNewest + diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/Startup.cs b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/Startup.cs index c1859db9..5168422c 100644 --- a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/Startup.cs +++ b/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(); + services.AddHostedService(o => o.GetRequiredService()); services.AddTransient(); services.AddTransient(); services.AddTransient(); - services.AddRouting(options => options.ConstraintMap["slugify"] = typeof(SlugifyParameterTransformer)); services.AddMvc(options => options.Conventions.Add(new RouteTokenTransformerConvention(new SlugifyParameterTransformer()))); services.AddApplication(); @@ -51,57 +44,6 @@ public class Startup { app.UseRouting(); app.UseEndpoints(endpoints => endpoints.MapHub("/api/hub")); - app.ApplicationServices.UseScheduler(scheduler => - { - //scheduler.Schedule().EveryMinute(); - scheduler.Schedule().EveryMinute(); - - using var scope = app.ApplicationServices.CreateScope(); - var jobs = scope.ServiceProvider.GetService().Set().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(); - var jobItemRepository = db.Set(); - var jobLogRepository = db.Set(); - 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 }); diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/appsettings.json b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/appsettings.json index 5f575aad..ad037e61 100644 --- a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/appsettings.json +++ b/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*" }, diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/start.cmd b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/start.cmd new file mode 100644 index 00000000..90a86864 --- /dev/null +++ b/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 diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd new file mode 100644 index 00000000..d147d6c6 --- /dev/null +++ b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/stop.cmd @@ -0,0 +1,3 @@ +@ECHO OFF + +taskkill /im SettleAccount.HttpApi.Host.exe /f /t \ No newline at end of file diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js index 3a842eab..5b5d1d57 100644 --- a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js +++ b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/components/list/index.js @@ -197,7 +197,7 @@ export default { @@ -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; } }; diff --git a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/views/base-data/job-item.js b/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/views/base-data/job-item.js index 022c98a4..bff1c136 100644 --- a/code/src/Modules/SettleAccount/host/SettleAccount.HttpApi.Host/wwwroot/views/base-data/job-item.js +++ b/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 }; }, 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 new file mode 100644 index 00000000..5f5ee604 --- /dev/null +++ b/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 ServiceTypes { get; private set; } + public ConcurrentDictionary> 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(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(); + var entity = db.Set().FirstOrDefault(o => o.Id == id); + if (entity != null) + { + entity.IsRunning = false; + var log = db.Set().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(); + var entity = db.Set().FirstOrDefault(o => o.Id == id); + if (entity != null) + { + entity.IsRunning = false; + var log = db.Set().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(); + var entity = db.Set().FirstOrDefault(o => o.Id == id); + if (entity != null) + { + entity.IsRunning = true; + var log = db.Set().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().Set().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(); + var jobs = db.Set().AsNoTracking().ToList(); + jobs.ForEach(this.AddJob); + return Task.CompletedTask; + } +} diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobItemAppService.cs b/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobItemAppService.cs index 6a6f59b9..674596d1 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.Application/Entities/BQ/JobItemAppService.cs +++ b/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> Jobs { get; } = new(); - - public static List 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(); - var jobItemRepository = db.Set(); - var jobLogRepository = db.Set(); - 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(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(); - var jobs = db.Set().AsNoTracking().ToList(); - jobs.ForEach(this.AddJob); - return Task.CompletedTask; - } -} [Route("api/settleaccount/[controller]/[action]")] public class JobItemAppService : ApplicationService, ITransientDependency 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 582b0374..855d6be4 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 @@ -75,7 +75,7 @@ public class VmiAppService : ApplicationService, IVmiService, IJobService, ITran using var dbContext = new VmiSqliteContext(connectionString); if (!dbContext.GetService().Exists() && dbContext.Database.EnsureCreated()) { - var list = this._balanceRepository.AsNoTracking().ToList(); + var list = this._balanceRepository.ToList(); foreach (var item in list) { dbContext.Set().Add(item); diff --git a/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContextModelCreatingExtensions.cs b/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContextModelCreatingExtensions.cs index fac649ec..1af20c64 100644 --- a/code/src/Modules/SettleAccount/src/SettleAccount.EntityFrameworkCore/EntityFrameworkCore/SettleAccountDbContextModelCreatingExtensions.cs +++ b/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 北汽结算 @@ -1010,7 +1011,7 @@ namespace Win.Sfs.SettleAccount b.Property(x => x.BussiessType).HasMaxLength(50); b.Property(x => x.Extend1).HasMaxLength(50); b.Property(x => x.Extend2).HasMaxLength(50); - + b.Property(x => x.ConcurrencyStamp).HasMaxLength(50); }); } @@ -1281,7 +1282,7 @@ namespace Win.Sfs.SettleAccount }); //seed - builder.Entity().HasData(new JobItem("vmi".ToGuid()) { Name = "库存快照", Cron = "0 0 8 26 *", Service = "Win.Sfs.SettleAccount.Entities.BQ.VmiService" }); + builder.Entity().HasData(new JobItem("vmi".ToGuid()) { Name = "库存快照", Cron = "0 0 8 26 *", Service = "Win.Sfs.SettleAccount.Entities.BQ.VmiAppService" }); builder.Entity().HasData(new VmiCategory("发运入库".ToGuid()) { Type = VmiType.In, Name = "发运入库", Number = "100" }); builder.Entity().HasData(new VmiCategory("结算出库".ToGuid()) { Type = VmiType.Out, Name = "结算出库", Number = "200" }); builder.Entity().HasData(new VmiCategory("客户退货".ToGuid()) { Type = VmiType.Out, Name = "客户退货", Number = "300" });