123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
-
- using System.Linq;
- namespace etoy
- {
- class CmdCheckDataIntegrity : Command
- {
- public override string Description => "数据完整性检验";
- List<Exception> _errors = new List<Exception>();
- const string FIELD_REPEATED = "[]";
- readonly string[] PRIMITIVE_TYPES =
- {
- FieldTypeDefine.Int,
- FieldTypeDefine.Long,
- FieldTypeDefine.Boolean,
- FieldTypeDefine.String,
- FieldTypeDefine.Float,
- FieldTypeDefine.Double,
- };
- protected override void OnProcess()
- {
- SetProgress(0.1f);
- foreach (var table in Context.Blackboard.KeyValueTables)
- CheckKeyValueTable(table);
- SetProgress(0.3f);
- foreach (var table in Context.Blackboard.MetadataTables)
- CheckMetadataTable(table);
- SetProgress(0.6f);
- foreach (var table in Context.Blackboard.Tables)
- CheckTable(table);
- if (_errors.Count > 0)
- {
- SetException(new Exception(string.Join('\n', (from e in _errors select $"## ERROR: {e.Message}").ToArray())));
- }
- else
- {
- Completed();
- }
- }
- void CheckMetadataTable(MetadataTable table)
- {
- foreach (var row in table.Rows)
- {
- if (row.Struct.StructType == MetadataStructType.Struct)
- continue;
- // 检测枚举值
- foreach (var field in row.Struct.Fields)
- {
- if (!string.IsNullOrEmpty(field.EnumValue))
- {
- if (!int.TryParse(field.EnumValue, out var _))
- throw new Exception($"枚举({row.Struct.Name}) 枚举值转int失败 {table.Path}");
- }
- }
- }
- }
- void AddException(Exception e)
- {
- _errors.Add(e);
- }
- void CheckKeyValueTable(KeyValueTable table)
- {
- foreach (var row in table.Rows)
- {
- if (string.IsNullOrEmpty(row.Key))
- AddException(new Exception($"配置表({table.Name}) Key为空, 不合法。{table.Path}, (行号: {row.Row + 1})"));
- if (string.IsNullOrEmpty(row.Value))
- AddException(new Exception($"配置表({table.Name}) Value为空, 不合法。 Key: ({row.Key}), {table.Path}, (行号: {row.Row + 1})"));
- var fieldType = row.Type;
- var repeadIdx = fieldType.IndexOf(FIELD_REPEATED);
- if (repeadIdx > 0)
- { // 是数组类型
- fieldType = fieldType.Substring(0, repeadIdx).Trim();
- var repeatedType = fieldType switch
- {
- FieldTypeDefine.Boolean => typeof(bool[]),
- FieldTypeDefine.Int => typeof(int[]),
- FieldTypeDefine.Long => typeof(long[]),
- FieldTypeDefine.String => typeof(string[]),
- _ => default,
- };
- if (repeatedType == null)
- {
- AddException(new Exception($"配置表({table.Name}) 不是支持类型: ({row.Type}), Key: ({row.Key}) {table.Path}, (行号: {row.Row + 1})"));
- }
- else
- {
- if (!row.Value.JsonToObject(repeatedType, out var result))
- {
- AddException(new Exception($"配置表({table.Name}) 字段内容: ({row.Value}) 与 字段类型: ({row.Type}) 转换失败。{table.Path}, (行号: {row.Row + 1})"));
- }
- }
- continue;
- }
- switch (fieldType)
- {
- case FieldTypeDefine.Boolean:
- if (!int.TryParse(row.Value, out var boolValue) || (boolValue != 0 && boolValue != 1))
- AddException(new Exception($"配置表({table.Name}) 字段内容: ({row.Value}) 与 字段类型: ({row.Type}) 转换失败。{table.Path}, (行号: {row.Row + 1})"));
- break;
- case FieldTypeDefine.Int:
- if (!int.TryParse(row.Value, out _))
- AddException(new Exception($"配置表({table.Name}) 字段内容: ({row.Value}) 与 字段类型: ({row.Type}) 转换失败。{table.Path}, (行号: {row.Row + 1})"));
- break;
- case FieldTypeDefine.Long:
- if (!long.TryParse(row.Value, out _))
- AddException(new Exception($"配置表({table.Name}) 字段内容: ({row.Value}) 与 字段类型: ({row.Type}) 转换失败。{table.Path}, (行号: {row.Row + 1})"));
- break;
- case FieldTypeDefine.String:
- // Nothing to check
- break;
- default:
- AddException(new Exception($"配置表({table.Name}) 不是支持类型: ({row.Type}), Key: ({row.Key}) {table.Path}, (行号: {row.Row + 1})"));
- break;
- }
- }
- }
- void CheckTable(Table table)
- {
- if (string.IsNullOrEmpty(table.Name))
- AddException(new Exception($"配置表的名字为null, Path: {table.Path}"));
- if (table.FieldInfos == null || table.FieldInfos.Length == 0)
- AddException(new Exception($"配置表结构错误, Path: {table.Path}"));
- // 表结构类型检测
- foreach (var fieldInfo in table.FieldInfos)
- {
- if (fieldInfo.Isi18nField)
- {
- if (fieldInfo.FieldType != FieldTypeDefine.String)
- AddException(new Exception($"配置表字段类型({fieldInfo.FieldType})不是string还想搞i18n, 疯了吧, 字段: ({fieldInfo.FieldName}), {table.Path}"));
- }
- // 基础类型过滤
- if (PRIMITIVE_TYPES.Contains(fieldInfo.FieldType))
- continue;
- foreach (var metadataTable in Context.Blackboard.MetadataTables)
- {
- if (!metadataTable.Structs.ContainsKey(fieldInfo.FieldType))
- AddException(new Exception($"Metadata不存在类型{fieldInfo.FieldType}, 所在表: {table.Path}"));
- }
- }
- // PrimaryKey检测
- if (table.GetPKFieldInfo() == null)
- AddException(new Exception($"配置表({table.Name}), 表结构未含PrimaryKey的字段. {table.Path}"));
- // Cells 合法性检测
- var primaryKeys = new HashSet<string>();
- for (int i = 0, rowCount = table.Rows.Count; i < rowCount; i++)
- {
- var row = table.Rows[i];
- for (int j = 0, colCount = row.Cells.Count; j < colCount; j++)
- {
- var cell = row.Cells[j];
- int excelRow = cell.Row + 1;
- int excelCol = cell.Column + 1;
- string fieldType = cell.FieldInfo.FieldType;
- if (cell.FieldInfo.IsPrimaryKey)
- { // 主键
- if (!primaryKeys.Add(cell.Value))
- {
- AddException(new Exception($"配置表({table.Name}) 主键重复 字段内容: ({cell.Value}) 与 字段类型: ({fieldType})。 {table.Path} (行: {excelRow}, 列: {excelCol})"));
- continue;
- }
- }
- bool valid;
- if (cell.FieldInfo.IsRepeated)
- {
- valid = !string.IsNullOrEmpty(cell.Value) && cell.Value.IsJsonString();
- }
- else if (Context.Blackboard.TryGetMetadata(fieldType, out var metadata) && metadata.StructType == MetadataStructType.Enum)
- { // 枚举值
- if (string.IsNullOrEmpty(cell.Value))
- {
- valid = false;
- }
- else if (int.TryParse(cell.Value, out var intVal))
- { // 存的是数字
- var field = metadata.Fields.First(a => a.EnumIntVal == intVal);
- valid = field != null;
- }
- else
- {
- // 存的是枚举的名字
- var field = metadata.Fields.First(a => a.Name.Equals(cell.Value, StringComparison.OrdinalIgnoreCase));
- valid = field != null;
- }
- }
- else
- {
- valid = fieldType switch
- {
- // 基础类型转换检测
- FieldTypeDefine.Int => int.TryParse(cell.Value, out _),
- FieldTypeDefine.Long => long.TryParse(cell.Value, out _),
- FieldTypeDefine.Float => float.TryParse(cell.Value, out _),
- FieldTypeDefine.Double => double.TryParse(cell.Value, out _),
- // 字符串可能有空的情况,不需要进行格子内容判空检测
- FieldTypeDefine.String => true,
- // 这里只做了非空metadata kson检测
- _ => !string.IsNullOrEmpty(cell.Value) && cell.Value.IsJsonString(),// metadata
- };
- }
- if (!valid)
- AddException(new Exception($"配置表({table.Name}) 字段内容: ({cell.Value}) 与 字段类型: ({fieldType}) 转换失败。 {table.Path} (行: {excelRow}, 列: {excelCol})"));
- }
- }
- primaryKeys.Clear();
- }
- }
- }
|