perf: reduce student training task configuration queries

main
chenyuan 6 days ago
parent 5d847faa42
commit 3fa742c5bc

@ -36,6 +36,7 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.UUID; import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -67,6 +68,9 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
private boolean trainingTaskTableChecked = false; private boolean trainingTaskTableChecked = false;
private boolean trainingTaskClassConfigTableChecked = false; private boolean trainingTaskClassConfigTableChecked = false;
private volatile boolean defaultTasksInitialized = false;
private final Object defaultTasksInitializationLock = new Object();
private final Set<String> initializedStudentClassTaskIds = ConcurrentHashMap.newKeySet();
@Override @Override
public List<TrainingTask> list(String projectName, String taskName, Boolean enabledOnly) { public List<TrainingTask> list(String projectName, String taskName, Boolean enabledOnly) {
@ -186,6 +190,13 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
@Override @Override
@Transactional(rollbackFor = Exception.class) @Transactional(rollbackFor = Exception.class)
public void ensureDefaultTasks(String operatorId) { public void ensureDefaultTasks(String operatorId) {
if (defaultTasksInitialized) {
return;
}
synchronized (defaultTasksInitializationLock) {
if (defaultTasksInitialized) {
return;
}
ensureTrainingTaskTable(); ensureTrainingTaskTable();
ensureTrainingTaskClassConfigTable(); ensureTrainingTaskClassConfigTable();
int sort = 1; int sort = 1;
@ -197,8 +208,9 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
} }
sort++; sort++;
} }
// 班级任务在对应班级首次读取时按需初始化。不要在学生读取单个任务时遍历所有教学班, ensureTaskAllocationClassIndex();
// 否则班级数量增加会把每次页面切换的查询放大为全量写入检查。 defaultTasksInitialized = true;
}
} }
@Override @Override
@ -304,7 +316,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|| (requiresExplicitPublication(normalizedTaskKey) && publishedTaskKeys == null)) { || (requiresExplicitPublication(normalizedTaskKey) && publishedTaskKeys == null)) {
return null; return null;
} }
ensureClassTasksInitialized(teachingClassId, trainingTaskMapper.selectList(null, null, null), null); ensureStudentClassTasksInitialized(teachingClassId);
TrainingTaskClassConfig classTask = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey); TrainingTaskClassConfig classTask = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
if (classTask != null) { if (classTask != null) {
return Boolean.TRUE.equals(classTask.getEnabled()) ? toTask(classTask) : null; return Boolean.TRUE.equals(classTask.getEnabled()) ? toTask(classTask) : null;
@ -334,7 +346,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
.filter(task -> !requiresExplicitPublication(task.getTaskKey())) .filter(task -> !requiresExplicitPublication(task.getTaskKey()))
.collect(Collectors.toList()), enabledOnly); .collect(Collectors.toList()), enabledOnly);
} }
List<TrainingTaskClassConfig> classTasks = ensureClassTasksInitialized(teachingClassId, defaults, null); List<TrainingTaskClassConfig> classTasks = readStudentClassTasks(teachingClassId, defaults);
List<TrainingTask> tasks = filterEnabled(toTasks(classTasks), enabledOnly); List<TrainingTask> tasks = filterEnabled(toTasks(classTasks), enabledOnly);
Set<String> publishedTaskKeys = getPublishedTaskKeys(teachingClassId); Set<String> publishedTaskKeys = getPublishedTaskKeys(teachingClassId);
if (publishedTaskKeys == null) { if (publishedTaskKeys == null) {
@ -476,6 +488,23 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
return new ArrayList<>(existingByKey.values()); return new ArrayList<>(existingByKey.values());
} }
private void ensureStudentClassTasksInitialized(String teachingClassId) {
if (StringUtils.isBlank(teachingClassId) || initializedStudentClassTaskIds.contains(teachingClassId)) {
return;
}
ensureClassTasksInitialized(teachingClassId, trainingTaskMapper.selectList(null, null, null), null);
initializedStudentClassTaskIds.add(teachingClassId);
}
private List<TrainingTaskClassConfig> readStudentClassTasks(String teachingClassId, List<TrainingTask> defaults) {
if (initializedStudentClassTaskIds.contains(teachingClassId)) {
return trainingTaskClassConfigMapper.selectListByTeachingClass(teachingClassId, null);
}
List<TrainingTaskClassConfig> classTasks = ensureClassTasksInitialized(teachingClassId, defaults, null);
initializedStudentClassTaskIds.add(teachingClassId);
return classTasks;
}
private void synchronizeClassTaskDefaults(TrainingTask previousTask, TrainingTask updatedTask) { private void synchronizeClassTaskDefaults(TrainingTask previousTask, TrainingTask updatedTask) {
if (schoolClassMapper == null || trainingTaskClassConfigMapper == null if (schoolClassMapper == null || trainingTaskClassConfigMapper == null
|| previousTask == null || updatedTask == null) { || previousTask == null || updatedTask == null) {
@ -687,6 +716,14 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
trainingTaskClassConfigTableChecked = true; trainingTaskClassConfigTableChecked = true;
} }
private void ensureTaskAllocationClassIndex() {
if (jdbcTemplate == null) {
return;
}
jdbcTemplate.execute("CREATE INDEX IF NOT EXISTS idx_task_allocation_class_disabled_module "
+ "ON task_allocation (class_id, disabled_status, module)");
}
TrainingTask buildTask(TrainingTaskImportDTO row, int rowNumber, List<String> errors) { TrainingTask buildTask(TrainingTaskImportDTO row, int rowNumber, List<String> errors) {
String projectName = StringUtils.trimToEmpty(row.getProjectName()); String projectName = StringUtils.trimToEmpty(row.getProjectName());
String taskKey = StringUtils.trimToEmpty(row.getTaskKey()); String taskKey = StringUtils.trimToEmpty(row.getTaskKey());

@ -28,13 +28,13 @@ class MarketOpportunitySelectionStepTwoServiceImplTest {
@Test @Test
void validateRejectsIncorrectTotalAndMissingStepOne() { void validateRejectsIncorrectTotalAndMissingStepOne() {
MarketOpportunitySelectionStepTwoValidationRequest invalid = completedRequest(); invalid.getWeights().get(0).setWeight(21); MarketOpportunitySelectionStepTwoValidationRequest invalid = completedRequest(); invalid.getPrimaryWeights().get(0).setWeight(21);
assertThrows(ServiceException.class, () -> serviceWithStepOne().validate(invalid, student())); assertThrows(ServiceException.class, () -> serviceWithStepOne().validate(invalid, student()));
MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(null); MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(null);
assertThrows(ServiceException.class, () -> service.validate(completedRequest(), student())); assertThrows(ServiceException.class, () -> service.validate(completedRequest(), student()));
} }
private MarketOpportunitySelectionStepTwoServiceImpl serviceWithStepOne() { MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep1Answer("{\"answers\":{}}"); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); return service; } private MarketOpportunitySelectionStepTwoServiceImpl serviceWithStepOne() { MarketOpportunitySelectionStepTwoServiceImpl service = new MarketOpportunitySelectionStepTwoServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep1Answer("{\"answers\":{\"search-count\":\"市场规模\",\"transaction-amount\":\"市场规模\",\"transaction-growth\":\"市场潜力\",\"category-transaction-growth\":\"市场潜力\",\"demand-supply-ratio\":\"竞争强度\",\"online-products\":\"竞争强度\",\"online-merchants\":\"竞争强度\",\"organic-traffic-ratio\":\"运营难度\",\"return-rate\":\"运营难度\"}}"); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); return service; }
private MarketOpportunitySelectionStepTwoValidationRequest completedRequest() { MarketOpportunitySelectionStepTwoValidationRequest request = new MarketOpportunitySelectionStepTwoValidationRequest(); ArrayList<MarketOpportunitySelectionStepTwoValidationRequest.Weight> weights = new ArrayList<>(); int[] values = {20, 15, 15, 15, 10, 10, 10, 5}; Arrays.asList("search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio").forEach(id -> { MarketOpportunitySelectionStepTwoValidationRequest.Weight item = new MarketOpportunitySelectionStepTwoValidationRequest.Weight(); item.setId(id); item.setWeight(values[weights.size()]); weights.add(item); }); request.setWeights(weights); return request; } private MarketOpportunitySelectionStepTwoValidationRequest completedRequest() { MarketOpportunitySelectionStepTwoValidationRequest request = new MarketOpportunitySelectionStepTwoValidationRequest(); ArrayList<MarketOpportunitySelectionStepTwoValidationRequest.PrimaryWeight> primary = new ArrayList<>(); Arrays.asList("市场规模", "市场潜力", "竞争强度", "运营难度").forEach(group -> { MarketOpportunitySelectionStepTwoValidationRequest.PrimaryWeight item = new MarketOpportunitySelectionStepTwoValidationRequest.PrimaryWeight(); item.setGroup(group); item.setWeight(25); primary.add(item); }); ArrayList<MarketOpportunitySelectionStepTwoValidationRequest.Weight> dimensions = new ArrayList<>(); String[] ids = {"search-count", "transaction-amount", "transaction-growth", "category-transaction-growth", "demand-supply-ratio", "online-products", "online-merchants", "organic-traffic-ratio", "return-rate"}; int[] values = {50, 50, 50, 50, 34, 33, 33, 50, 50}; for (int i = 0; i < ids.length; i++) { MarketOpportunitySelectionStepTwoValidationRequest.Weight item = new MarketOpportunitySelectionStepTwoValidationRequest.Weight(); item.setId(ids[i]); item.setWeight(values[i]); dimensions.add(item); } request.setPrimaryWeights(primary); request.setDimensionWeights(dimensions); return request; }
private JwtUser student() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); user.setRoleId(4); return user; } private JwtUser student() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); user.setRoleId(4); return user; }
} }

Loading…
Cancel
Save