restore package in ACCEPTER;PROCCESSING statuses

This commit is contained in:
etreschenkov 2025-11-28 14:24:54 +03:00
parent 3d86558acb
commit a24a3b5975
13 changed files with 164 additions and 27 deletions

View file

@ -66,7 +66,7 @@ ct.load-dir=D:\\folder_name\\signals
# min # min
ct.monitoring-interval=${MONITORING_INTERVAL:15} ct.monitoring-interval=${MONITORING_INTERVAL:15}
# days # days
ct.update-flags-interval=${UPDATE_FLAGS_INTERVAL:0 0 0 2 * ?} ct.update-flags-cron-expression=${UPDATE_FLAGS_CRON_EXPRESSION:0 22 * * *}
ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30} ct.days-to-update-flags=${DAYS_TO_UPDATE_FLAGS:30}
ct.db-version=${DB_VERSION:1234} ct.db-version=${DB_VERSION:1234}

View file

@ -14,8 +14,8 @@ public class CreditTrackerApplication {
UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class); UpdateProcessingPackagesService updateProcessingPackagesService = ctx.getBean(UpdateProcessingPackagesService.class);
MonitoringService monitoringService = ctx.getBean(MonitoringService.class); MonitoringService monitoringService = ctx.getBean(MonitoringService.class);
// UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class); // UpdatingFlagsService updatingFlagsService = ctx.getBean(UpdatingFlagsService.class);
updateProcessingPackagesService.run(); // updateProcessingPackagesService.run();
// monitoringService.run(); monitoringService.run();
// updatingFlagsService.run(); // updatingFlagsService.run();
} }

View file

@ -31,7 +31,7 @@ public class CreditTrackerSettings {
private Long maxXmlFileSize; private Long maxXmlFileSize;
private String loadDir; private String loadDir;
private Long monitoringInterval; private Long monitoringInterval;
private String updateFlagsInterval; private String updateFlagsCronExpression;
private Long daysToUpdateFlags; private Long daysToUpdateFlags;
private String encoding; private String encoding;
private String dbVersion; private String dbVersion;

View file

@ -31,4 +31,5 @@ public interface PhoneFidMapMapper {
boolean hasNonNullFids(@Param("packageId") Long packageId); boolean hasNonNullFids(@Param("packageId") Long packageId);
Long deleteByPackageId(@Param("packageId") Long id); Long deleteByPackageId(@Param("packageId") Long id);
boolean hasOldRow(@Param("packageId") Long packageId, @Param("daysCount") Integer daysCount);
} }

View file

