2024-02-02 17:14:41 +08:00
|
|
|
|
// #define USE_TEST_DB // 测试库的结构与生产库不一样,如果使用测试库运行,则加上USE_TEST_DB预处理器指令
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
2024-02-02 17:14:41 +08:00
|
|
|
|
using MesETL.App;
|
|
|
|
|
using MesETL.App.Services;
|
|
|
|
|
using MesETL.App.Services.ETL;
|
|
|
|
|
using MesETL.App.Cache;
|
|
|
|
|
using MesETL.App.Const;
|
|
|
|
|
using MesETL.App.HostedServices;
|
|
|
|
|
using MesETL.App.HostedServices.Abstractions;
|
|
|
|
|
using MesETL.App.Options;
|
|
|
|
|
using MesETL.App.Services.ErrorRecorder;
|
|
|
|
|
using MesETL.App.Services.Loggers;
|
2023-12-28 15:18:03 +08:00
|
|
|
|
using Microsoft.Extensions.Configuration;
|
|
|
|
|
using Microsoft.Extensions.DependencyInjection;
|
|
|
|
|
using Microsoft.Extensions.Hosting;
|
|
|
|
|
using Microsoft.Extensions.Logging;
|
|
|
|
|
using Serilog;
|
2024-01-17 17:44:08 +08:00
|
|
|
|
using Serilog.Events;
|
2024-02-02 17:14:41 +08:00
|
|
|
|
using DumpDataHelper = MesETL.App.Helpers.DumpDataHelper;
|
2024-01-12 16:50:37 +08:00
|
|
|
|
|
2023-12-29 16:16:05 +08:00
|
|
|
|
await RunProgram();
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
async Task RunProgram()
|
2023-12-28 15:18:03 +08:00
|
|
|
|
{
|
2023-12-29 16:16:05 +08:00
|
|
|
|
ThreadPool.SetMaxThreads(200, 200);
|
2024-01-12 16:50:37 +08:00
|
|
|
|
var host = Host.CreateApplicationBuilder(args);
|
2024-01-22 15:49:32 +08:00
|
|
|
|
host.Configuration.AddJsonFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "appsettings.json"), false, false);
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Configuration.AddCommandLine(args, new Dictionary<string, string>
|
2023-12-29 16:16:05 +08:00
|
|
|
|
{
|
2024-02-02 17:14:41 +08:00
|
|
|
|
{ "-d", "Input:InputDir" },
|
|
|
|
|
{ "--InputDir", "Input:InputDir" },
|
|
|
|
|
{ "-s", "Output:ConnectionString" },
|
|
|
|
|
{ "--ConnectionString", "Output:ConnectionString" },
|
|
|
|
|
{ "-r", "RedisCache:Configuration" },
|
|
|
|
|
{ "--Redis", "RedisCache:Configuration" },
|
|
|
|
|
{ "-g", "TenantDb:UseDbGroup" },
|
|
|
|
|
{ "--UseDbGroup", "TenantDb:UseDbGroup" },
|
|
|
|
|
{ "-c", "Command" },
|
|
|
|
|
{ "--Command", "Command" }
|
2024-01-29 09:29:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
var inputOptions = host.Configuration.GetRequiredSection("Input").Get<DataInputOptions>()
|
|
|
|
|
?? throw new ApplicationException("缺少Input配置");
|
|
|
|
|
|
|
|
|
|
var transformOptions = host.Configuration.GetRequiredSection("Transform").Get<DataTransformOptions>()
|
|
|
|
|
?? throw new ApplicationException("缺少Transform配置");
|
|
|
|
|
|
|
|
|
|
var outputOptions = host.Configuration.GetRequiredSection("Output").Get<DatabaseOutputOptions>()
|
|
|
|
|
?? throw new ApplicationException("缺少Output配置");
|
|
|
|
|
|
|
|
|
|
var redisSection = host.Configuration.GetRequiredSection("RedisCache");
|
|
|
|
|
var redisOptions = redisSection.Get<RedisCacheOptions>() ?? throw new ApplicationException("缺少RedisCache配置");
|
2024-02-02 17:14:41 +08:00
|
|
|
|
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var tenantDbSection = host.Configuration.GetRequiredSection("TenantDb");
|
2024-02-02 17:14:41 +08:00
|
|
|
|
var tenantDbOptions = new TenantDbOptions()
|
|
|
|
|
{
|
|
|
|
|
TenantKey = tenantDbSection.GetValue<string>(nameof(TenantDbOptions.TenantKey)) ?? throw new ApplicationException("分库配置缺少分库键TenantKey"),
|
|
|
|
|
UseDbGroup = tenantDbSection.GetValue<string>(nameof(TenantDbOptions.UseDbGroup)) ?? throw new ApplicationException("分库配置缺少使用分库组UseDbGroup")
|
|
|
|
|
};
|
|
|
|
|
tenantDbOptions.DbGroup = tenantDbSection.GetRequiredSection($"DbGroups:{tenantDbOptions.UseDbGroup}").Get<Dictionary<string,int>>()
|
|
|
|
|
?? throw new ApplicationException($"分库配置无法解析分库组{tenantDbOptions.UseDbGroup},请检查配置");
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
2024-02-02 17:14:41 +08:00
|
|
|
|
host.Services.Configure<TenantDbOptions>(options =>
|
|
|
|
|
{
|
|
|
|
|
options.TenantKey = tenantDbOptions.TenantKey;
|
|
|
|
|
options.UseDbGroup = tenantDbOptions.UseDbGroup;
|
|
|
|
|
options.DbGroup = tenantDbOptions.DbGroup;
|
|
|
|
|
});
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Services.Configure<RedisCacheOptions>(redisSection);
|
|
|
|
|
|
2024-02-01 13:41:59 +08:00
|
|
|
|
var oldestTime = DateTime.ParseExact(transformOptions.CleanDate, "yyyyMM", System.Globalization.DateTimeFormatInfo.InvariantInfo);
|
|
|
|
|
var oldestTimeInt = int.Parse(transformOptions.CleanDate);
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
|
|
|
|
// 输入配置
|
2024-01-12 16:50:37 +08:00
|
|
|
|
host.Services.Configure<DataInputOptions>(options =>
|
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
options.InputDir = inputOptions.InputDir ?? throw new ApplicationException("未配置输入目录");
|
|
|
|
|
options.UseMock = inputOptions.UseMock;
|
|
|
|
|
options.TableMockConfig = inputOptions.TableMockConfig;
|
|
|
|
|
options.MockCountMultiplier = inputOptions.MockCountMultiplier;
|
|
|
|
|
|
|
|
|
|
// 配置文件输入方法
|
|
|
|
|
options.FileInputMetaBuilder = fileName =>
|
2024-01-12 16:50:37 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if (fileName.EndsWith(".dat.zst"))
|
2024-01-12 16:50:37 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var tableName = DumpDataHelper.GetTableNameFromCsvFileName(
|
|
|
|
|
Path.GetFileNameWithoutExtension(fileName)); // 去除.zst
|
|
|
|
|
string[]? headers;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
try
|
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// 查找同目录下同表的SQL文件
|
|
|
|
|
var sqlFile = Directory.GetFiles(options.InputDir)
|
|
|
|
|
.SingleOrDefault(f => f.Equals(fileName.Replace(".dat.zst",".sql.zst")));
|
|
|
|
|
if (sqlFile is null)
|
|
|
|
|
return null;
|
|
|
|
|
headers = DumpDataHelper.GetCsvHeadersFromSqlFile(
|
|
|
|
|
DumpDataHelper.DecompressZstAsStringAsync(File.OpenRead(sqlFile)).Result);
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
catch (InvalidOperationException e)
|
2024-01-12 16:50:37 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
throw new ApplicationException($"目录下不止一个{tableName}表的SQL文件", e);
|
2024-01-12 16:50:37 +08:00
|
|
|
|
}
|
2024-01-16 18:00:23 +08:00
|
|
|
|
|
2024-01-29 09:29:16 +08:00
|
|
|
|
return new FileInputInfo
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
FileName = fileName,
|
|
|
|
|
TableName = tableName,
|
|
|
|
|
Headers = headers
|
|
|
|
|
};
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
return null;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
};
|
2024-01-12 16:50:37 +08:00
|
|
|
|
|
2024-01-29 09:29:16 +08:00
|
|
|
|
options.TableOrder =
|
|
|
|
|
[
|
|
|
|
|
TableNames.Machine,
|
|
|
|
|
|
|
|
|
|
TableNames.Order,
|
|
|
|
|
TableNames.OrderBoxBlock, // 依赖Order.CompanyID
|
|
|
|
|
|
|
|
|
|
TableNames.OrderBlockPlan,
|
|
|
|
|
TableNames.OrderBlockPlanResult,// 依赖OrderBlockPlan.CompanyID / 删除
|
|
|
|
|
|
|
|
|
|
TableNames.OrderItem,
|
|
|
|
|
TableNames.OrderDataBlock,
|
|
|
|
|
TableNames.OrderDataGoods,
|
|
|
|
|
TableNames.OrderDataParts,
|
|
|
|
|
TableNames.OrderModule,
|
|
|
|
|
TableNames.OrderModuleExtra,
|
|
|
|
|
TableNames.OrderModuleItem,
|
|
|
|
|
TableNames.OrderPackage,
|
|
|
|
|
TableNames.OrderPatchDetail,
|
|
|
|
|
|
|
|
|
|
TableNames.OrderProcess,
|
|
|
|
|
TableNames.OrderProcessStep,
|
|
|
|
|
TableNames.OrderProcessStepItem,// 依赖OrderProcess.ShardKey / 删除
|
|
|
|
|
|
|
|
|
|
TableNames.OrderProcessSchedule,
|
|
|
|
|
TableNames.OrderScrapBoard,
|
|
|
|
|
TableNames.ProcessGroup,
|
|
|
|
|
TableNames.ProcessInfo,
|
|
|
|
|
TableNames.ProcessItemExp,
|
|
|
|
|
TableNames.ProcessScheduleCapacity,
|
|
|
|
|
TableNames.ProcessStepEfficiency,
|
|
|
|
|
TableNames.ReportTemplate,
|
|
|
|
|
TableNames.SimplePackage,
|
|
|
|
|
TableNames.SimplePlanOrder,
|
|
|
|
|
TableNames.SysConfig,
|
|
|
|
|
TableNames.WorkCalendar,
|
|
|
|
|
TableNames.WorkShift,
|
|
|
|
|
TableNames.WorkTime
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// options.TableMockConfig = new Dictionary<string, TableMockConfig>
|
|
|
|
|
// {
|
|
|
|
|
// { TableNames.Machine, new TableMockConfig(true, 14655, ["ID"]) },
|
|
|
|
|
// { TableNames.Order, new TableMockConfig(true, 5019216, ["OrderNo"]) },
|
|
|
|
|
// { TableNames.OrderDataBlock, new TableMockConfig(true, 731800334, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderDataGoods, new TableMockConfig(true, 25803671, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderDataParts, new TableMockConfig(true, 468517543, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderModule, new TableMockConfig(true, 103325385, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderModuleExtra, new TableMockConfig(true, 54361321, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderModuleItem, new TableMockConfig(true, 69173339, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderPackage, new TableMockConfig(true, 16196195, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderProcess, new TableMockConfig(true, 3892685, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderProcessStep, new TableMockConfig(true, 8050349, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderProcessStepItem, new TableMockConfig(true, 14538058, ["ID"]) },
|
|
|
|
|
// { TableNames.OrderScrapBoard, new TableMockConfig(true, 123998, ["ID"]) },
|
|
|
|
|
// { TableNames.ProcessGroup, new TableMockConfig(true, 1253, ["ID"]) },
|
|
|
|
|
// { TableNames.ProcessInfo, new TableMockConfig(true, 7839, ["ID"]) },
|
|
|
|
|
// { TableNames.ProcessItemExp, new TableMockConfig(true, 28, ["ID"]) },
|
|
|
|
|
// { TableNames.ProcessScheduleCapacity, new TableMockConfig(true, 39736, ["ID"]) },
|
|
|
|
|
// { TableNames.ProcessStepEfficiency, new TableMockConfig(true, 8, ["ID"]) },
|
|
|
|
|
// { TableNames.ReportTemplate, new TableMockConfig(true, 7337, ["ID"]) },
|
|
|
|
|
// { TableNames.SimplePackage, new TableMockConfig(true, 130436, ["ID"]) },
|
|
|
|
|
// { TableNames.SysConfig, new TableMockConfig(true, 2296, ["ID"]) },
|
|
|
|
|
// { TableNames.WorkCalendar, new TableMockConfig(true, 11, ["ID"]) },
|
|
|
|
|
// { TableNames.WorkShift, new TableMockConfig(true, 59, ["ID"]) },
|
|
|
|
|
// { TableNames.WorkTime, new TableMockConfig(true, 62, ["ID"]) }
|
|
|
|
|
// };
|
|
|
|
|
options.TableMockConfig = new Dictionary<string, TableMockConfig>
|
|
|
|
|
{
|
|
|
|
|
{ TableNames.Machine, new TableMockConfig(true, 14655, ["ID"]) },
|
|
|
|
|
{ TableNames.Order, new TableMockConfig(true, 50192, ["OrderNo"]) },
|
|
|
|
|
{ TableNames.OrderDataBlock, new TableMockConfig(true, 7318003, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderDataGoods, new TableMockConfig(true, 258036, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderDataParts, new TableMockConfig(true, 4685175, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderItem, new TableMockConfig(true, 13298896, ["ID"])},
|
|
|
|
|
{ TableNames.OrderModule, new TableMockConfig(true, 1033253, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderModuleExtra, new TableMockConfig(true, 543613, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderModuleItem, new TableMockConfig(true, 691733, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderPackage, new TableMockConfig(true, 161961, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderProcess, new TableMockConfig(true, 38926, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderProcessStep, new TableMockConfig(true, 80503, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderProcessStepItem, new TableMockConfig(true, 145380, ["ID"]) },
|
|
|
|
|
{ TableNames.OrderScrapBoard, new TableMockConfig(true, 1239, ["ID"]) },
|
|
|
|
|
{ TableNames.ProcessGroup, new TableMockConfig(true, 125, ["ID"]) },
|
|
|
|
|
{ TableNames.ProcessInfo, new TableMockConfig(true, 783, ["ID"]) },
|
|
|
|
|
{ TableNames.ProcessItemExp, new TableMockConfig(true, 28, ["ID"]) },
|
|
|
|
|
{ TableNames.ProcessScheduleCapacity, new TableMockConfig(true, 39736, ["ID"]) },
|
|
|
|
|
{ TableNames.ProcessStepEfficiency, new TableMockConfig(true, 8, ["ID"]) },
|
|
|
|
|
{ TableNames.ReportTemplate, new TableMockConfig(true, 7337, ["ID"]) },
|
|
|
|
|
{ TableNames.SimplePackage, new TableMockConfig(true, 130436, ["ID"]) },
|
|
|
|
|
{ TableNames.SysConfig, new TableMockConfig(true, 2296, ["Key"]) },
|
|
|
|
|
{ TableNames.WorkCalendar, new TableMockConfig(true, 11, ["ID"]) },
|
|
|
|
|
{ TableNames.WorkShift, new TableMockConfig(true, 59, ["ID"]) },
|
|
|
|
|
{ TableNames.WorkTime, new TableMockConfig(true, 62, ["ID"]) }
|
2024-01-16 18:00:23 +08:00
|
|
|
|
};
|
2024-01-29 09:29:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
host.Services.Configure<DataTransformOptions>(options =>
|
|
|
|
|
{
|
|
|
|
|
// 数据缓存键格式为[TableName]-[ColumnName@ColumnValue]-[CacheColumnName]
|
|
|
|
|
static string BuildCacheKey(string tableName, string columnName, string columnValue, string cacheColumnName)
|
|
|
|
|
=> $"{tableName}-{columnName}@{columnValue}-{cacheColumnName}";
|
|
|
|
|
|
|
|
|
|
static string CalculateShardKeyByOrderNo(ReadOnlySpan<char> orderNo)
|
|
|
|
|
=> $"{orderNo[2..6]}0";
|
|
|
|
|
|
|
|
|
|
options.StrictMode = transformOptions.StrictMode;
|
|
|
|
|
options.EnableFilter = transformOptions.EnableFilter;
|
|
|
|
|
options.EnableReplacer = transformOptions.EnableReplacer;
|
|
|
|
|
options.EnableReBuilder = transformOptions.EnableReBuilder;
|
|
|
|
|
|
|
|
|
|
// order_block_plan_item和order_package_item表不导入,根据order_item数据直接重建
|
|
|
|
|
|
|
|
|
|
// 数据清理
|
|
|
|
|
options.RecordFilter = async context =>
|
2024-01-12 16:50:37 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var record = context.Record;
|
|
|
|
|
var cache = context.Cacher;
|
|
|
|
|
switch (record.TableName)
|
2024-01-12 16:50:37 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderBoxBlock删除对应Order.OrderNo不存在的对象
|
|
|
|
|
case TableNames.OrderBoxBlock:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if (!await cache.ExistsAsync(CacheKeys.Order_OrderNo_CompanyID(record["OrderNo"])))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderBlockPlan删除CreateTime < 202301的
|
|
|
|
|
case TableNames.OrderBlockPlan:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var time = DateTime.Parse(record["CreateTime"].Trim('"'));
|
|
|
|
|
if (time < oldestTime)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderBlockPlanResult删除对应order_block_plan.ID不存在的对象
|
|
|
|
|
case TableNames.OrderBlockPlanResult:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if (!await cache.ExistsAsync(CacheKeys.OrderBlockPlan_ID_CompanyID(record["ID"])))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderModule删除OrderNo < 202301的
|
|
|
|
|
case TableNames.OrderModule:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var orderNo = record["OrderNo"];
|
|
|
|
|
if(int.Parse(orderNo.AsSpan(0, 6).ToString()) < oldestTimeInt)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderProcess删除OrderNo < 202301的
|
|
|
|
|
case TableNames.OrderProcess:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var orderNo = record["OrderNo"];
|
|
|
|
|
if(int.Parse(orderNo.AsSpan(0, 6).ToString()) < oldestTimeInt)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderProcessStep删除OrderNo < 202301的
|
|
|
|
|
case TableNames.OrderProcessStep:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var orderNo = record["OrderNo"];
|
|
|
|
|
if(int.Parse(orderNo.AsSpan(0, 6).ToString()) < oldestTimeInt)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// OrderProcessStepStep删除对应OrderProcess.ID不存在的对象
|
|
|
|
|
case TableNames.OrderProcessStepItem:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if (!await cache.ExistsAsync(CacheKeys.OrderProcess_ID_ShardKey(record["OrderProcessID"])))
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// SimplePackage删除OrderNo < 202301的
|
|
|
|
|
case TableNames.SimplePackage:
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var orderNo = record["OrderNo"];
|
|
|
|
|
if(int.Parse(orderNo.AsSpan(0, 6).ToString()) < oldestTimeInt)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// SimplePlanOrder删除CreateTime < 202301的
|
|
|
|
|
case TableNames.SimplePlanOrder:
|
2024-01-22 15:49:32 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var time = DateTime.Parse(record["CreateTime"].Trim('"'));
|
|
|
|
|
if (time < oldestTime)
|
|
|
|
|
return false;
|
|
|
|
|
break;
|
2024-01-22 15:49:32 +08:00
|
|
|
|
}
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 数据替换
|
|
|
|
|
options.RecordModify = async context =>
|
|
|
|
|
{
|
|
|
|
|
var record = context.Record;
|
|
|
|
|
var cache = context.Cacher;
|
|
|
|
|
switch (record.TableName)
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
#if USE_TEST_DB
|
|
|
|
|
// Order表移除IsBatch列
|
|
|
|
|
case TableNames.Order:
|
|
|
|
|
record.RemoveField("IsBatch");
|
|
|
|
|
break;
|
|
|
|
|
#endif
|
|
|
|
|
//OrderBlockPlan将OrderNo长度<2的置空
|
|
|
|
|
case TableNames.OrderBlockPlan:
|
|
|
|
|
if (record["OrderNos"].Length < 2)
|
2024-02-02 17:14:41 +08:00
|
|
|
|
record["OrderNos"] = "NULL";
|
2024-01-29 09:29:16 +08:00
|
|
|
|
break;
|
|
|
|
|
// OrderBlockPlanResult表添加CompanyID列
|
|
|
|
|
case TableNames.OrderBlockPlanResult:
|
|
|
|
|
record.AddField("CompanyID",
|
|
|
|
|
// 获取OrderBlockPlan.ID -> CompanyID
|
|
|
|
|
ThrowIfNoCached(await cache.GetStringAsync(CacheKeys.OrderBlockPlan_ID_CompanyID(record["ID"])),
|
|
|
|
|
TableNames.OrderBlockPlanResult, TableNames.OrderBlockPlan, "ID", "脏数据未处理"));
|
|
|
|
|
break;
|
|
|
|
|
// OrderBoxBlock添加CompanyID列
|
|
|
|
|
case TableNames.OrderBoxBlock:
|
|
|
|
|
record.AddField("CompanyID",
|
|
|
|
|
// 获取Order.OrderNo -> CompanyID
|
|
|
|
|
ThrowIfNoCached(await cache.GetStringAsync(CacheKeys.Order_OrderNo_CompanyID(record["OrderNo"])),
|
|
|
|
|
TableNames.OrderBoxBlock, TableNames.Order, "OrderNo", "脏数据未处理"));
|
|
|
|
|
break;
|
|
|
|
|
// OrderModule添加ShardKey列,移除ViewFileName列
|
|
|
|
|
case TableNames.OrderModule:
|
|
|
|
|
record.AddField("ShardKey", CalculateShardKeyByOrderNo(record["OrderNo"]));
|
|
|
|
|
record.RemoveField("ViewFileName");
|
|
|
|
|
break;
|
|
|
|
|
// OrderProcess添加ShardKey列,NextStepID的空值转换为0
|
|
|
|
|
case TableNames.OrderProcess:
|
|
|
|
|
record.AddField("ShardKey", CalculateShardKeyByOrderNo(record["OrderNo"]));
|
|
|
|
|
#if USE_TEST_DB
|
|
|
|
|
if(record["NextStepID"] == "\\N")
|
|
|
|
|
record["NextStepID"] = "0";
|
|
|
|
|
#endif
|
|
|
|
|
break;
|
|
|
|
|
// OrderProcessStep,OrderProcessStepItem添加ShardKey列
|
|
|
|
|
case TableNames.OrderProcessStep:
|
|
|
|
|
record.AddField("ShardKey", CalculateShardKeyByOrderNo(record["OrderNo"]));
|
|
|
|
|
break;
|
|
|
|
|
case TableNames.OrderProcessStepItem:
|
|
|
|
|
record.AddField("ShardKey",
|
|
|
|
|
// 获取OrderProcess.ID -> ShardKey
|
|
|
|
|
ThrowIfNoCached(await cache.GetStringAsync(CacheKeys.OrderProcess_ID_ShardKey(record["OrderProcessID"])),
|
|
|
|
|
TableNames.OrderProcessStepItem, TableNames.OrderProcessStep, "OrderProcessID", "脏数据未处理"));
|
|
|
|
|
break;
|
|
|
|
|
case TableNames.SimplePlanOrder:
|
|
|
|
|
#if USE_TEST_DB
|
|
|
|
|
record.RemoveField("ProcessState");
|
|
|
|
|
#endif
|
|
|
|
|
record.AddField("Deleted", "0");
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
|
|
|
|
return record;
|
|
|
|
|
|
|
|
|
|
string ThrowIfNoCached(string? cached, string tableName, string cachedTableName, string cachedColumn, string appendMessage = "")
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if (cached is null)
|
|
|
|
|
throw new InvalidDataException(
|
|
|
|
|
$"{tableName}数据异常,在缓存中未找到对应{cachedTableName}.{cachedColumn}\t{appendMessage}");
|
|
|
|
|
return cached;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 数据缓存
|
|
|
|
|
options.RecordCache = async context =>
|
|
|
|
|
{
|
|
|
|
|
var record = context.Record;
|
|
|
|
|
var cache = context.Cacher;
|
|
|
|
|
switch (record.TableName)
|
2024-01-16 18:00:23 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// 缓存Order.OrderNo -> CompanyID
|
|
|
|
|
case TableNames.Order:
|
|
|
|
|
CacheKeys.Order_OrderNo_CompanyID = orderNo =>
|
|
|
|
|
BuildCacheKey(TableNames.Order, "OrderNo", orderNo, "CompanyID");
|
|
|
|
|
await cache.SetStringAsync(
|
|
|
|
|
CacheKeys.Order_OrderNo_CompanyID(record["OrderNo"]),
|
|
|
|
|
record["CompanyID"]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// 缓存OrderBlockPlan.ID -> CompanyID
|
|
|
|
|
case TableNames.OrderBlockPlan:
|
|
|
|
|
CacheKeys.OrderBlockPlan_ID_CompanyID = id =>
|
|
|
|
|
BuildCacheKey(TableNames.OrderBlockPlan, "ID", id, "CompanyID");
|
|
|
|
|
await cache.SetStringAsync(
|
|
|
|
|
CacheKeys.OrderBlockPlan_ID_CompanyID(record["ID"]),
|
|
|
|
|
record["CompanyID"]);
|
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
// 缓存OrderProcess.ID -> ShardKey
|
|
|
|
|
case TableNames.OrderProcess:
|
|
|
|
|
CacheKeys.OrderProcess_ID_ShardKey = id =>
|
|
|
|
|
BuildCacheKey(TableNames.OrderProcess, "ID", id, "ShardKey");
|
|
|
|
|
await cache.SetStringAsync(
|
|
|
|
|
CacheKeys.OrderProcess_ID_ShardKey(record["ID"]),
|
|
|
|
|
record["ShardKey"]);
|
|
|
|
|
break;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
|
|
|
|
};
|
2024-01-29 09:29:16 +08:00
|
|
|
|
|
|
|
|
|
// 数据库过滤
|
|
|
|
|
options.DatabaseFilter = record =>
|
2024-01-15 17:26:44 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
var companyId = int.Parse(record[tenantDbOptions.TenantKey]); // 每个实体都应存在CompanyID,否则异常
|
|
|
|
|
return tenantDbOptions.GetDbNameByTenantKeyValue(companyId);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// 数据重建
|
|
|
|
|
options.RecordReBuild = context =>
|
|
|
|
|
{
|
|
|
|
|
var record = context.Record;
|
2024-01-16 18:00:23 +08:00
|
|
|
|
var resultList = new List<DataRecord>();
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// 分流OrderItem表
|
|
|
|
|
if (record.TableName == TableNames.OrderItem)
|
2024-01-15 17:26:44 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
record.TryGetField("ID", out var itemId);
|
2024-01-16 18:00:23 +08:00
|
|
|
|
record.TryGetField("ShardKey", out var shardKey);
|
2024-01-29 09:29:16 +08:00
|
|
|
|
record.TryGetField("PlanID", out var planId);
|
|
|
|
|
record.TryGetField("PackageID", out var packageId);
|
|
|
|
|
record.TryGetField("CompanyID", out var companyId);
|
|
|
|
|
if(!int.TryParse(planId, out var pid))
|
|
|
|
|
throw new ApplicationException($"数据发生异常:OrderItem.PlanID,值: {planId}");
|
2024-01-16 18:00:23 +08:00
|
|
|
|
if (pid > 0)
|
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
resultList.Add(new DataRecord(new[] { itemId, shardKey, planId, companyId },
|
|
|
|
|
TableNames.OrderBlockPlanItem,
|
|
|
|
|
["ItemID", "ShardKey", "PlanID", "CompanyID"]
|
|
|
|
|
));
|
2024-01-16 18:00:23 +08:00
|
|
|
|
}
|
2024-01-29 09:29:16 +08:00
|
|
|
|
if(!int.TryParse(packageId, out var pkid))
|
|
|
|
|
throw new ApplicationException($"数据发生异常:OrderItem.PackageID,值: {packageId}");
|
2024-01-16 18:00:23 +08:00
|
|
|
|
if(pkid > 0)
|
2024-01-15 17:26:44 +08:00
|
|
|
|
{
|
2024-01-29 09:29:16 +08:00
|
|
|
|
resultList.Add(new DataRecord(new[] { itemId, shardKey, packageId, companyId },
|
|
|
|
|
TableNames.OrderPackageItem,
|
|
|
|
|
[ "ItemID", "ShardKey", "PackageID", "CompanyID" ]
|
|
|
|
|
));
|
2024-01-15 17:26:44 +08:00
|
|
|
|
}
|
|
|
|
|
}
|
2024-01-16 18:00:23 +08:00
|
|
|
|
return resultList;
|
2024-01-15 17:26:44 +08:00
|
|
|
|
|
|
|
|
|
};
|
2024-01-29 09:29:16 +08:00
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
host.Services.Configure<DatabaseOutputOptions>(options =>
|
|
|
|
|
{
|
2024-02-01 13:41:59 +08:00
|
|
|
|
options.ConnectionString = outputOptions.ConnectionString;
|
2024-01-29 09:29:16 +08:00
|
|
|
|
options.FlushCount = outputOptions.FlushCount;
|
|
|
|
|
options.MaxAllowedPacket = outputOptions.MaxAllowedPacket;
|
|
|
|
|
options.MaxDatabaseOutputTask = outputOptions.MaxDatabaseOutputTask;
|
|
|
|
|
options.TreatJsonAsHex = outputOptions.TreatJsonAsHex;
|
|
|
|
|
|
|
|
|
|
#if USE_TEST_DB
|
|
|
|
|
// Test Server
|
|
|
|
|
options.ColumnTypeConfig = new Dictionary<string, ColumnType>
|
|
|
|
|
{
|
|
|
|
|
{ "simple_plan_order.PlaceData", ColumnType.Blob },
|
|
|
|
|
{ "order_block_plan_result.PlaceData", ColumnType.Blob },
|
|
|
|
|
{ "order_box_block.Data", ColumnType.Blob },
|
|
|
|
|
{ "order_data_goods.ExtraProp", ColumnType.Json },
|
|
|
|
|
{ "order_module_extra.JsonStr", ColumnType.Text },
|
|
|
|
|
{ "process_info.Users", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.CustomOrderNo", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.OrderProcessStepName", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.AreaName", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.ConsigneeAddress", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.ConsigneePhone", ColumnType.Text },
|
|
|
|
|
{ "report_source.Sql", ColumnType.Text },
|
|
|
|
|
{ "report_source.KeyValue", ColumnType.Text },
|
|
|
|
|
{ "report_source.Setting", ColumnType.Text },
|
|
|
|
|
{ "order_data_block.RemarkJson", ColumnType.Text },
|
|
|
|
|
{ "order_patch_detail.BlockDetail", ColumnType.Json },
|
|
|
|
|
{ "order_scrap_board.OutLineJson", ColumnType.Text },
|
|
|
|
|
{ "simple_package.Items", ColumnType.Json },
|
|
|
|
|
{ "order_batch_pack_config.Setting", ColumnType.Text },
|
|
|
|
|
{ "machine.Settings", ColumnType.Text },
|
|
|
|
|
{ "sys_config.Value", ColumnType.Text },
|
|
|
|
|
{ "sys_config.JsonStr", ColumnType.Text },
|
|
|
|
|
{ "process_item_exp.ItemJson", ColumnType.Text },
|
|
|
|
|
{ "report_template.Template", ColumnType.Text },
|
|
|
|
|
{ "report_template.SourceConfig", ColumnType.Text },
|
|
|
|
|
{ "order_block_plan.OrderNos", ColumnType.Json },
|
|
|
|
|
{ "order_block_plan.BlockInfo", ColumnType.Text },
|
|
|
|
|
};
|
|
|
|
|
#else
|
|
|
|
|
// 配置列类型
|
|
|
|
|
// Prod server
|
|
|
|
|
options.ColumnTypeConfig = new Dictionary<string, ColumnType>
|
2023-12-29 16:16:05 +08:00
|
|
|
|
{
|
|
|
|
|
{ "simple_plan_order.PlaceData", ColumnType.Blob },
|
|
|
|
|
{ "order_block_plan_result.PlaceData", ColumnType.Blob },
|
|
|
|
|
{ "order_box_block.Data", ColumnType.Blob },
|
|
|
|
|
{ "order_data_goods.ExtraProp", ColumnType.Text },
|
|
|
|
|
{ "order_module_extra.JsonStr", ColumnType.Text },
|
|
|
|
|
{ "process_info.Users", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.CustomOrderNo", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.OrderProcessStepName", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.AreaName", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.ConsigneeAddress", ColumnType.Text },
|
|
|
|
|
{ "order_process_schdule.ConsigneePhone", ColumnType.Text },
|
|
|
|
|
{ "report_source.Sql", ColumnType.Text },
|
|
|
|
|
{ "report_source.KeyValue", ColumnType.Text },
|
|
|
|
|
{ "report_source.Setting", ColumnType.Text },
|
|
|
|
|
{ "order_data_block.RemarkJson", ColumnType.Text },
|
|
|
|
|
{ "order_patch_detail.BlockDetail", ColumnType.Text },
|
|
|
|
|
{ "order_scrap_board.OutLineJson", ColumnType.Text },
|
|
|
|
|
{ "simple_package.Items", ColumnType.Text },
|
|
|
|
|
{ "order_batch_pack_config.Setting", ColumnType.Text },
|
|
|
|
|
{ "machine.Settings", ColumnType.Text },
|
|
|
|
|
{ "sys_config.Value", ColumnType.Text },
|
|
|
|
|
{ "sys_config.JsonStr", ColumnType.Text },
|
|
|
|
|
{ "process_item_exp.ItemJson", ColumnType.Text },
|
|
|
|
|
{ "report_template.Template", ColumnType.Text },
|
|
|
|
|
{ "report_template.SourceConfig", ColumnType.Text },
|
2024-01-18 14:36:36 +08:00
|
|
|
|
{ "order_block_plan.OrderNos", ColumnType.Json },
|
2023-12-29 16:16:05 +08:00
|
|
|
|
{ "order_block_plan.BlockInfo", ColumnType.Text },
|
|
|
|
|
};
|
2024-01-29 09:29:16 +08:00
|
|
|
|
#endif
|
2023-12-29 16:16:05 +08:00
|
|
|
|
});
|
2024-01-18 15:03:45 +08:00
|
|
|
|
|
2023-12-29 16:16:05 +08:00
|
|
|
|
host.Services.AddLogging(builder =>
|
|
|
|
|
{
|
|
|
|
|
builder.ClearProviders();
|
2024-01-17 17:44:08 +08:00
|
|
|
|
builder.AddSerilog(new LoggerConfiguration()
|
|
|
|
|
.WriteTo.Console()
|
2024-02-02 17:14:41 +08:00
|
|
|
|
.WriteTo.File(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, $"./Log/Error/{ErrorRecorder.UID}.log"),
|
|
|
|
|
restrictedToMinimumLevel:LogEventLevel.Error)
|
2024-01-29 09:29:16 +08:00
|
|
|
|
// .WriteTo.File("./Log/Info/{ErrorRecorder.UID}.log", restrictedToMinimumLevel:LogEventLevel.Information) //性能考虑暂不使用
|
2024-01-17 17:44:08 +08:00
|
|
|
|
.CreateLogger()
|
|
|
|
|
);
|
2023-12-29 16:16:05 +08:00
|
|
|
|
});
|
|
|
|
|
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Services.AddDataSourceFactory();
|
|
|
|
|
host.Services.AddErrorRecorderFactory();
|
|
|
|
|
host.Services.AddSingleton<ProcessContext>();
|
|
|
|
|
host.Services.AddKeyedSingleton<DataRecordQueue>(ProcessStep.Produce);
|
2024-02-02 17:14:41 +08:00
|
|
|
|
host.Services.AddRecordQueuePool(tenantDbOptions.DbGroup.Keys.Select(key => (key:key, queue:new DataRecordQueue(500_000))).ToArray());
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Services.AddSingleton<ITaskMonitorLogger, CacheTaskMonitorLogger>();
|
|
|
|
|
host.Services.AddSingleton<ITaskMonitorLogger, LoggerTaskMonitorLogger>();
|
2023-12-29 16:16:05 +08:00
|
|
|
|
|
2024-01-04 09:00:44 +08:00
|
|
|
|
host.Services.AddHostedService<MainHostedService>();
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Services.AddSingleton<IInputService, FileInputService>();
|
2024-01-04 09:00:44 +08:00
|
|
|
|
host.Services.AddSingleton<ITransformService, TransformService>();
|
|
|
|
|
host.Services.AddSingleton<IOutputService, OutputService>();
|
2024-02-01 15:25:42 +08:00
|
|
|
|
host.Services.AddSingleton<TaskMonitorService>();
|
2024-01-29 09:29:16 +08:00
|
|
|
|
host.Services.AddRedisCache(redisOptions);
|
2023-12-29 16:16:05 +08:00
|
|
|
|
var app = host.Build();
|
|
|
|
|
await app.RunAsync();
|
|
|
|
|
}
|