优化项目结构,新增export命令

This commit is contained in:
xief
2022-01-18 09:37:36 +08:00
parent 66569ddec1
commit de141de3bd
10 changed files with 396 additions and 99 deletions

View File

@@ -0,0 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="CliFx" Version="2.2.1" />
<PackageReference Include="Dapper" Version="2.0.123" />
<PackageReference Include="MySqlConnector" Version="2.1.2" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,154 @@
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using CliFx;
using CliFx.Infrastructure;
using CliFx.Attributes;
using System.Data;
using System.Data.Common;
using Chenfeng.MES.Archiver.Core;
using Chenfeng.MES.Archiver.Data;
namespace Chenfeng.MES.Archiver.Commands
{
[Command("copy")]
public class CopyTableCommand : ICommand,IArchiveReader
{
[CommandParameter(0, Description = "数据库连接")]
public string Connection { get; set; } = "";
public string TableSuffix { get; set; } = "new";
public DbConnection? Db { get; private set; }
public Action<string> Print { get; set; } = (text) => { };
[CommandOption("legacy", Description = "旧版MES")]
public bool Legacy { get; set; } = false;
[CommandOption("replace", Description = "替换表")]
public bool Replace { get; set; } = false;
[CommandOption("restore", Description = "回复表")]
public bool Restore { get; set; } = false;
[CommandOption("rm-backup", Description = "删除备份表")]
public bool RemoveBackup { get; set; }
[CommandOption("timeout", Description = "sql命令超时时间")]
public int Timeout { get; set; } = 3;
public ValueTask ExecuteAsync(IConsole console)
{
Print = (text) => console.Output.WriteLine($"{DateTime.Now.ToLongTimeString()} {text}");
var connBuilder = new MySqlConnectionStringBuilder(this.Connection);
connBuilder.SslMode = MySqlSslMode.None;
connBuilder.CharacterSet = "utf8";
var starMonth = DateTime.Now.AddMonths(-12).ToString("yyyyMM");
SqlMapper.Settings.CommandTimeout = 60 * Timeout;
if (RemoveBackup)
{
console.Output.WriteLine("删除备份表");
}
else if (Restore)
{
console.Output.WriteLine("还原备份表");
}
else
{
console.Output.WriteLine($"拷贝"+( Legacy ? "旧" :"新")+"版表数据"+(Replace?"并替换":""));
}
try
{
Db = new MySqlConnection(connBuilder.ConnectionString);
Db.Open();
if (Legacy)
{
TableQueryHelper.MesLegacyTables(this,starMonth);
}
else
{
TableQueryHelper.MesTables(this,starMonth);
}
Print("操作成功");
}
catch (Exception ex)
{
console.Output.WriteLine(ex.ToString());
}
finally
{
Db?.Close();
}
return default;
}
public void ExecuteData(string table, string where, object param)
{
Db.Execute($"INSERT INTO `{TableDic[table]}` SELECT * FROM `{table}` WHERE {where}", param);
}
public void ExecuteChildData(string table, string parentTable, string condition)
{
Db.Execute($"INSERT INTO `{TableDic[table]}` SELECT child.* FROM `{TableDic[parentTable]}` parent join `{table}` child on {condition} ");
}
public Dictionary<string,string> TableDic { get; set; } = new Dictionary<string,string>();
public void Step(Action<string> action, params string[] tables)
{
var list = tables.Select(table => new
{
Table = table,
NewTable = table + "_" + this.TableSuffix
});
foreach (var item in list)
{
if (RemoveBackup)
{
Db.Execute($"DROP TABLE IF EXISTS `{item.Table}_bak`");
}
else if (Restore)
{
Db.Execute($"DROP TABLE IF EXISTS `{item.Table}`");
Db.Execute($"RENAME TABLE `{item.Table}_bak` TO `{item.Table}`");
}
else
{
TableDic[item.Table] = item.NewTable;
Db.Execute($"DROP TABLE IF EXISTS `{item.NewTable}`");
Db.Execute($"CREATE TABLE `{item.NewTable}` LIKE `{item.Table}`"); // IF NOT EXISTS
Print(item.NewTable + " 创建完成");
action(item.Table);
Print(item.NewTable + " 拷贝完成");
if (Replace)
{
Db.Execute($"RENAME TABLE `{item.Table}` TO `{item.Table}_bak`");
Db.Execute($"RENAME TABLE `{item.NewTable}` TO `{item.Table}`");
}
}
}
}
}
}

View File

