perf: reduce student training task configuration queries

main
chenyuan 5 days ago
parent 5d847faa42
commit 3fa742c5bc

@ -36,6 +36,7 @@ import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
@ -67,6 +68,9 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
private boolean trainingTaskTableChecked = false;
private boolean trainingTaskClassConfigTableChecked = false;
private volatile boolean defaultTasksInitialized = false;
private final Object defaultTasksInitializationLock = new Object();
private final Set<String> initializedStudentClassTaskIds = ConcurrentHashMap.newKeySet();
@Override
public List<TrainingTask> list(String projectName, String taskName, Boolean enabledOnly) {
@ -186,19 +190,27 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
@Override
@Transactional(rollbackFor = Exception.class)
public void ensureDefaultTasks(String operatorId) {
ensureTrainingTaskTable();
ensureTrainingTaskClassConfigTable();
int sort = 1;
for (TrainingTask task : defaultTasks()) {
TrainingTask existing = trainingTaskMapper.selectByTaskKey(task.getTaskKey());
if (existing == null) {
task.setSort(sort);
create(task, operatorId);
if (defaultTasksInitialized) {
return;
}
synchronized (defaultTasksInitializationLock) {
if (defaultTasksInitialized) {
return;
}
ensureTrainingTaskTable();
ensureTrainingTaskClassConfigTable();
int sort = 1;
for (TrainingTask task : defaultTasks()) {
TrainingTask existing = trainingTaskMapper.selectByTaskKey(task.getTaskKey());
if (existing == null) {
task.setSort(sort);
create(task, operatorId);
}
sort++;
}
sort++;
ensureTaskAllocationClassIndex();
defaultTasksInitialized = true;
}
// 班级任务在对应班级首次读取时按需初始化。不要在学生读取单个任务时遍历所有教学班,
// 否则班级数量增加会把每次页面切换的查询放大为全量写入检查。
}
@Override
@ -304,7 +316,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|| (requiresExplicitPublication(normalizedTaskKey) && publishedTaskKeys == null)) {
return null;
}
ensureClassTasksInitialized(teachingClassId, trainingTaskMapper.selectList(null, null, null), null);
ensureStudentClassTasksInitialized(teachingClassId);
TrainingTaskClassConfig classTask = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
if (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()))
.collect(Collectors.toList()), enabledOnly);
}
List<TrainingTaskClassConfig> classTasks = ensureClassTasksInitialized(teachingClassId, defaults, null);
List<TrainingTaskClassConfig> classTasks = readStudentClassTasks(teachingClassId, defaults);
List<TrainingTask> tasks = filterEnabled(toTasks(classTasks), enabledOnly);
Set<String> publishedTaskKeys = getPublishedTaskKeys(teachingClassId);
if (publishedTaskKeys == null) {
@ -476,6 +488,23 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
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) {
if (schoolClassMapper == null || trainingTaskClassConfigMapper == null
|| previousTask == null || updatedTask == null) {
@ -687,6 +716,14 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
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) {
String projectName = StringUtils.trimToEmpty(row.getProjectName());
String taskKey = StringUtils.trimToEmpty(row.getTaskKey());

@ -28,13 +28,13 @@ class MarketOpportunitySelectionStepTwoServiceImplTest {
@Test
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()));
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()));
}
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 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 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.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; }
}

Loading…
Cancel
Save