@ -33,7 +33,7 @@ public class PhoneRepository {
WITH UR WITH UR
"""; """;
private static final String SELCT_FIDS_WITH_NAME = """ private static final String SELECT_FIDS_WITH_NAME = """
SELECT SELECT
pn.number, pn.number,
pn.fid, pn.fid,
@ -49,6 +49,18 @@ public class PhoneRepository {
WHERE number in (:phones) WITH UR WHERE number in (:phones) WITH UR
"""; """;
private static final String SELECT_NAME_BY_FIDS = """
SELECT
fid,
first,
middle,
name_1,
birth_dt,
file_since_dt as file_since_dt_name
FROM NAME
WHERE fid in (:fid) WITH UR
""";
private final NamedParameterJdbcTemplate jdbcTemplate; private final NamedParameterJdbcTemplate jdbcTemplate;
public PhoneRepository(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) { public PhoneRepository(@Qualifier("jdbcTemplateIndic") NamedParameterJdbcTemplate jdbcTemplate) {
@ -70,7 +82,7 @@ public class PhoneRepository {
MapSqlParameterSource params = new MapSqlParameterSource(); MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("phones", phones); params.addValue("phones", phones);
return jdbcTemplate.query(SELCT_FIDS_WITH_NAME, params, (rs, rowNum) -> return jdbcTemplate.query(SELECT_FIDS_WITH_NAME, params, (rs, rowNum) ->
new PhoneRepository.FidsDataWithName( new PhoneRepository.FidsDataWithName(
rs.getInt("FID"), rs.getInt("FID"),
rs.getString("NUMBER"), rs.getString("NUMBER"),
@ -87,7 +99,7 @@ public class PhoneRepository {
MapSqlParameterSource params = new MapSqlParameterSource(); MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("phones", phones); params.addValue("phones", phones);
return jdbcTemplate.query(SELCT_FIDS_WITH_NAME, params, (rs, rowNum) -> return jdbcTemplate.query(SELECT_FIDS_WITH_NAME, params, (rs, rowNum) ->
Name.builder() Name.builder()
.fid(rs.getInt("FID")) .fid(rs.getInt("FID"))
.number(rs.getString("NUMBER")) .number(rs.getString("NUMBER"))
@ -99,6 +111,21 @@ public class PhoneRepository {
.build()); .build());
} }
public List<Name> findNameByFids(List<Integer> fids) {
MapSqlParameterSource params = new MapSqlParameterSource();
params.addValue("fids", fids);
return jdbcTemplate.query(SELECT_NAME_BY_FIDS, params, (rs, rowNum) ->
Name.builder()
.fid(rs.getInt("FID"))
.fileSinceDate(getLocalDate(rs, "FILE_SINCE_DT_NAME"))
.first(rs.getString("FIRST"))
.middle(rs.getString("MIDDLE"))
.last(rs.getString("NAME_1"))
.birthDate(getLocalDate(rs, "BIRTH_DT"))
.build());
}
private static LocalDate getLocalDate(ResultSet rs, String col) throws SQLException { private static LocalDate getLocalDate(ResultSet rs, String col) throws SQLException {
java.sql.Date d = rs.getDate(col); java.sql.Date d = rs.getDate(col);
return d != null ? d.toLocalDate() : null; return d != null ? d.toLocalDate() : null;

View file

@ -101,7 +101,7 @@ public class MonitoringService {
log.info("Package has no fids. Package will be skipped."); log.info("Package has no fids. Package will be skipped.");
return; return;
} }
SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId().substring(0, 6) + "_" + userPackage.getPackageName()); SgnlOnlineReport onlineReports = signalRestService.getReports(userPackage.getUserId() + "_" + userPackage.getPackageName());
if (onlineReports == null) { if (onlineReports == null) {
log.info("No reports found for package"); log.info("No reports found for package");

View file

@ -3,16 +3,18 @@ package ru.nbch.credit_tracker.service;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.List; import java.util.List;
import java.util.function.Predicate;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.slf4j.MDC; import org.slf4j.MDC;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.scheduling.annotation.EnableScheduling; import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler;
import org.springframework.scheduling.support.CronTrigger;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
import ru.nbch.credit_tracker.entities.app.UserPackage; import ru.nbch.credit_tracker.entities.app.UserPackage;
import ru.nbch.credit_tracker.enums.Status; import ru.nbch.credit_tracker.enums.Status;
import ru.nbch.credit_tracker.mapper.signal.PhoneFidMapMapper;
import ru.nbch.credit_tracker.service.signal.SignalRestService; import ru.nbch.credit_tracker.service.signal.SignalRestService;
/** /**
@ -22,21 +24,33 @@ import ru.nbch.credit_tracker.service.signal.SignalRestService;
@Slf4j @Slf4j
@Service @Service
@EnableScheduling @EnableScheduling
public class UpdatingFlagsService /*implements InitializingBean*/ { public class UpdatingFlagsService implements InitializingBean {
private final SignalService signalService; private final SignalService signalService;
private final SignalRestService signalRestService; private final SignalRestService signalRestService;
private final MonitoringErrorProcesingService monitoringErrorProcesingService; private final MonitoringErrorProcesingService monitoringErrorProcesingService;
private final Long daysToUpdateFlags; private final Long daysToUpdateFlags;
private final String updateFlagsCronExpression;
private final PhoneFidMapMapper phoneFidMapMapper;
public UpdatingFlagsService(SignalService signalService, public UpdatingFlagsService(SignalService signalService,
SignalRestService signalRestService, SignalRestService signalRestService,
MonitoringErrorProcesingService monitoringErrorProcesingService, MonitoringErrorProcesingService monitoringErrorProcesingService,
CreditTrackerSettings config) { CreditTrackerSettings config,
PhoneFidMapMapper phoneFidMapMapper) {
this.signalService = signalService; this.signalService = signalService;
this.signalRestService = signalRestService; this.signalRestService = signalRestService;
this.monitoringErrorProcesingService = monitoringErrorProcesingService; this.monitoringErrorProcesingService = monitoringErrorProcesingService;
this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1; this.daysToUpdateFlags = config.getDaysToUpdateFlags() != null ? config.getDaysToUpdateFlags() : 1;
this.updateFlagsCronExpression = config.getUpdateFlagsCronExpression() != null ? config.getUpdateFlagsCronExpression() : "*/10 * * * * *";
this.phoneFidMapMapper = phoneFidMapMapper;
}
public void run() {
ThreadPoolTaskScheduler scheduler = new ThreadPoolTaskScheduler();
scheduler.setPoolSize(1);
scheduler.initialize();
scheduler.schedule(this::update, new CronTrigger(updateFlagsCronExpression));
} }
@Scheduled(cron = "${ct.update-flags-interval}") @Scheduled(cron = "${ct.update-flags-interval}")
@ -44,9 +58,13 @@ public class UpdatingFlagsService /*implements InitializingBean*/ {
log.info("Start updating flags"); log.info("Start updating flags");
List<UserPackage> packages = signalService.findPackagesOnMonitoring(); List<UserPackage> packages = signalService.findPackagesOnMonitoring();
for (UserPackage userPackage : packages) { for (UserPackage userPackage : packages) {
log.info("Updating flags for Package {}", userPackage.getPackageName());
MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]"); MDC.put("ru.nbch.credit_tracker.log.mdc.key", "[" + userPackage.getPackageName() +"]");
log.info("Check on updating flags for Package {}", userPackage.getPackageName());
boolean hasOldRow = phoneFidMapMapper.hasOldRow(userPackage.getId(), daysToUpdateFlags.intValue());
log.info("There are {} old rows", hasOldRow ? "exist" : "doesn't exist");
if (hasOldRow) {
updateFlags(userPackage); updateFlags(userPackage);
}
MDC.remove("ru.nbch.credit_tracker.log.mdc.key"); MDC.remove("ru.nbch.credit_tracker.log.mdc.key");
} }
log.info("Finish updating flags"); log.info("Finish updating flags");
@ -57,8 +75,7 @@ public class UpdatingFlagsService /*implements InitializingBean*/ {
String packageName = userPackage.getPackageName(); String packageName = userPackage.getPackageName();
try { try {
signalService.updatePackageStatus(packageId, Status.PROCESSING); signalService.updatePackageStatus(packageId, Status.PROCESSING);
Predicate<PhoneFidMap> filter = subject -> subject.getCalculatedAt() != null && hasDaysPassed(subject.getCalculatedAt()); SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, null);
SignalService.UpdateResult updateResult = signalService.updateSubjects(packageId, filter);
if (updateResult.isUpdated()) { if (updateResult.isUpdated()) {
if (signalRestService.setupMonitoring(packageId)) { if (signalRestService.setupMonitoring(packageId)) {
// LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now(); // LocalDateTime monitoringAt = userPackage.getMonitoringAt() != null ? userPackage.getMonitoringAt() : LocalDateTime.now();
@ -83,4 +100,9 @@ public class UpdatingFlagsService /*implements InitializingBean*/ {
long daysPassed = ChronoUnit.DAYS.between(pastDateTime, now); long daysPassed = ChronoUnit.DAYS.between(pastDateTime, now);
return daysPassed >= daysToUpdateFlags; return daysPassed >= daysToUpdateFlags;
} }
@Override
public void afterPropertiesSet() throws Exception {
}
} }

View file

@ -59,6 +59,30 @@ public class PhoneService2 {
)); ));
} }
/**
* Поиск данных по таблицам NAME по списку FID
* Если пакет ACCEPTED, то в базе могут находиться записи с уже рассчитанными FID
* Нужен для исключения долгих запросов findNameByPhones
* Смотри UpdateProcessingPackagesService:processingRestore
* @param subjects
* @return - отображение NAME к FID, отфильтрованному по логике
* FID с максимальным значением FILE_SINCE_DT. Если FILE_SINCE_DT оказался одинаковый, берется максимальный FID
*/
public Map<Integer, Name> nameByFid(List<SubjectProfile> subjects) {
List<Integer> fids = subjects.stream()
.map(SubjectProfile::getFid)
.toList();
List<Name> names = phoneRepository.findNameByFids(fids);
return names.stream()
.collect(Collectors.toMap(
Name::getFid,
Function.identity(),
BinaryOperator.maxBy(byDateThenIdNullSafe)
));
}
public List<Long> registerSubjects(List<PhoneFidMap> subjects) { public List<Long> registerSubjects(List<PhoneFidMap> subjects) {
try { try {
//fixme, временное решение //fixme, временное решение
@ -99,4 +123,8 @@ public class PhoneService2 {
return phoneFidMapMapper.findNullFidPhoneFidMap(packageId, limit, offset); return phoneFidMapMapper.findNullFidPhoneFidMap(packageId, limit, offset);
} }
public List<PhoneFidMap> pageByPackageIdWithNotNullFids(Long packageId, Integer limit, Integer offset) {
return phoneFidMapMapper.findNotNullFidPhoneFidMap(packageId, limit, offset);
}
} }

View file

@ -18,6 +18,7 @@ import ru.nbch.credit_tracker.utils.CTUtil;
/** /**
* Стадия для сопоставления FID к номеру телефона и регистрации пакета в базе данных. * Стадия для сопоставления FID к номеру телефона и регистрации пакета в базе данных.
* Так же выполняется поиск и обогащение перс.данными из NAME для дальнейшей обработки. * Так же выполняется поиск и обогащение перс.данными из NAME для дальнейшей обработки.
* Имеет функуию при режиме RESTORE у Pipeline, когда фиды были установлены, но данные нужно подтянуть снова
*/ */
@Service @Service
@Slf4j @Slf4j
@ -36,6 +37,15 @@ public class FidNameEnricher implements Handler<SubjectPayload, SubjectPayload>
List<SubjectProfile> subjects = taskPayload.getSubjects(); List<SubjectProfile> subjects = taskPayload.getSubjects();
log.debug("NameEnricherStage handle by packageId: {}, size of bucket for save: {}", taskPayload.getPackageId(), subjects.size()); log.debug("NameEnricherStage handle by packageId: {}, size of bucket for save: {}", taskPayload.getPackageId(), subjects.size());
if (subjects.getFirst().getFid() == null) {
processByNumber(subjects);
} else {
processByFid(subjects);
}
return taskPayload;
}
private void processByNumber(List<SubjectProfile> subjects) {
LocalDateTime now = LocalDateTime.now(); LocalDateTime now = LocalDateTime.now();
Map<String, Name> nameByPhone = phoneService2.findNameByNumber(subjects); Map<String, Name> nameByPhone = phoneService2.findNameByNumber(subjects);
List<PhoneFidMap> subjectForSave = subjects.stream().map(subject -> { List<PhoneFidMap> subjectForSave = subjects.stream().map(subject -> {
@ -63,7 +73,24 @@ public class FidNameEnricher implements Handler<SubjectPayload, SubjectPayload>
}).toList(); }).toList();
phoneService2.updateFid(subjectForSave); phoneService2.updateFid(subjectForSave);
return taskPayload; }
private void processByFid(List<SubjectProfile> subjects) {
Map<Integer, Name> nameByPhone = phoneService2.nameByFid(subjects);
subjects.forEach(subject -> {
Name name = nameByPhone.get(subject.getFid());
if (name != null) {
NameData nameData = NameData.builder()
.middleName(name.getMiddle())
.firstName(name.getFirst())
.lastName(name.getLast())
.fileSinceDate(name.getFileSinceDate())
.birthDate(name.getBirthDate())
.build();
subject.setNameData(nameData);
}
});
} }
@Override @Override

View file

@ -28,6 +28,7 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
protected final PhoneService2 phoneService2; protected final PhoneService2 phoneService2;
private final int BATCH_SIZE; private final int BATCH_SIZE;
private boolean isInterrupted = false; private boolean isInterrupted = false;
protected int offset = 0;
public KickPipeLine(StaxXmlProcessor xmlProcessor, public KickPipeLine(StaxXmlProcessor xmlProcessor,
@ -53,10 +54,9 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) { public void startFillingQueue(BlockingQueue<SubjectPayload> firstQueue) {
try { try {
log.info("Start registering subjects. PackageId: {};", packageId); log.info("Start registering subjects. PackageId: {};", packageId);
int offset = 0;
int limit = BATCH_SIZE; int limit = BATCH_SIZE;
UserPackage userPackage = userPackageMapper.findPackage(packageId); UserPackage userPackage = userPackageMapper.findPackage(packageId);
List<PhoneFidMap> page = nextPage(limit, offset); List<PhoneFidMap> page = nextPage(limit);
while (!page.isEmpty()) { while (!page.isEmpty()) {
log.debug("Get page {}", page.size()); log.debug("Get page {}", page.size());
List<SubjectProfile> subjectProfiles = page.stream().map(phoneFidMap -> SubjectProfile.builder() List<SubjectProfile> subjectProfiles = page.stream().map(phoneFidMap -> SubjectProfile.builder()
@ -72,10 +72,10 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
throw new PipelineStarterException(); throw new PipelineStarterException();
} }
offset += limit; offset += limit;
page = nextPage(limit, offset); page = nextPage(limit);
} }
} catch (PipelineStarterException e) { } catch (PipelineStarterException e) {
log.info("Pipeline starter shitting down..."); log.info("Pipeline starter shutting down...");
} finally { } finally {
if (isInterrupted) { if (isInterrupted) {
//noinspection ReturnInsideFinallyBlock //noinspection ReturnInsideFinallyBlock
@ -92,5 +92,5 @@ public abstract class KickPipeLine implements IPipeLineStarter<SubjectPayload> {
} }
protected abstract PipelineMode mode(); protected abstract PipelineMode mode();
protected abstract List<PhoneFidMap> nextPage(Integer limit, Integer offset); protected abstract List<PhoneFidMap> nextPage(Integer limit);
} }

View file

@ -24,7 +24,7 @@ public class KickPipeLineDefaultImpl extends KickPipeLine {
} }
@Override @Override
protected List<PhoneFidMap> nextPage(Integer limit, Integer offset) { protected List<PhoneFidMap> nextPage(Integer limit) {
return phoneService2.pageByPackageId(packageId, limit, offset); return phoneService2.pageByPackageId(packageId, limit, offset);
} }

View file

@ -1,5 +1,6 @@
package ru.nbch.credit_tracker.service.task.impl; package ru.nbch.credit_tracker.service.task.impl;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings; import ru.nbch.credit_tracker.config.settings.CreditTrackerSettings;
import ru.nbch.credit_tracker.entities.app.PhoneFidMap; import ru.nbch.credit_tracker.entities.app.PhoneFidMap;
@ -10,7 +11,7 @@ import ru.nbch.credit_tracker.service.xml.stax.StaxXmlProcessor;
public class KickPipeLineRestoreImpl extends KickPipeLine { public class KickPipeLineRestoreImpl extends KickPipeLine {
private boolean nullFidsWereFound = false;
public KickPipeLineRestoreImpl(StaxXmlProcessor xmlProcessor, public KickPipeLineRestoreImpl(StaxXmlProcessor xmlProcessor,
SetupParam setupParam, SetupParam setupParam,
UserPackageMapper userPackageMapper, UserPackageMapper userPackageMapper,
@ -25,7 +26,16 @@ public class KickPipeLineRestoreImpl extends KickPipeLine {
} }
@Override @Override
protected List<PhoneFidMap> nextPage(Integer limit, Integer offset) { protected List<PhoneFidMap> nextPage(Integer limit) {
return phoneService2.pageByPackageIdWithNullFids(packageId, limit, offset); List<PhoneFidMap> pageWithNullFids = new ArrayList<>();
if (!nullFidsWereFound) {
pageWithNullFids = phoneService2.pageByPackageIdWithNullFids(packageId, limit, offset);
}
if (pageWithNullFids.isEmpty()) {
nullFidsWereFound = true;
offset = 0;
return phoneService2.pageByPackageIdWithNotNullFids(packageId, limit, offset);
}
return pageWithNullFids;
} }
} }

View file

@ -175,6 +175,22 @@
LIMIT #{limit} OFFSET #{offset} LIMIT #{limit} OFFSET #{offset}
</select> </select>
<select id="findNotNullFidPhoneFidMap" resultMap="PhoneFidMapResultMap" resultType="java.util.List">
SELECT id,
package_id,
subject_id,
phone,
fid,
flag_90_12,
flag_debt5000,
calculated_at,
error_text
FROM signals_phone_fid_map
WHERE package_id = #{packageId} and fid is not null
ORDER BY id
LIMIT #{limit} OFFSET #{offset}
</select>
<select id="findPhoneFidMapById" resultMap="PhoneFidMapResultMap" <select id="findPhoneFidMapById" resultMap="PhoneFidMapResultMap"
resultType="ru.nbch.credit_tracker.entities.app.PhoneFidMap"> resultType="ru.nbch.credit_tracker.entities.app.PhoneFidMap">
SELECT id, SELECT id,
@ -198,5 +214,11 @@
AND fid is not null AND fid is not null
</select> </select>
<select id="hasOldRow" resultType="java.lang.Boolean">
select EXISTS(SELECT 1
FROM signals_phone_fid_map
WHERE package_id = #{packageId}
AND calculated_at &lt; now() - make_interval(days => #{daysCount})
)
</select>
</mapper> </mapper>