@@ -0,0 +1,251 @@
using MySqlConnector;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Dapper;
using CliFx;
using CliFx.Infrastructure;
using CliFx.Attributes;
using System.Data;
using System.Data.Common;
using Chenfeng.MES.Archiver.Core;
using Chenfeng.MES.Archiver.Data;
using System.Diagnostics;
namespace Chenfeng.MES.Archiver.Commands
{
[Command("export")]
public class ExportTableCommand : ICommand, IArchiveReader
{
[CommandParameter(0, Description = "数据库连接")]
public string Connection { get; set; } = "";
public MySqlConnection? Db { get; private set; }
public Action<string> Print { get; set; } = (text) => { };
[CommandOption("legacy", Description = "旧版MES")]
public bool Legacy { get; set; } = false;
[CommandOption("timeout", Description = "sql命令超时时间")]
public int Timeout { get; set; } = 3;
[CommandOption("compress", Description = "文件压缩,默认gzip")]
public string Compress { get; set; } = "gz";
[CommandOption("pagesize", Description = "查询分页")]
public int PageSize { get; set; } = 500_000;
[CommandOption("output", Description = "导出路径")]
public string Output { get; set; } = "./output";
public ValueTask ExecuteAsync(IConsole console)
{
var startTime = DateTime.Now;
Print = (text) => console.Output.WriteLine($"{DateTime.Now.ToLongTimeString()} {text}");
var connBuilder = new MySqlConnectionStringBuilder(this.Connection);
connBuilder.SslMode = MySqlSslMode.None;
connBuilder.CharacterSet = "utf8";
var starMonth = DateTime.Now.AddMonths(-12).ToString("yyyyMM");
SqlMapper.Settings.CommandTimeout = 60 * Timeout;
console.Output.WriteLine($"拷贝" + (Legacy ? "旧" : "新") + "版表数据");
if (Directory.Exists(Output) == false)
{
Directory.CreateDirectory(Output);
}
try
{
Db = new MySqlConnection(connBuilder.ConnectionString);
Db.Open();
if (Legacy)
{
TableQueryHelper.MesLegacyTables(this, starMonth);
}
else
{
TableQueryHelper.MesTables(this, starMonth);
}
Print("操作成功");
}
catch (Exception ex)
{
console.Output.WriteLine(ex.ToString());
}
finally
{
Db?.Close();
}
console.Output.WriteLine("执行耗时:" + (DateTime.Now - startTime).TotalMilliseconds+"ms");
return default;
}
public void ExecuteData(string table, string where, object param)
{
var sql = $"SELECT * FROM `{table}` WHERE {where} ";
if (PageSize > 0)
{
sql += "limit @pageIndex,@pageSize";
}
DumpData(table, sql, param);
}
public void ExecuteChildData(string table, string parentTable, string condition)
{
var parentSqlInfo = tableSqlDic[parentTable];
DumpData(table, $"SELECT child.* FROM ({parentSqlInfo.Item1}) parent join `{table}` child on {condition} ", parentSqlInfo.Item2);
}
public Dictionary<string, Tuple<string, object?>> tableSqlDic { get; set; } = new();
private void DumpData(string table, string sql, object? param = null)
{
var watch = new Stopwatch();
watch.Start();
Print(table);
Print("开始导出");
tableSqlDic[table] = new Tuple<string, object?>(sql, param);
var cmd = Db.CreateCommand();
cmd.CommandTimeout = 60 * Timeout;
cmd.CommandText = sql;
if (param != null)
{
switch (param)
{
case Dictionary<string, object> dic:
foreach (var kv in dic.AsEnumerable())
{
cmd.Parameters.AddWithValue("@" + kv.Key, kv.Value);
}
break;
default:
foreach (var property in param.GetType().GetProperties())
{
cmd.Parameters.AddWithValue("@" + property.Name, property.GetValue(param));
}
break;
}
}
var columns = new List<string>();
var columnString = new StringBuilder();
var pageIndex = 1;
cmd.Parameters.AddWithValue("@pageSize", PageSize);
cmd.Parameters.AddWithValue("@pageIndex", 0);
while (true)
{
cmd.Parameters["@pageIndex"].Value = (pageIndex - 1) * PageSize;
var valString = new StringBuilder();
using (var reader = cmd.ExecuteReader())
{
if (columnString.Length ==0)
{
for (int i = 0; i < reader.FieldCount; i++)
{
//columns.Add(reader.GetName(i));
columnString.Append($"`reader.GetName(i)`,");
}
columnString.Length--;
}
while (reader.Read())
{
var row = new List<object>();
for (int i = 0; i < reader.FieldCount; i++)
{
var val = reader.GetValue(i);
switch (val)
{
case string s:
row.Add($"'{s}'");
break;
case DateTime dt:
row.Add(dt.ToString("'yyyy-MM-dd HH:mm:ss'"));
break;
case byte[] byteList:
row.Add("0x" + string.Concat(byteList.Select(i => i.ToString("X2"))));
break;
default:
row.Add(val);
break;
}
}
valString.Append($"({string.Join(",", row)}),");
}
if (valString.Length == 0) break;
valString.Length--; // 移除最后一个逗号
var distFile = $"{Output}/{table}-p{pageIndex}.{Compress}.sql";
using var fileStream = File.OpenWrite(distFile);
Stream compressStream;
switch (Compress)
{
case "gz":
case "gzip":
compressStream = new System.IO.Compression.GZipStream(fileStream, System.IO.Compression.CompressionLevel.Fastest);
break;
case "deflate":
compressStream = new System.IO.Compression.DeflateStream(fileStream, System.IO.Compression.CompressionLevel.Fastest);
break;
case "br":
compressStream = new System.IO.Compression.BrotliStream(fileStream, System.IO.Compression.CompressionLevel.Fastest);
break;
case "none": // 不压缩
default:
compressStream = fileStream;
break;
}
using (compressStream)
{
using (StreamWriter streamWriter = new StreamWriter(compressStream))
{
streamWriter.Write($"INSERT INTO `{table}` (");
streamWriter.Write(columnString);
streamWriter.Write(") VALUES ");
streamWriter.Write(valString);
//streamWriter.Flush();
}
}
pageIndex++;
}
}
watch.Stop();
Print("导出完成,耗时" + watch.ElapsedMilliseconds+"ms");
}
public void Step(Action<string> action, params string[] tables)
{
foreach (var item in tables)
{
action(item);
}
}
}
}

