Загрузка данных
import java.util.stream.Collectors; // ИЗМЕНЕНО: добавлен импорт Collectors
@Slf4j
@Service
public class CsvGeneratorService {
private static final int DEFAULT_SLA = 3;
private static final String STATIC_URL = "/ic/ufs/corp-retail/{{version}}/js/app/corp-retail-mf.js";
private static final String STATIC_OPERATION_ID = "UC00_StaticJS_1_WEB";
private static final String GET_CONTACTS_OPERATION_ID = "UC29_GET_contacts_WEB";
private static final int STATIC_INTENSITY_DEFAULT = 300000;
private static final int STATIC_SLA_DEFAULT = 1;
@Autowired
private UriNormalizer uriNormalizer;
private String urlPrefix;
private String projectType;
private Set<String> lastExtraInScript = new HashSet<>();
public Set<String> getLastExtraInScript() {
return lastExtraInScript;
}
public List<CsvRow> generateProfile(Map<String, List<String>> urlToOpIds,
List<ProfileEntry> profileEntries,
Map<String, OldProfileData> lastProfileData,
String outputFolder,
String outputFilename,
String requirement,
String urlPrefix,
String projectType) {
this.urlPrefix = urlPrefix;
this.projectType = projectType;
// Маппинг: "method|normalizedUri" -> operationId из прошлого профиля
Map<String, String> oldUriToOpId = new HashMap<>();
if (lastProfileData != null) {
for (Map.Entry<String, OldProfileData> oldEntry : lastProfileData.entrySet()) {
String oldOpId = oldEntry.getKey();
OldProfileData oldData = oldEntry.getValue();
if (oldData.getFullService() != null && oldData.getFullService().contains(" ")) {
String oldMethod = oldData.getFullService().substring(0, oldData.getFullService().indexOf(' '));
String oldUri = oldData.getFullService().substring(oldData.getFullService().indexOf(' ') + 1);
String normalizedOldUri = uriNormalizer.normalizeUri(oldUri, projectType);
oldUriToOpId.put(oldMethod.toUpperCase() + "|" + normalizedOldUri, oldOpId);
}
}
}
List<CsvRow> rowsForCsv = new ArrayList<>(); // для CSV (intensity > 0)
List<CsvRow> allRowsForExcel = new ArrayList<>(); // для Excel (все операции)
// ========== 1. Статический запрос (ТОЛЬКО ДЛЯ WEB) ==========
if ("WEB".equals(projectType)) {
int staticIntensity = STATIC_INTENSITY_DEFAULT;
int staticSla = STATIC_SLA_DEFAULT;
if (lastProfileData.containsKey(STATIC_OPERATION_ID)) {
OldProfileData data = lastProfileData.get(STATIC_OPERATION_ID);
staticIntensity = data.getIntensity();
staticSla = data.getSla();
log.info(" Найдена в прошлом профиле {}: интенсивность = {}, SLA = {} сек",
STATIC_OPERATION_ID, staticIntensity, staticSla);
} else {
log.info(" ⚠️ Не найдена в прошлом профиле {}, используем значения по умолчанию: инт = {}, SLA = {} сек",
STATIC_OPERATION_ID, staticIntensity, staticSla);
}
CsvRow staticRow = new CsvRow(
STATIC_OPERATION_ID,
STATIC_URL,
"GET",
staticIntensity,
staticSla,
STATIC_OPERATION_ID,
requirement,
""
);
rowsForCsv.add(staticRow);
allRowsForExcel.add(staticRow);
log.info(" Добавлен статический запрос: {} (инт: {}, SLA: {})",
STATIC_OPERATION_ID, staticIntensity, staticSla);
} else if ("MOBILE".equals(projectType)) {
log.debug(" Статический запрос не добавляется для проекта: {}", projectType);
} else if ("PPRB".equals(projectType)) {
log.debug(" Статический запрос не добавляется для проекта: {}", projectType);
}
// ========== 2. Собираем URI из профиля аналитика ==========
Map<String, ProfileEntry> profileEntryMap = new HashMap<>();
for (ProfileEntry entry : profileEntries) {
if (entry.getIntensity() > 0) {
String key;
if ("PPRB".equals(projectType)) {
String normalizedUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
key = entry.getMethod() + "|" + normalizedUri;
} else {
key = entry.getMethod() + "|" + entry.getUri();
}
profileEntryMap.put(key, entry);
}
}
// ========== 3. Исключаем статический URL из проверок (только для Web) ==========
int totalScriptOperations = 0;
for (List<String> opIds : urlToOpIds.values()) {
totalScriptOperations += opIds.size();
}
Map<String, List<String>> filteredUrlToOpIds = new HashMap<>();
boolean removedStatic = false;
for (Map.Entry<String, List<String>> entry : urlToOpIds.entrySet()) {
if ("WEB".equals(projectType) && entry.getKey().equals(STATIC_URL)) {
removedStatic = true;
continue;
}
filteredUrlToOpIds.put(entry.getKey(), entry.getValue());
}
int scriptOperationsAfterRemoval = 0;
for (List<String> opIds : filteredUrlToOpIds.values()) {
scriptOperationsAfterRemoval += opIds.size();
}
log.info(" Всего операций в скрипте: {}", totalScriptOperations);
if (removedStatic) {
log.info(" Удален статический запрос: {}", STATIC_URL);
log.info(" Скрипт после исключения статики: {} операций", scriptOperationsAfterRemoval);
} else {
log.info(" Скрипт: {} операций", scriptOperationsAfterRemoval);
}
// ========== 4. Находим операции, которые есть в скрипте, но НЕТ в профиле ==========
Set<String> scriptKeys = new HashSet<>();
for (Map.Entry<String, List<String>> scriptEntry : filteredUrlToOpIds.entrySet()) {
String scriptUri = scriptEntry.getKey();
for (String opId : scriptEntry.getValue()) {
String methodFromOpId = extractMethodFromOperationId(opId);
String key;
if ("PPRB".equals(projectType)) {
String normalizedUri = uriNormalizer.normalizeUri(scriptUri, projectType);
key = methodFromOpId + "|" + normalizedUri;
} else {
key = methodFromOpId + "|" + scriptUri;
}
scriptKeys.add(key);
}
}
Set<String> profileKeys = new HashSet<>();
for (ProfileEntry entry : profileEntries) {
if (entry.getIntensity() > 0) {
String key;
if ("PPRB".equals(projectType)) {
String normalizedUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
key = entry.getMethod() + "|" + normalizedUri;
} else {
key = entry.getMethod() + "|" + entry.getUri();
}
profileKeys.add(key);
}
}
Set<String> extraInScript = new HashSet<>(scriptKeys);
extraInScript.removeAll(profileKeys);
this.lastExtraInScript = extraInScript;
if (!extraInScript.isEmpty()) {
log.info("\n⚠️ ВНИМАНИЕ! Операции, которые есть в скрипте, но ОТСУТСТВУЮТ в профиле аналитика:");
log.info(" (нужно удалить из скрипта)");
log.info("========================================");
for (String key : extraInScript) {
String[] parts = key.split("\\|", 2);
String method = parts[0];
String uri = parts[1];
List<String> opIds = filteredUrlToOpIds.get(uri);
log.info(" {} {} → {}", method, uri, opIds);
}
log.info("========================================");
log.info(" Всего лишних операций в скрипте: {}\n", extraInScript.size());
} else {
log.info("\n Все операции из скрипта присутствуют в профиле аналитика\n");
}
// ========== 5. Находим операции, которые есть в профиле, но НЕТ в скрипте ==========
Set<String> extraInProfile = new HashSet<>(profileKeys);
extraInProfile.removeAll(scriptKeys);
if (!extraInProfile.isEmpty()) {
log.info("\n СПИСОК: Операции, которые есть в профиле аналитика, но ОТСУТСТВУЮТ в скрипте:");
log.info(" (будут созданы как новые запросы)");
log.info("========================================");
for (String key : extraInProfile) {
String[] parts = key.split("\\|", 2);
String method = parts[0];
String uri = parts[1];
log.info(" {} {} (интенсивность: {})", method, uri,
profileEntryMap.get(key).getIntensity());
}
log.info("========================================");
log.info(" Всего новых операций (нужно создать): {}\n", extraInProfile.size());
} else {
log.info("\n Все операции из профиля аналитика присутствуют в скрипте\n");
}
log.debug("\n=== Сравнение с нормализацией ===");
for (ProfileEntry entry : profileEntries) {
if (entry.getIntensity() == 0) continue;
String normalizedProfileUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
boolean found = false;
String matchedScriptUri = null;
for (String scriptUri : filteredUrlToOpIds.keySet()) {
String normalizedScriptUri = uriNormalizer.normalizeUri(scriptUri, projectType);
if (normalizedScriptUri.equals(normalizedProfileUri)) {
found = true;
matchedScriptUri = scriptUri;
break;
}
}
if (!found) {
log.warn(" НЕ СОВПАДАЕТ: Профиль='{}' -> нормализовано='{}'",
entry.getUri(), normalizedProfileUri);
}
else {
log.debug(" СОВПАДАЕТ: Профиль='{}' -> Скрипт='{}'", entry.getUri(), matchedScriptUri);
}
}
log.info("========================================\n");
// ========== 6. Обрабатываем операции из профиля аналитика ==========
Set<String> usedOpIds = new HashSet<>();
for (List<String> opIds : filteredUrlToOpIds.values()) {
usedOpIds.addAll(opIds);
}
if ("WEB".equals(projectType)) {
usedOpIds.add(STATIC_OPERATION_ID);
}
Set<String> matchedOpIds = new HashSet<>();
int nextUcNum = getNextUcNumber(usedOpIds);
int matchedCount = 0;
int newCount = 0;
int validProfileEntriesCount = 0;
log.debug("--- Обработка операций из профиля аналитика ---");
for (ProfileEntry entry : profileEntries) {
log.debug(" Обработка entry: метод={}, uri={}, интенсивность={}", entry.getMethod(), entry.getUri(), entry.getIntensity());
if (entry.getIntensity() > 0) {
validProfileEntriesCount++;
} else {
log.info(" Операция с нулевой интенсивностью: {} {} - будет покрашена красным в Excel",
entry.getMethod(), entry.getUri());
}
String operationId = null;
String profileMethod = entry.getMethod();
if (filteredUrlToOpIds.containsKey(entry.getUri())) {
List<String> opIds = filteredUrlToOpIds.get(entry.getUri());
for (String opId : opIds) {
if (!matchedOpIds.contains(opId)) {
String opMethod = extractMethodFromOperationId(opId);
if (opMethod.equals(profileMethod)) {
operationId = opId;
matchedOpIds.add(opId);
break;
}
}
}
if (operationId == null) {
for (String opId : opIds) {
if (!matchedOpIds.contains(opId)) {
operationId = opId;
matchedOpIds.add(opId);
if (entry.getIntensity() > 0) {
log.warn(" ⚠️ Метод не совпадает! Профиль: {}, operationId: {} (URI: {})",
profileMethod, opId, entry.getUri());
}
break;
}
}
}
if (operationId != null) {
if (entry.getIntensity() > 0) {
if (opIds.size() > 1) {
log.debug(" Найден в скрипте (один из {}): {} {} → {} (инт: {})",
opIds.size(), profileMethod, entry.getUri(), operationId, entry.getIntensity());
} else {
log.debug(" Найден в скрипте: {} {} → {} (инт: {})",
profileMethod, entry.getUri(), operationId, entry.getIntensity());
}
matchedCount++;
}
} else {
// Все operation_id для URI уже использованы — ищем в прошлом профиле
String normalizedEntryUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
String oldKey = entry.getMethod().toUpperCase() + "|" + normalizedEntryUri;
String existingOpId = oldUriToOpId.get(oldKey);
if (existingOpId != null && !usedOpIds.contains(existingOpId)) {
operationId = existingOpId;
usedOpIds.add(operationId);
if (entry.getIntensity() > 0) {
log.info(" Восстановлен из прошлого профиля (URI в скрипте): {} {} → {} (инт: {})",
profileMethod, entry.getUri(), operationId, entry.getIntensity());
matchedCount++;
}
} else {
if (entry.getIntensity() > 0) {
log.warn(" ⚠️ Все operation_id для URI {} уже использованы. Создаём новый.", entry.getUri());
}
operationId = generateNewOperationId(entry, usedOpIds, nextUcNum);
nextUcNum = extractUcNumber(operationId) + 1;
usedOpIds.add(operationId);
if (entry.getIntensity() > 0) {
newCount++;
}
}
}
} else {
// URI не найден в скрипте — ищем в прошлом профиле
String normalizedEntryUri = uriNormalizer.normalizeUri(entry.getUri(), projectType);
String oldKey = entry.getMethod().toUpperCase() + "|" + normalizedEntryUri;
String existingOpId = oldUriToOpId.get(oldKey);
if (existingOpId != null && !usedOpIds.contains(existingOpId)) {
operationId = existingOpId;
usedOpIds.add(operationId);
if (entry.getIntensity() > 0) {
log.info(" Восстановлен из прошлого профиля: {} {} → {} (инт: {})",
profileMethod, entry.getUri(), operationId, entry.getIntensity());
matchedCount++;
} else {
log.info(" Восстановлен ID из прошлого профиля для нулевой операции: {} {} → {} (инт: 0)",
profileMethod, entry.getUri(), operationId);
}
} else {
operationId = generateNewOperationId(entry, usedOpIds, nextUcNum);
nextUcNum = extractUcNumber(operationId) + 1;
usedOpIds.add(operationId);
if (entry.getIntensity() > 0) {
log.info(" Создан новый: {} {} → {} (инт: {})",
profileMethod, entry.getUri(), operationId, entry.getIntensity());
newCount++;
} else {
log.info(" Создан ID для нулевой операции: {} {} → {} (инт: 0)",
profileMethod, entry.getUri(), operationId);
}
}
}
int sla = DEFAULT_SLA;
if (lastProfileData.containsKey(operationId)) {
sla = lastProfileData.get(operationId).getSla();
}
String fullUri;
String uriFromProfile = entry.getUri();
if ("PPRB".equals(projectType)) {
// Для PPRB не добавляем префикс и не меняем URI
fullUri = uriFromProfile;
} else if (uriFromProfile.startsWith("/ic/ufs/corp-retail/") ||
uriFromProfile.startsWith("/ufs/mobile/")) {
fullUri = uriFromProfile;
} else if (uriFromProfile.startsWith("/")) {
fullUri = urlPrefix + uriFromProfile;
} else {
fullUri = urlPrefix + "/" + uriFromProfile;
}
CsvRow row = new CsvRow(
operationId,
fullUri,
entry.getMethod(),
entry.getIntensity(), // может быть 0
sla,
operationId,
requirement,
""
);
// ИЗМЕНЕНО: добавляем ВСЕ операции в список для Excel
allRowsForExcel.add(row);
// ИЗМЕНЕНО: в CSV добавляем ТОЛЬКО операции с интенсивностью > 0
if (entry.getIntensity() > 0) {
rowsForCsv.add(row);
log.info(" Добавлена в CSV и Excel: {} -> интенсивность={}", operationId, entry.getIntensity());
} else {
log.info(" Добавлена ТОЛЬКО в Excel (не в CSV): {} -> интенсивность=0", operationId);
}
}
// ========== СИНХРОНИЗАЦИЯ СТАТИКИ С UC29 ==========
if ("WEB".equals(projectType)) {
Optional<CsvRow> contactsRow = allRowsForExcel.stream()
.filter(row -> GET_CONTACTS_OPERATION_ID.equals(row.getName()))
.findFirst();
Optional<CsvRow> staticRow = allRowsForExcel.stream()
.filter(row -> STATIC_OPERATION_ID.equals(row.getName()))
.findFirst();
if (contactsRow.isPresent() && staticRow.isPresent()) {
int contactsIntensity = contactsRow.get().getIntensity();
CsvRow oldStaticRow = staticRow.get();
if (contactsIntensity != oldStaticRow.getIntensity()) {
CsvRow updatedStaticRow = new CsvRow(
oldStaticRow.getName(),
oldStaticRow.getUri(),
oldStaticRow.getMethod(),
contactsIntensity,
oldStaticRow.getSla(),
oldStaticRow.getTransactionName(),
oldStaticRow.getRequirement(),
oldStaticRow.getComment()
);
int index = allRowsForExcel.indexOf(oldStaticRow);
allRowsForExcel.set(index, updatedStaticRow);
// ИЗМЕНЕНО: также обновляем в rowsForCsv, если там есть
int csvIndex = rowsForCsv.indexOf(oldStaticRow);
if (csvIndex != -1) {
rowsForCsv.set(csvIndex, updatedStaticRow);
}
log.info(" Синхронизирована интенсивность: {} -> {} (как у {})",
STATIC_OPERATION_ID, contactsIntensity, GET_CONTACTS_OPERATION_ID);
}
}
}
// ========== 7. Сортировка ==========
// Сортировка для Excel
List<CsvRow> rowsToSort;
boolean hasStatic = "WEB".equals(projectType) && allRowsForExcel.size() > 0 &&
allRowsForExcel.get(0).getName().equals(STATIC_OPERATION_ID);
if (hasStatic) {
rowsToSort = allRowsForExcel.subList(1, allRowsForExcel.size());
log.debug("Сортируем {} строк для Excel (без статики)", rowsToSort.size());
} else {
rowsToSort = allRowsForExcel;
log.debug("Сортируем все {} строк для Excel", rowsToSort.size());
}
rowsToSort.sort((row1, row2) -> {
int num1 = extractUcNumber(row1.getName());
int num2 = extractUcNumber(row2.getName());
return Integer.compare(num1, num2);
});
List<CsvRow> finalRowsForExcel;
if (hasStatic) {
finalRowsForExcel = new ArrayList<>();
finalRowsForExcel.add(allRowsForExcel.get(0));
finalRowsForExcel.addAll(rowsToSort);
} else {
finalRowsForExcel = rowsToSort;
}
// ========== 8. Сортировка для CSV ==========
List<CsvRow> csvRowsToSort;
boolean hasStaticCsv = "WEB".equals(projectType) && rowsForCsv.size() > 0 &&
rowsForCsv.get(0).getName().equals(STATIC_OPERATION_ID);
if (hasStaticCsv) {
csvRowsToSort = rowsForCsv.subList(1, rowsForCsv.size());
log.debug("Сортируем {} строк для CSV (без статики)", csvRowsToSort.size());
} else {
csvRowsToSort = rowsForCsv;
log.debug("Сортируем все {} строк для CSV", csvRowsToSort.size());
}
csvRowsToSort.sort((row1, row2) -> {
int num1 = extractUcNumber(row1.getName());
int num2 = extractUcNumber(row2.getName());
return Integer.compare(num1, num2);
});
List<CsvRow> finalRowsForCsv;
if (hasStaticCsv) {
finalRowsForCsv = new ArrayList<>();
finalRowsForCsv.add(rowsForCsv.get(0));
finalRowsForCsv.addAll(csvRowsToSort);
} else {
finalRowsForCsv = csvRowsToSort;
}
// ========== 9. Сохранение CSV и возврат результата ==========
log.info("\n--- Статистика ---");
log.info(" Скрипт (операций): {} операций", scriptOperationsAfterRemoval);
log.info(" Профиль аналитика (инт > 0): {} операций", validProfileEntriesCount);
log.info(" Нужно удалить из скрипта: {} операций", extraInScript.size());
log.info(" Нужно добавить в скрипт: {} операций", extraInProfile.size());
log.info(" Найдено совпадений: {}", matchedCount);
log.info(" Создано новых ID: {}", newCount);
log.info(" Итого в CSV (intensity > 0): {} строк", finalRowsForCsv.size());
log.info(" Итого в Excel (все операции): {} строк", finalRowsForExcel.size());
saveCsv(finalRowsForCsv, outputFolder, outputFilename);
return finalRowsForExcel; }
private String generateNewOperationId(ProfileEntry entry, Set<String> usedOpIds, int nextUcNum) {
String method = entry.getMethod();
String uri = entry.getUri();
String suffix;
if ("MOBILE".equals(projectType)) {
String path = uri;
path = path.replaceFirst("^/ic/ufs/mobile/corp-retail/rest/", "");
path = path.replaceFirst("^/ufs/mobile/corp-retail/rest/", "");
path = path.replaceFirst("^/ufs/", "");
path = path.replaceFirst("^/business-analytics/", "");
path = path.replaceFirst("^/dictionaries/", "");
path = path.replaceFirst("^/", "");
suffix = path.replaceAll("/", "_");
suffix = suffix.replaceAll("-", "_");
suffix = suffix.replaceAll("[^a-zA-Z0-9_]", "_");
suffix = suffix.replaceAll("_+", "_");
suffix = suffix.replaceAll("^_|_$", "");
String methodPrefix = method.toUpperCase();
suffix = methodPrefix + "_" + suffix;
if (suffix.length() > 50) {
suffix = suffix.substring(0, 50);
}
while (true) {
String candidate = String.format("UC%02d_%s_WEB", nextUcNum, suffix);
if (!usedOpIds.contains(candidate)) {
return candidate;
}
nextUcNum++;
}
} else {
String pathAfterPrefix = uri;
if (uri.startsWith(urlPrefix)) {
pathAfterPrefix = uri.substring(urlPrefix.length());
}
if (pathAfterPrefix.startsWith("/")) {
pathAfterPrefix = pathAfterPrefix.substring(1);
}
if (pathAfterPrefix.endsWith("/")) {
pathAfterPrefix = pathAfterPrefix.substring(0, pathAfterPrefix.length() - 1);
}
suffix = pathAfterPrefix.replaceAll("/", "_");
suffix = suffix.replaceAll("-", "_");
suffix = suffix.replaceAll("[^a-zA-Z0-9_]", "_");
suffix = suffix.replaceAll("_+", "_");
suffix = suffix.replaceAll("^_|_$", "");
while (true) {
String candidate = String.format("UC%02d_%s_%s_WEB", nextUcNum, method, suffix);
if (!usedOpIds.contains(candidate)) {
return candidate;
}
nextUcNum++;
}
}
}
private int getNextUcNumber(Set<String> usedOpIds) {
if (usedOpIds == null || usedOpIds.isEmpty()) {
return 1;
}
int maxNum = 0;
for (String opId : usedOpIds) {
int num = extractUcNumber(opId);
if (num > maxNum && num < 1000) {
maxNum = num;
}
}
return maxNum + 1;
}
private int extractUcNumber(String operationId) {
if (operationId == null || !operationId.startsWith("UC")) {
return 0;
}
try {
int underscoreIndex = operationId.indexOf('_', 2);
if (underscoreIndex == -1) {
return 0;
}
return Integer.parseInt(operationId.substring(2, underscoreIndex));
} catch (Exception e) {
return 0;
}
}
private void saveCsv(List<CsvRow> rows, String outputFolder, String outputFilename) {
File folder = new File(outputFolder);
if (!folder.exists()) {
folder.mkdirs();
}
File outputFile = new File(folder, outputFilename);
try (PrintWriter writer = new PrintWriter(new FileWriter(outputFile, StandardCharsets.UTF_8))) {
writer.println("Название;Интенсивность выполнения;Требование к времени отклика;Название транзакции;Требование;Комментарий");
for (CsvRow row : rows) {
writer.printf("%s;%d;%d;%s;%s;%s%n",
row.getName(),
row.getIntensity(),
row.getSla(),
row.getTransactionName(),
row.getRequirement(),
row.getComment()
);
}
log.info("\n Профиль сохранён: {}", outputFile.getAbsolutePath());
log.info(" Всего строк в CSV: {}", rows.size());
} catch (IOException e) {
log.error("Ошибка при сохранении CSV: {}", e.getMessage());
}
}
private String extractMethodFromOperationId(String operationId) {
if (operationId == null) return "GET";
if (operationId.contains("_POST_") || operationId.contains("POST_")) {
return "POST";
}
if (operationId.contains("_GET_") || operationId.contains("GET_")) {
return "GET";
}
String[] parts = operationId.split("_");
for (String part : parts) {
if (part.equalsIgnoreCase("POST")) {
return "POST";
}
if (part.equalsIgnoreCase("GET")) {
return "GET";
}
}
return "GET";
}
}