");
+ sb.AppendLine($"
Файл: {fileGroup.Key}
");
+
+ var groupedBySeverity = fileGroup
+ .GroupBy(v => v.Severity)
+ .OrderByDescending(g => g.Key);
+
+ foreach (var severityGroup in groupedBySeverity)
+ {
+ string severityClass = severityGroup.Key switch
+ {
+ RuleViolationSeverity.Critical => "critical",
+ RuleViolationSeverity.Warning => "warning",
+ RuleViolationSeverity.Info => "info",
+ _ => ""
+ };
+
+ sb.AppendLine($"
");
+ sb.AppendLine($"
{severityGroup.Key}
");
+ sb.AppendLine("
");
+ sb.AppendLine("");
+ sb.AppendLine("| # | Строка | Колонка | Правило | Описание |
");
+ sb.AppendLine("");
+ sb.AppendLine("");
+
+ int rowIndex = 1;
+ foreach (var v in severityGroup
+ .OrderBy(x => x.Line)
+ .ThenBy(x => x.Column))
+ {
+ sb.AppendLine($"| {rowIndex} | {v.Line} | {v.Column} | {v.RuleName} | {v.Text} |
");
+ rowIndex++;
+ }
+
+ sb.AppendLine("");
+ sb.AppendLine("
");
+ sb.AppendLine("
");
+ }
+
+ sb.AppendLine("
");
+ fileIndex++;
+ }
+
+ // Табы снизу
+ sb.AppendLine("");
+ fileIndex = 0;
+ foreach (var fileGroup in groupedByFile)
+ {
+ sb.AppendLine($"
{fileGroup.Key}
");
+ fileIndex++;
+ }
+ sb.AppendLine("
");
+
+ // JS для переключения
+ sb.AppendLine("");
+
+ sb.AppendLine("");
+ sb.AppendLine("");
+
+ return sb.ToString();
+ }
+}
diff --git a/SQLLinter/Infrastructure/Reporters/MarkdownFileReporter.cs b/SQLLinter/Infrastructure/Reporters/MarkdownFileReporter.cs
new file mode 100644
index 0000000..adcd108
--- /dev/null
+++ b/SQLLinter/Infrastructure/Reporters/MarkdownFileReporter.cs
@@ -0,0 +1,53 @@
+using System.Text;
+
+namespace SQLLinter.Infrastructure.Reporters;
+
+public class MarkdownFileReporter : FileReporter
+{
+ public override string GetContent()
+ {
+ var violations = Violations;
+ if (violations.Count == 0)
+ {
+ return "_Нет нарушений_";
+ }
+
+ // Группировка по файлу
+ var groupedByFile = violations
+ .GroupBy(v => v.FileName)
+ .OrderBy(g => g.Key); // сортировка файлов по имени
+
+ var sb = new StringBuilder();
+
+ foreach (var fileGroup in groupedByFile)
+ {
+ sb.AppendLine($"## Файл: {fileGroup.Key}");
+ sb.AppendLine();
+
+ // Группировка по severity внутри файла
+ var groupedBySeverity = fileGroup
+ .GroupBy(v => v.Severity)
+ .OrderByDescending(g => g.Key); // сначала Error, потом Warning, потом Info
+
+ foreach (var severityGroup in groupedBySeverity)
+ {
+ sb.AppendLine($"### {severityGroup.Key}");
+ sb.AppendLine();
+ sb.AppendLine("| Строка | Колонка | Правило | Описание |");
+ sb.AppendLine("|--------|---------|---------|----------|");
+
+ foreach (var v in severityGroup
+ .OrderBy(x => x.Line)
+ .ThenBy(x => x.Column))
+ {
+ sb.AppendLine($"| {v.Line} | {v.Column} | {v.RuleName} | {v.Text} |");
+ }
+
+ sb.AppendLine();
+ }
+ }
+
+ return sb.ToString();
+
+ }
+}
\ No newline at end of file
diff --git a/SQLLinter/Infrastructure/Reporters/Reporter.cs b/SQLLinter/Infrastructure/Reporters/Reporter.cs
new file mode 100644
index 0000000..04f4b39
--- /dev/null
+++ b/SQLLinter/Infrastructure/Reporters/Reporter.cs
@@ -0,0 +1,41 @@
+using SQLLinter.Common;
+using SQLLinter.Infrastructure.Rules.RuleViolations;
+using System.Collections.Concurrent;
+
+namespace SQLLinter.Infrastructure.Reporters;
+
+public class Reporter : IReporter
+{
+ private readonly List