Удаление пакета с мониторинга

This commit is contained in:
ialbert 2025-11-19 17:47:20 +03:00
parent e18e1f3724
commit d0f9459ce8
14 changed files with 292 additions and 61 deletions

View file

@ -0,0 +1,27 @@
package ru.nbch.credit_tracker.component.comparator;
import java.util.Comparator;
import java.util.function.ToIntFunction;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
public class DeletePackageStatusComparator {
public static Comparator<UserPackage> INSTANCE = Comparator
.comparingInt((ToIntFunction<UserPackage>) userPackage -> statusToInt(userPackage.getStatus()))
.thenComparingLong(UserPackage::getId);
public static int statusToInt(String str) {
try {
Status status = Status.valueOf(str);
return switch (status) {
case ON_MONITORING -> 1;
case DELETED -> 0;
default -> -1;
};
} catch (Throwable e) {
return -1;
}
}
}

View file

@ -1,6 +1,10 @@
package ru.nbch.credit_tracker.config.xml;
import jakarta.xml.bind.JAXBException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
@ -12,12 +16,15 @@ import ru.nbch.credit_tracker.entities.user_response.errors.MonitoringError;
import ru.nbch.credit_tracker.entities.user_response.success.MonitoringSetupResponse;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import ru.nbch.credit_tracker.service.xml.jaxb.XmlProcessorImplAnyClasses;
import ru.nbch.credit_tracker.service.xml.stax.*;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Supplier;
import ru.nbch.credit_tracker.service.xml.stax.MonitoringReportErrorsStaxtReader;
import ru.nbch.credit_tracker.service.xml.stax.MonitoringRequestStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.MonitoringSignalResponseStaxXmlReader;
import ru.nbch.credit_tracker.service.xml.stax.OnlineDownloadReportPersonsStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.SgnlReportDeletedStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.SgnlReportStatusStaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxReader;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.service.xml.stax.XmlProcessorStaxImpl;
@Configuration
public class XmlConfig {
@ -39,6 +46,7 @@ public class XmlConfig {
public StaxXmlProcessor staxXmlProcessor() {
List<Supplier<StaxReader<?>>> staxReader = new ArrayList<>();
staxReader.add(SgnlReportStatusStaxReader::new);
staxReader.add(SgnlReportDeletedStaxReader::new);
staxReader.add(MonitoringRequestStaxReader::new);
staxReader.add(OnlineDownloadReportPersonsStaxReader::new);
staxReader.add(MonitoringSignalResponseStaxXmlReader::new);

View file

@ -64,7 +64,12 @@ public class SignalControllerV3 {
@PostMapping(value = "/delete", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(@RequestParam("username") String username, @RequestParam("packageName") String packageName) {
try {
return signalFacadeV3.deletePackage(username, packageName);
} catch (Exception e) {
throwOrWrap(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
return null;
}
}
@GetMapping(value = "/report_list", params = {"username", "packageName"}, produces = MediaType.APPLICATION_XML_VALUE)

View file

@ -0,0 +1,18 @@
package ru.nbch.credit_tracker.entities.external_response.list_package;
import lombok.Getter;
import lombok.Setter;
@Setter
@Getter
public class SgnlReportDeleted {
private Integer deleteFlag;
public Integer getDeleteFlag() {
return deleteFlag;
}
public void setDeleteFlag(Integer deleteFlag) {
this.deleteFlag = deleteFlag;
}
}

View file

@ -21,7 +21,12 @@ public enum SignalError implements IEnumKeyCode {
ERROR_019("019", "У данного пользователя не найдено ни одного пакета"),
ERROR_020("020", "Пакет {} был отклонён при установке на мониторинг. Отчеты не формируются."),
ERROR_021("021", "Обработка пакета {} завершилась с ошибкой, отчёты недоступны. Обратитесь в техническую поддержку."),
ERROR_022("022", "Пакет {} снят с мониторинга, отчёты больше недоступны.");
ERROR_022("022", "Пакет {} снят с мониторинга, отчёты больше недоступны."),
ERROR_023("023", "Пакет {} находится в обработке, удаление невозможно. Дождитесь его установки на мониторинг и повторите попытку."),
ERROR_024("024", "Пакет {} отклонен или при его установке произошла ошибка. Удаление невозможно."),
ERROR_025("025", "Пакет {} уже был удален ранее, удаление невозможно."),
ERROR_026("026", "Пакет {} обновляется. Удаление невозможно. Дождитесь завершения обновления и повторите попытку."),
;
private final String code;

View file

@ -4,6 +4,8 @@ public enum Status {
ON_MONITORING, // пакет стоит на мониторинге в оф. сервисе
REJECTED, // отклонен на стороне НБКИ
PROCESSING, // идет поиск телефона и расчет флагов по пакету
PROCESSING_REPEATED,
ACCEPTED,
DELETED, // пакет удален
ERROR; // произошла ошибка при постановке пакета на мониторинг в оф. сервис
}

View file

@ -13,6 +13,7 @@ import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import ru.nbch.credit_tracker.component.MonitoringRequestValidator;
import ru.nbch.credit_tracker.component.comparator.DeletePackageStatusComparator;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.entities.user_request.active_packages.MonitoringActivePackagesRequest;
@ -36,6 +37,7 @@ import ru.nbch.credit_tracker.service.scoring.AuthorizationService;
import ru.nbch.credit_tracker.service.signal.SignalRestService;
import ru.nbch.credit_tracker.service.task.PipelinePackageManager;
import ru.nbch.credit_tracker.utils.CTUtil;
import ru.nbch.credit_tracker.utils.EnumUtil;
import ru.nbch.credit_tracker.utils.LogUtil;
import ru.nbch.credit_tracker.utils.ZipUtils;
import static ru.nbch.credit_tracker.utils.error.ExceptionUtils.clientExcp;
@ -182,28 +184,43 @@ public class SignalFacadeV3 {
}
public ResponseEntity<MonitoringPackageDeleteResponse> deletePackage(String username, String packageName) {
try {
if (!signalService.isPackageExists(username, packageName)) {
throw clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName);
UserPackage packageToDelete = signalService.findByMemberCodeAndPackageName(username, packageName)
.stream()
.max(DeletePackageStatusComparator.INSTANCE)
.orElseThrow(()
-> clientExcp(SignalError.ERROR_016, XmlErrorFormat.MonitoringPackageDeleteError, packageName));
Status status = EnumUtil.getEnum(Status.class, packageToDelete.getStatus());
if (!Status.ON_MONITORING.equals(status)) {
throw clientExcp(deleteStatusError(status), XmlErrorFormat.MonitoringPackageDeleteError, packageName);
}
boolean successfullyDeleted = signalRestService.deletePackage(username, packageName);
if (!successfullyDeleted) {
throw clientExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, packageName);
}
LocalDateTime deactivatedAt = LocalDateTime.now();
signalService.deletePackage(username, packageName, deactivatedAt);
signalService.deletePackageById(packageToDelete.getId(), deactivatedAt);
MonitoringPackageDeleteResponse response = new MonitoringPackageDeleteResponse();
response.setStatus("Deleted");
response.setPackageName(packageName);
response.setDeactivatedAt(deactivatedAt.toString());
signalRestService.deletePackage(username, packageName);
log.info("Package {} deleted", packageName);
return ResponseEntity.ok(response);
} catch (Exception e) {
if (e instanceof SignalClientException) {
throw (SignalClientException) e;
}
throw serverExcp(SignalError.ERROR_017, XmlErrorFormat.MonitoringPackageDeleteError, e);
}
private SignalError deleteStatusError(Status status) {
if (status == null) return SignalError.ERROR_017;
return switch (status) {
case PROCESSING, ACCEPTED -> SignalError.ERROR_016;
case REJECTED, ERROR -> SignalError.ERROR_024;
case DELETED -> SignalError.ERROR_025;
case PROCESSING_REPEATED -> SignalError.ERROR_026;
default -> throw new IllegalStateException("Unexpected value: " + status);
};
}
public ResponseEntity<MonitoringReportListResponse> getReportList(String username, String packageName) {

View file

@ -1,11 +1,10 @@
package ru.nbch.credit_tracker.mapper.signal;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import java.time.LocalDateTime;
import java.util.List;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import ru.nbch.credit_tracker.entities.app.UserPackage;
@Mapper
public interface UserPackageMapper {
@ -18,6 +17,8 @@ public interface UserPackageMapper {
void deletePackage(@Param("userId") String userId, @Param("packageName") String packageName, @Param("status") String newStatus, @Param("previousStatus") String previousStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
void deletePackageById(@Param("id") Long id, @Param("status") String newStatus, @Param("deactivatedAt") LocalDateTime deactivatedAt);
Long insertPackage(@Param("userPackage") UserPackage userPackage);
void updatePackageStatus(@Param("packageId") Long packageId, @Param("status") String status);
@ -33,4 +34,6 @@ public interface UserPackageMapper {
List<UserPackage> findAllPackagesByStatus(@Param("status") String status);
List<UserPackage> findAllPackagesByUserId(@Param("userId") String userId);
List<UserPackage> findByMemberCodeAndPackageName(@Param("memberCode") String memberCode,
@Param("packageName") String packageName);
}

View file

@ -60,6 +60,10 @@ public class SignalServiceV2 {
return userPackageService.isPackageExists(username, packageName, Status.ON_MONITORING);
}
public List<UserPackage> findByMemberCodeAndPackageName(String memberCode, String packageName) {
return userPackageService.findByMemberCodeAndPackageName(memberCode, packageName);
}
public boolean isPackagesByStatusExist(String username, String packageName, Status status) {
return userPackageService.hasActivePackages(username, packageName, status);
}
@ -67,4 +71,8 @@ public class SignalServiceV2 {
public void deletePackage(String username, String packageName, LocalDateTime deactivatedAt) {
userPackageService.deletePackage(username, packageName, Status.DELETED, Status.ON_MONITORING, deactivatedAt);
}
public void deletePackageById(Long id, LocalDateTime deactivatedAt) {
userPackageService.deletePackageById(id, deactivatedAt);
}
}

View file

@ -1,24 +1,6 @@
package ru.nbch.credit_tracker.service.signal;
import jakarta.xml.bind.DatatypeConverter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.external_response.errors.SgnlReport;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError;
import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport;
import ru.nbch.credit_tracker.service.SignalService;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.InputStream;
@ -28,9 +10,34 @@ import java.nio.file.Files;
import java.nio.file.Path;
import java.security.MessageDigest;
import java.util.Base64;
import java.util.Optional;
import java.util.UUID;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.core.io.FileUrlResource;
import org.springframework.core.io.Resource;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.HttpStatusCode;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.external_response.errors.SgnlReport;
import ru.nbch.credit_tracker.entities.external_response.list_package.SgnlReportDeleted;
import ru.nbch.credit_tracker.entities.external_response.monitoring_report_file.SgnlReportMonitorigSetupError;
import ru.nbch.credit_tracker.entities.external_response.online_report.SgnlOnlineReport;
import ru.nbch.credit_tracker.service.SignalService;
import ru.nbch.credit_tracker.service.xml.jaxb.IXmlProcessor;
import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
import ru.nbch.credit_tracker.utils.CTUtil;
@Slf4j
@Service
@ -170,8 +177,9 @@ public class SignalRestService {
/**
* Удаляет пакет с мониторинга
* @return Успешность удаления пакета
*/
public void deletePackage(String username, String packageName) {
public boolean deletePackage(String username, String packageName) {
String packCode = username.substring(0, 6) + "_" + packageName;
String url = DELETE_URI + "?packCode=" + packCode;
HttpHeaders headers = new HttpHeaders();
@ -179,18 +187,72 @@ public class SignalRestService {
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
ResponseEntity<String> response = restTemplate.exchange(
ResponseEntity<String> responseEntity = restTemplate.exchange(
url,
HttpMethod.GET,
requestEntity,
String.class
);
String answer = response.getBody();
HttpStatusCode statusCode = responseEntity.getStatusCode();
SgnlReport response = staxProcessor.read(responseEntity.getBody(), SgnlReport.class);
SgnlReport error = staxProcessor.read(answer, SgnlReport.class);
if (error.getError().getDescription() != null) {
log.error("Ошибка при удалении пакета из сервиса НБКИ. packCode={}. Описание: {}", packCode, error.getError().getDescription());
if (statusCode.value() == 200 && !isError(response)) { //все ок
return true;
}
log.error("Ошибка при удалении пакета из сервиса НБКИ. packCode={}. status={}. Описание: {}",
packCode,
statusCode.value(),
errorDescription(response));
if (statusCode.value() != 200) { //была фатальная ошибка
return false;
}
return isRemotePackageStatedDeleted(username, packageName);
}
private boolean isRemotePackageStatedDeleted(String username, String packageName) {
HttpHeaders headers = new HttpHeaders();
headers.setBasicAuth(authToken);
HttpEntity<MultiValueMap<String, String>> requestEntity = new HttpEntity<>(headers);
String packCode = username.substring(0, 6) + "_" + packageName;
ResponseEntity<String> responseEntity = restTemplate.exchange(
GET_REPORT_URI + "?packCode=" + packCode,
HttpMethod.GET,
requestEntity,
String.class
);
HttpStatusCode statusCode = responseEntity.getStatusCode();
SgnlReportDeleted response = staxProcessor.read(responseEntity.getBody(), SgnlReportDeleted.class);
Integer deletedFlag = Optional.ofNullable(response)
.map(SgnlReportDeleted::getDeleteFlag)
.orElse(null);
if ((deletedFlag == null) || statusCode.value() != 200) {
log.error("couldn't perform report for {}: HTTP code: {}; delete flag {}",
packCode, statusCode.value(), deletedFlag);
return false;
}
if (deletedFlag != 0) {
log.info("{} was not deleted", packCode);
return false;
}
log.info("{} was deleted", packCode);
return true;
}
private static boolean isError(SgnlReport response) {
return response.getError() != null
&& (!StringUtils.isEmpty(response.getError().getDescription()) || !StringUtils.isEmpty(response.getError().getName()));
}
private static String errorDescription(SgnlReport response) {
if (response.getError() == null)
return "unknown error";
return "%s: %s".formatted(response.getError().getName(), response.getError().getDescription());
}
/**

View file

@ -1,13 +1,12 @@
package ru.nbch.credit_tracker.service.signal;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper;
import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.mapper.signal.UserPackageMapper;
@Service
public class UserPackageService {
@ -34,6 +33,10 @@ public class UserPackageService {
mapper.deletePackage(userId.substring(0, 6), packageName, newStatus.name(), previousStatus.name(), deactivatedAt);
}
public void deletePackageById(Long id, LocalDateTime deactivatedAt) {
mapper.deletePackageById(id, Status.DELETED.name(), deactivatedAt);
}
public Long insertPackage(UserPackage userPackage) {
return mapper.insertPackage(userPackage);
}
@ -62,6 +65,10 @@ public class UserPackageService {
return mapper.findAllPackagesByUserId(userId.substring(0, 6));
}
public List<UserPackage> findByMemberCodeAndPackageName(String memberCode, String packageName) {
return mapper.findByMemberCodeAndPackageName(memberCode, packageName);
}
public List<UserPackage> findAllPackagesByStatus(Status status) {
return mapper.findAllPackagesByStatus(status.name());
}

View file

@ -0,0 +1,44 @@
package ru.nbch.credit_tracker.service.xml.stax;
import javax.xml.stream.XMLStreamReader;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import ru.nbch.credit_tracker.entities.external_response.list_package.SgnlReportDeleted;
import ru.nbch.credit_tracker.utils.error.ExceptionUtils;
public class SgnlReportDeletedStaxReader extends BaseStaxReader<SgnlReportDeleted> {
private final Logger log = LoggerFactory.getLogger(getClass());
@Override
public Class<SgnlReportDeleted> type() {
return SgnlReportDeleted.class;
}
@Override
protected SgnlReportDeleted createObject() {
return new SgnlReportDeleted();
}
private Integer parseDeleteFlag(String text) {
try {
return Integer.parseInt(text);
} catch (Throwable e) {
log.error("couldn't parse delete flag: {}", ExceptionUtils.getStackTrace(e));
return null;
}
}
@Override
protected boolean innerProcess(XMLStreamReader reader, SgnlReportDeleted obj) {
String text = buffer.toString();
String currPath = getPath();
if (currPath.equals("/SgnlReport/OperationReport/PackageForNextReport/deleteFlag")) {
obj.setDeleteFlag(parseDeleteFlag(text));
}
return false;
}
@Override
protected void innerProcessStartElement(XMLStreamReader reader, SgnlReportDeleted obj) {
}
}

View file

@ -0,0 +1,13 @@
package ru.nbch.credit_tracker.utils;
public class EnumUtil {
public static <E extends Enum<E>> E getEnum(final Class<E> enumClass, final String name) {
E[] enumConstants = enumClass.getEnumConstants();
for (E e : enumConstants) {
if (e.name().equals(name)) {
return e;
}
}
return null;
}
}

View file

@ -52,6 +52,12 @@
#{previousStatus}
</update>
<update id="deletePackageById">
UPDATE signals_user_packages
SET status = #{status}, deactivated_at = #{deactivatedAt}
WHERE id =#{id}
</update>
<select id="insertPackage" resultType="java.lang.Long"
parameterType="ru.nbch.credit_tracker.entities.app.UserPackage">
INSERT INTO signals_user_packages (membercode, package_name, created_at, status)
@ -111,4 +117,10 @@
WHERE status = #{status}
</select>
<select id="findByMemberCodeAndPackageName" resultMap="packageDataResultMap">
SELECT id, membercode, package_name, created_at, status, deactivated_at, last_downloaded_report, monitoring_at
FROM signals_user_packages
WHERE membercode = #{memberCode} AND package_name = #{packageName}
</select>
</mapper>