| | | 1 | | namespace Services.DocumentationServices; |
| | | 2 | | |
| | | 3 | | /// <summary> |
| | | 4 | | /// Шаблоны user story и expected behavior для реестра function-inventory. |
| | | 5 | | /// Дублирует логику <c>Scripts/generate-function-inventory.py</c>. |
| | | 6 | | /// </summary> |
| | | 7 | | public static class FunctionInventoryMetadata |
| | | 8 | | { |
| | 1 | 9 | | private static readonly IReadOnlyDictionary<string, string> StoryTemplates = |
| | 1 | 10 | | new Dictionary<string, string>(StringComparer.Ordinal) |
| | 1 | 11 | | { |
| | 1 | 12 | | ["Orders"] = "Как менеджер заказов, я хочу {action}, чтобы управлять списком заказов Ozon.", |
| | 1 | 13 | | ["Transaction"] = "Как оператор, я хочу {action}, чтобы просматривать журнал транзакций.", |
| | 1 | 14 | | ["BitrixStock"] = "Как менеджер остатков, я хочу {action}, чтобы синхронизировать склад Ozon ↔ Bitrix.", |
| | 1 | 15 | | ["BackgroundJobs"] = "Как администратор, я хочу {action}, чтобы настраивать автоматические фоновые задания." |
| | 1 | 16 | | ["TransactionJobs"] = "Как администратор, я хочу {action}, чтобы управлять автоматической обработкой транзак |
| | 1 | 17 | | ["Labels"] = "Как логист, я хочу {action}, чтобы печатать этикетки Ozon.", |
| | 1 | 18 | | ["WarehouseStock"] = "Как кладовщик, я хочу {action}, чтобы сверять остатки со Google Sheets.", |
| | 1 | 19 | | ["AccessProfiles"] = "Как администратор, я хочу {action}, чтобы настроить профиль доступа пользователя.", |
| | 1 | 20 | | ["User"] = "Как администратор, я хочу {action}, чтобы управлять учётными записями.", |
| | 1 | 21 | | ["Catalog"] = "Как бухгалтер, я хочу {action}, чтобы просмотреть справочник 1С Fresh через OData.", |
| | 1 | 22 | | ["Document"] = "Как бухгалтер, я хочу {action}, чтобы просмотреть документ 1С Fresh через OData.", |
| | 1 | 23 | | }; |
| | | 24 | | |
| | 1 | 25 | | private static readonly IReadOnlyDictionary<string, string> ExpectedByAction = |
| | 1 | 26 | | new Dictionary<string, string>(StringComparer.Ordinal) |
| | 1 | 27 | | { |
| | 1 | 28 | | ["Index"] = "Возвращает HTML-страницу со списком; требует авторизации; применяет RLS где настроено.", |
| | 1 | 29 | | ["Create"] = "GET — форма создания; POST — сохранение с валидацией и редиректом на список.", |
| | 1 | 30 | | ["Edit"] = "GET — форма редактирования по id; POST — обновление записи.", |
| | 1 | 31 | | ["Delete"] = "POST — удаление по id с antiforgery; редирект или JSON-ответ.", |
| | 1 | 32 | | ["Details"] = "GET — детальная карточка сущности по id; 404 если не найдено.", |
| | 1 | 33 | | }; |
| | | 34 | | |
| | 1 | 35 | | private static readonly HashSet<string> DefaultPostActions = new(StringComparer.Ordinal) |
| | 1 | 36 | | { |
| | 1 | 37 | | "Create", "Edit", "Delete", "Update", "Save", "Submit", |
| | 1 | 38 | | }; |
| | | 39 | | |
| | | 40 | | public static string BuildActionId(string area, string controller, string action) => |
| | 1 | 41 | | $"{area}:{controller}:{action}"; |
| | | 42 | | |
| | | 43 | | public static string InferUserStory(string controller, string action) |
| | | 44 | | { |
| | 3 | 45 | | if (StoryTemplates.TryGetValue(controller, out var template)) |
| | 2 | 46 | | return template.Replace("{action}", action.ToLowerInvariant(), StringComparison.Ordinal); |
| | | 47 | | |
| | 1 | 48 | | return $"Как пользователь системы, я хочу выполнить {controller}/{action}, чтобы получить ожидаемый результат."; |
| | | 49 | | } |
| | | 50 | | |
| | | 51 | | public static string InferExpectedBehavior(string action) => |
| | 3 | 52 | | ExpectedByAction.TryGetValue(action, out var expected) |
| | 3 | 53 | | ? expected |
| | 3 | 54 | | : "HTTP-ответ согласно коду action; ошибки — через ModelState, NotFound или JSON error."; |
| | | 55 | | |
| | | 56 | | public static string InferHttpMethod(string? explicitVerb, string action) |
| | | 57 | | { |
| | 4 | 58 | | if (!string.IsNullOrWhiteSpace(explicitVerb)) |
| | 2 | 59 | | return explicitVerb; |
| | | 60 | | |
| | 2 | 61 | | return DefaultPostActions.Contains(action) ? "POST" : "GET"; |
| | | 62 | | } |
| | | 63 | | } |