fix(aihr): gate vision processing with ai runtime

This commit is contained in:
2026-07-14 16:45:53 +08:00
parent ad21e24ed3
commit 81060fba08
8 changed files with 48 additions and 4 deletions
@@ -272,6 +272,10 @@ public class AihrModelSeedService {
}
}
public boolean visionAllowed() {
return aiEnabled && chatEnabled;
}
public record SpeechModel(String providerCode, String modelName, String baseUrl, String apiKey) {
}
@@ -123,19 +123,22 @@ public class AihrSopSeedService {
private final String importRootConfig;
private final ScheduledExecutorService scheduledExecutorService;
private final AihrVideoService videoService;
private final AihrModelSeedService modelService;
private volatile boolean knowledgeGapTableReady;
private volatile boolean sopReviewTableReady;
private volatile boolean answerFeedbackTableReady;
public AihrSopSeedService(ObjectMapper objectMapper, JdbcTemplate jdbcTemplate, ISysOssService ossService,
@Value("${aihr.import.root:}") String importRootConfig,
ScheduledExecutorService scheduledExecutorService, AihrVideoService videoService) {
ScheduledExecutorService scheduledExecutorService, AihrVideoService videoService,
AihrModelSeedService modelService) {
this.objectMapper = objectMapper;
this.jdbcTemplate = jdbcTemplate;
this.ossService = ossService;
this.importRootConfig = importRootConfig;
this.scheduledExecutorService = scheduledExecutorService;
this.videoService = videoService;
this.modelService = modelService;
}
public SearchResponse search(SearchRequest request) {
@@ -951,6 +954,9 @@ public class AihrSopSeedService {
/** 优先查 category='vision',无则回退 category='chat'(大多数多模态模型通用)。 */
private Optional<ChatRuntime> visionRuntime() {
if (modelService != null && !modelService.visionAllowed()) {
return Optional.empty();
}
try {
List<ChatRuntime> rows = jdbcTemplate.query("""
select c.model_name,
@@ -68,7 +68,7 @@ public class AihrPracticeSeedServiceTest {
RecordingTransactionManager transactionManager = new RecordingTransactionManager();
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
PilotExportJdbcTemplate jdbcTemplate = new PilotExportJdbcTemplate(transactionManager);
AihrSopSeedService sopSeedService = new AihrSopSeedService(new ObjectMapper(), jdbcTemplate, null, "", null, null);
AihrSopSeedService sopSeedService = new AihrSopSeedService(new ObjectMapper(), jdbcTemplate, null, "", null, null, null);
Constructor<AihrPracticeSeedService> constructor = (Constructor<AihrPracticeSeedService>) AihrPracticeSeedService.class
.getDeclaredConstructor(ObjectMapper.class, JdbcTemplate.class, AihrPracticeLlmService.class,
AihrSopSeedService.class, TransactionTemplate.class);
@@ -1,16 +1,21 @@
package org.dromara.aihr.service;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.dromara.aihr.domain.AihrSopDto;
import org.junit.jupiter.api.Tag;
import org.junit.jupiter.api.Test;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.List;
import java.util.Optional;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class AihrSopSeedServiceTest {
@@ -126,6 +131,32 @@ public class AihrSopSeedServiceTest {
assertTrue(code.contains("runLocalImportTask(taskId, plan)"));
}
@Test
@Tag("dev")
public void visionProcessingUsesTheSharedAiRuntimeGate() throws Exception {
Path source = Path.of("src/main/java/org/dromara/aihr/service/AihrSopSeedService.java");
if (!Files.exists(source)) {
source = Path.of("ruoyi-modules/ruoyi-aihr/src/main/java/org/dromara/aihr/service/AihrSopSeedService.java");
}
String code = Files.readString(source);
assertTrue(code.contains("!modelService.visionAllowed()"));
}
@Test
@Tag("dev")
public void disabledVisionGateStopsBeforeDatabaseLookup() throws Exception {
AihrModelSeedService modelService = mock(AihrModelSeedService.class);
when(modelService.visionAllowed()).thenReturn(false);
AihrSopSeedService service = new AihrSopSeedService(new ObjectMapper(), null, null, "", null, null, modelService);
Method visionRuntime = AihrSopSeedService.class.getDeclaredMethod("visionRuntime");
visionRuntime.setAccessible(true);
Optional<?> runtime = (Optional<?>) visionRuntime.invoke(service);
assertTrue(runtime.isEmpty());
}
@Test
@Tag("dev")
public void sopReviewUsesAuthenticatedOperatorForAuditTrail() throws Exception {