feat: refresh es after external import commit
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
import java.util.concurrent.ThreadPoolExecutor;
|
||||
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.scheduling.annotation.EnableAsync;
|
||||
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
||||
|
||||
/**
|
||||
* 外部岗位导入后置任务的独立执行器。
|
||||
*
|
||||
* 全量重建会删除并创建 job_document 索引,必须串行执行,不能占用通用业务线程池。
|
||||
*/
|
||||
@Configuration
|
||||
@EnableAsync
|
||||
public class ExternalJobImportAsyncConfig {
|
||||
|
||||
@Bean(name = "externalJobImportEsRefreshExecutor")
|
||||
public ThreadPoolTaskExecutor externalJobImportEsRefreshExecutor() {
|
||||
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
||||
executor.setCorePoolSize(1);
|
||||
executor.setMaxPoolSize(1);
|
||||
executor.setQueueCapacity(10);
|
||||
executor.setKeepAliveSeconds(60);
|
||||
executor.setThreadNamePrefix("external-job-es-refresh-");
|
||||
// 队列满时在提交后的监听线程执行,确保成功批次最终一定会请求索引刷新。
|
||||
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
|
||||
return executor;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.transaction.event.TransactionPhase;
|
||||
import org.springframework.transaction.event.TransactionalEventListener;
|
||||
|
||||
/**
|
||||
* 在外部岗位导入事务提交后安排 ES 全量刷新。
|
||||
*/
|
||||
@Component
|
||||
public class ExternalJobImportEsRefreshListener {
|
||||
private final ExternalJobImportEsRefreshService refreshService;
|
||||
|
||||
public ExternalJobImportEsRefreshListener(ExternalJobImportEsRefreshService refreshService) {
|
||||
this.refreshService = refreshService;
|
||||
}
|
||||
|
||||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
|
||||
public void onImportSucceeded(ExternalJobImportSucceededEvent event) {
|
||||
refreshService.refreshAfterCommitAsync(event.getBatchId());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
import com.ruoyi.cms.service.IESJobSearchService;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.scheduling.annotation.Async;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* 外部导入提交后的 ES 全量重建执行器。
|
||||
*/
|
||||
@Service
|
||||
public class ExternalJobImportEsRefreshService {
|
||||
private static final Logger LOGGER = LoggerFactory.getLogger(ExternalJobImportEsRefreshService.class);
|
||||
|
||||
private final IESJobSearchService esJobSearchService;
|
||||
|
||||
public ExternalJobImportEsRefreshService(IESJobSearchService esJobSearchService) {
|
||||
this.esJobSearchService = esJobSearchService;
|
||||
}
|
||||
|
||||
@Async("externalJobImportEsRefreshExecutor")
|
||||
public void refreshAfterCommitAsync(String batchId) {
|
||||
LOGGER.info("外部岗位导入批次 {} 已提交,开始异步全量重建 ES 岗位索引", batchId);
|
||||
try {
|
||||
esJobSearchService.resetTextCache();
|
||||
LOGGER.info("外部岗位导入批次 {} 的 ES 岗位索引异步重建完成", batchId);
|
||||
} catch (RuntimeException ex) {
|
||||
LOGGER.error("外部岗位导入批次 {} 的 ES 岗位索引异步重建失败", batchId, ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,7 @@ package com.ruoyi.cms.externalimport;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import org.springframework.context.ApplicationEventPublisher;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.jdbc.support.GeneratedKeyHolder;
|
||||
import org.springframework.jdbc.support.KeyHolder;
|
||||
@@ -40,16 +41,19 @@ public class ExternalJobImportServiceImpl implements IExternalJobImportService {
|
||||
private final ExternalUploadClient uploadClient;
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
private final ObjectMapper objectMapper;
|
||||
private final ApplicationEventPublisher eventPublisher;
|
||||
private final ExternalJobExcelParser parser = new ExternalJobExcelParser();
|
||||
|
||||
public ExternalJobImportServiceImpl(ExternalJobImportProperties properties,
|
||||
ExternalUploadClient uploadClient,
|
||||
JdbcTemplate jdbcTemplate,
|
||||
ObjectMapper objectMapper) {
|
||||
ObjectMapper objectMapper,
|
||||
ApplicationEventPublisher eventPublisher) {
|
||||
this.properties = properties;
|
||||
this.uploadClient = uploadClient;
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
this.objectMapper = objectMapper;
|
||||
this.eventPublisher = eventPublisher;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -158,6 +162,8 @@ public class ExternalJobImportServiceImpl implements IExternalJobImportService {
|
||||
result.setStatus("SUCCESS");
|
||||
result.setMessage("外部岗位全量快照发布成功");
|
||||
updateBatch(batchId, "SUCCESS", result, null);
|
||||
// 仅在事务成功提交后异步全量刷新 ES,避免索引读到未提交或最终回滚的岗位数据。
|
||||
eventPublisher.publishEvent(new ExternalJobImportSucceededEvent(batchId));
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
/**
|
||||
* 外部岗位全量快照已成功落库的领域事件。
|
||||
*
|
||||
* 事件由事务监听器在提交后处理,因此不会为回滚、阻断或重复跳过的批次刷新 ES。
|
||||
*/
|
||||
public class ExternalJobImportSucceededEvent {
|
||||
private final String batchId;
|
||||
|
||||
public ExternalJobImportSucceededEvent(String batchId) {
|
||||
this.batchId = batchId;
|
||||
}
|
||||
|
||||
public String getBatchId() {
|
||||
return batchId;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class ExternalJobImportEsRefreshListenerTest {
|
||||
|
||||
@Test
|
||||
void delegatesCommittedBatchToAsyncRefreshService() {
|
||||
ExternalJobImportEsRefreshService refreshService = mock(ExternalJobImportEsRefreshService.class);
|
||||
ExternalJobImportEsRefreshListener listener = new ExternalJobImportEsRefreshListener(refreshService);
|
||||
|
||||
listener.onImportSucceeded(new ExternalJobImportSucceededEvent("batch-20260721"));
|
||||
|
||||
verify(refreshService).refreshAfterCommitAsync("batch-20260721");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
package com.ruoyi.cms.externalimport;
|
||||
|
||||
import com.ruoyi.cms.service.IESJobSearchService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
|
||||
class ExternalJobImportEsRefreshServiceTest {
|
||||
|
||||
@Test
|
||||
void rebuildsJobIndexForCommittedBatch() {
|
||||
IESJobSearchService searchService = mock(IESJobSearchService.class);
|
||||
ExternalJobImportEsRefreshService refreshService = new ExternalJobImportEsRefreshService(searchService);
|
||||
|
||||
refreshService.refreshAfterCommitAsync("batch-20260721");
|
||||
|
||||
verify(searchService).resetTextCache();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user