View File

@@ -0,0 +1,26 @@
using CliFx;
using CliFx.Attributes;
using CliFx.Infrastructure;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chenfeng.MES.Archiver.Commands
{
[Command("import")]
public class ImportSqlCommand : ICommand
{
[CommandParameter(0, Description = "路径")]
public string Source { get; set; } = "";
public string Connection { get; set; } = "";
public ValueTask ExecuteAsync(IConsole console)
{
throw new NotImplementedException();
}
}
}

View File

@@ -0,0 +1,17 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chenfeng.MES.Archiver.Core
{
public interface IArchiveReader
{
void ExecuteData(string table, string where, object param);
void ExecuteChildData(string table,string parentTable, string condition);
void Step(Action<string> action, params string[] tables);
}
}

View File

@@ -0,0 +1,77 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Chenfeng.MES.Archiver.Data
{
public class TableQueryHelper
{
public static void MesTables(Core.IArchiveReader executor, string starMonth)
{
executor.Step((table) =>
{
executor.ExecuteData(table, "LEFT(orderno,6) >= @starMonth", new { starMonth });
}, "order", "order_box_block", "order_data_block", "order_data_goods", "order_data_parts", "order_item", "order_module_extra", "order_module_item", "order_package");
executor.Step((tProcess) =>
{
executor.ExecuteData(tProcess, "LEFT(orderno,6) >= @starMonth", new { starMonth });
executor.Step((table) =>
{
executor.ExecuteChildData(table, tProcess, "parent.ID = child.OrderProcessID");
}, "order_process_step_item", "order_process_step");
}, "order_process");
executor.Step((table) =>
{
executor.ExecuteData(table, "LEFT(DATE_FORMAT(CreateTime,'%Y%m'),6) >= @starMonth", new { starMonth });
}, "simple_plan_order");
executor.Step((parent) =>
{
executor.ExecuteData(parent, "LEFT(DATE_FORMAT(CreateTime,'%Y%m'),6) >= @starMonth", new { starMonth });
executor.Step((child) =>
{
executor.ExecuteChildData(child, parent, "parent.ID = child.ID");
}, "order_block_plan_result");
}, "order_block_plan");
//CopyStep((table, newTable) =>
//{
// CopyData(table, newTable, "concat('20',left(id,4)) >= @starMonth ", new { starMonth });
//}, "order_scrap_board");
}
public static void MesLegacyTables(Core.IArchiveReader executor, string starMonth)
{
executor.Step((order) =>
{
executor.ExecuteData(order, "LEFT(DATE_FORMAT(SaleDate,'%Y%m'),6) >= @starMonth", new { starMonth });
executor.Step((child) =>
{
executor.ExecuteChildData(child, order, "parent.orderno=child.orderno");
}, "SaleOrderItem", "saleblock", "saleobject", "saleorderobject", "saleorderoffer", "saleorderpackage", "saleblockobjids", "saleobjectobjids", "saleorder_block_cadmodelinfo", "saleorderblockbaseposition",
"saleordergoodsinfo", "saleorder_block_point", "saleorder_block_pointinfo", "orderprocess", "orderprocessstep", "orderprocessstepitem");
}, "saleorder");
executor.Step((plan) =>
{
executor.ExecuteData(plan, "LEFT(DATE_FORMAT(createtime,'%Y%m'),6) >= @starMonth", new { starMonth });
executor.Step((child) =>
{
executor.ExecuteChildData(child, plan, "parent.id=child.id");
}, "orderblockprocessplanplaceresult");
}, "orderblockprocessplan");
}
}
}

11
src/Archiver/Program.cs Normal file
View File

@@ -0,0 +1,11 @@
// See https://aka.ms/new-console-template for more information
using CliFx;
//Console.WriteLine("Hello, World!");
await new CliApplicationBuilder()
.AddCommandsFromThisAssembly()
.Build()
.RunAsync(args);