diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java index 856a3f2..36075d2 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java @@ -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 initializedStudentClassTaskIds = ConcurrentHashMap.newKeySet(); @Override public List 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 classTasks = ensureClassTasksInitialized(teachingClassId, defaults, null); + List classTasks = readStudentClassTasks(teachingClassId, defaults); List tasks = filterEnabled(toTasks(classTasks), enabledOnly); Set 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 readStudentClassTasks(String teachingClassId, List defaults) { + if (initializedStudentClassTaskIds.contains(teachingClassId)) { + return trainingTaskClassConfigMapper.selectListByTeachingClass(teachingClassId, null); + } + List 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 errors) { String projectName = StringUtils.trimToEmpty(row.getProjectName()); String taskKey = StringUtils.trimToEmpty(row.getTaskKey()); diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java index 665321b..8696b6f 100644 --- a/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/MarketOpportunitySelectionStepTwoServiceImplTest.java @@ -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 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 primary = new ArrayList<>(); Arrays.asList("市场规模", "市场潜力", "竞争强度", "运营难度").forEach(group -> { MarketOpportunitySelectionStepTwoValidationRequest.PrimaryWeight item = new MarketOpportunitySelectionStepTwoValidationRequest.PrimaryWeight(); item.setGroup(group); item.setWeight(25); primary.add(item); }); ArrayList 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; } }