From b760a5cfeefc6a9243c6f629af36dfb06c356377 Mon Sep 17 00:00:00 2001 From: chenyuan Date: Thu, 30 Jul 2026 13:23:31 +0800 Subject: [PATCH] feat: initialize school default tasks asynchronously --- .../PlatformAdminController.java | 4 + .../service/SchoolDefaultTaskService.java | 2 + .../impl/SchoolDefaultTaskServiceImpl.java | 75 ++++++++++++++++++- ...latformAdminControllerDefaultTaskTest.java | 50 +++++++++++++ .../SchoolDefaultTaskServiceImplTest.java | 18 +++++ 5 files changed, 148 insertions(+), 1 deletion(-) create mode 100644 src/test/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminControllerDefaultTaskTest.java diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminController.java b/src/main/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminController.java index 5259883..4a9f427 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminController.java @@ -7,6 +7,7 @@ import com.sztzjy.linkCommerce.config.security.TokenProvider; import com.sztzjy.linkCommerce.entity.*; import com.sztzjy.linkCommerce.mapper.*; import com.sztzjy.linkCommerce.service.PlatformAdminService; +import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService; import com.sztzjy.linkCommerce.service.SchoolProductConfigService; import com.sztzjy.linkCommerce.service.UserInfoService; import com.sztzjy.linkCommerce.util.ResultEntity; @@ -31,6 +32,8 @@ public class PlatformAdminController { @Autowired private SchoolProductConfigService schoolProductConfigService; @Autowired + private SchoolDefaultTaskService schoolDefaultTaskService; + @Autowired private UserInfoService userInfoService; @Autowired private SchoolMapper schoolMapper; @@ -84,6 +87,7 @@ public class PlatformAdminController { int count = schoolMapper.insertSelective(school); if (count > 0) { schoolProductConfigService.createDefaultConfig(school.getSchoolId()); + schoolDefaultTaskService.enqueueInitialization(school.getSchoolId()); } return writeResult(count, "新增成功", "新增失败"); } diff --git a/src/main/java/com/sztzjy/linkCommerce/service/SchoolDefaultTaskService.java b/src/main/java/com/sztzjy/linkCommerce/service/SchoolDefaultTaskService.java index 247d344..a301da2 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/SchoolDefaultTaskService.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/SchoolDefaultTaskService.java @@ -14,6 +14,8 @@ public interface SchoolDefaultTaskService { SchoolDefaultTaskInitialization getInitialization(String schoolId); + void enqueueInitialization(String schoolId); + void replaceSchoolDefault(String schoolId, List tasks); void initializeSchoolDefault(String schoolId); diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImpl.java index 11875e2..4220f42 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImpl.java @@ -10,6 +10,8 @@ import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper; import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Qualifier; +import org.springframework.core.task.AsyncTaskExecutor; import org.springframework.stereotype.Service; import java.util.Collections; @@ -27,6 +29,9 @@ public class SchoolDefaultTaskServiceImpl implements SchoolDefaultTaskService { SchoolClassMapper schoolClassMapper; @Autowired(required = false) SchoolDefaultTaskInitializationMapper initializationMapper; + @Autowired(required = false) + @Qualifier("asyncTaskExecutor") + AsyncTaskExecutor taskExecutor; public static String schoolDefaultClassId(String schoolId) { return SCHOOL_DEFAULT_PREFIX + schoolId; @@ -62,6 +67,24 @@ public class SchoolDefaultTaskServiceImpl implements SchoolDefaultTaskService { ? null : initializationMapper.selectByPrimaryKey(schoolId); } + @Override + public void enqueueInitialization(String schoolId) { + if (StringUtils.isBlank(schoolId)) { + throw new IllegalArgumentException("学校不能为空"); + } + markPending(schoolId); + if (taskExecutor == null) { + initializeInBackground(schoolId); + return; + } + try { + taskExecutor.execute(() -> initializeInBackground(schoolId)); + } catch (RuntimeException ex) { + markFailed(schoolId, ex); + throw ex; + } + } + @Override public void replaceSchoolDefault(String schoolId, List tasks) { if (StringUtils.isBlank(schoolId)) { @@ -82,9 +105,13 @@ public class SchoolDefaultTaskServiceImpl implements SchoolDefaultTaskService { @Override public void initializeSchoolDefault(String schoolId) { if (StringUtils.isBlank(schoolId) || hasSchoolDefault(schoolId)) { + markReady(schoolId); return; } List platformTasks = listByClassId(PLATFORM_DEFAULT_CLASS_ID); + if (platformTasks.isEmpty()) { + throw new IllegalStateException("平台基线实训任务为空"); + } for (TaskAllocation source : platformTasks) { TaskAllocation copy = new TaskAllocation(); copy.setId(UUID.randomUUID().toString()); @@ -98,6 +125,15 @@ public class SchoolDefaultTaskServiceImpl implements SchoolDefaultTaskService { markReady(schoolId); } + private void initializeInBackground(String schoolId) { + markRunning(schoolId); + try { + initializeSchoolDefault(schoolId); + } catch (RuntimeException ex) { + markFailed(schoolId, ex); + } + } + private boolean hasSchoolDefault(String schoolId) { TaskAllocationExample example = new TaskAllocationExample(); example.createCriteria().andClassIdEqualTo(schoolDefaultClassId(schoolId)); @@ -116,12 +152,49 @@ public class SchoolDefaultTaskServiceImpl implements SchoolDefaultTaskService { } private void markReady(String schoolId) { + updateInitialization(schoolId, "READY", null); + } + + private void markPending(String schoolId) { if (initializationMapper == null) { return; } + SchoolDefaultTaskInitialization existing = initializationMapper.selectByPrimaryKey(schoolId); + Date now = new Date(); + if (existing == null) { + SchoolDefaultTaskInitialization record = new SchoolDefaultTaskInitialization(); + record.setSchoolId(schoolId); + record.setStatus("PENDING"); + record.setAttemptCount(1); + record.setCreateTime(now); + record.setUpdateTime(now); + initializationMapper.insert(record); + return; + } + SchoolDefaultTaskInitialization record = new SchoolDefaultTaskInitialization(); + record.setSchoolId(schoolId); + record.setStatus("PENDING"); + record.setAttemptCount((existing.getAttemptCount() == null ? 0 : existing.getAttemptCount()) + 1); + record.setUpdateTime(now); + initializationMapper.updateByPrimaryKeySelective(record); + } + + private void markRunning(String schoolId) { + updateInitialization(schoolId, "RUNNING", null); + } + + private void markFailed(String schoolId, RuntimeException ex) { + updateInitialization(schoolId, "FAILED", StringUtils.abbreviate(ex.getMessage(), 1000)); + } + + private void updateInitialization(String schoolId, String status, String error) { + if (initializationMapper == null || StringUtils.isBlank(schoolId)) { + return; + } SchoolDefaultTaskInitialization record = new SchoolDefaultTaskInitialization(); record.setSchoolId(schoolId); - record.setStatus("READY"); + record.setStatus(status); + record.setLastError(error); record.setUpdateTime(new Date()); initializationMapper.updateByPrimaryKeySelective(record); } diff --git a/src/test/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminControllerDefaultTaskTest.java b/src/test/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminControllerDefaultTaskTest.java new file mode 100644 index 0000000..fffa2d0 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/controller/platformadmin/PlatformAdminControllerDefaultTaskTest.java @@ -0,0 +1,50 @@ +package com.sztzjy.linkCommerce.controller.platformadmin; + +import com.sztzjy.linkCommerce.config.security.JwtUser; +import com.sztzjy.linkCommerce.config.security.TokenProvider; +import com.sztzjy.linkCommerce.entity.School; +import com.sztzjy.linkCommerce.mapper.SchoolMapper; +import com.sztzjy.linkCommerce.service.PlatformAdminService; +import com.sztzjy.linkCommerce.service.SchoolDefaultTaskService; +import com.sztzjy.linkCommerce.service.SchoolProductConfigService; +import org.junit.jupiter.api.Test; +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; +import org.springframework.mock.web.MockHttpServletRequest; +import org.springframework.test.util.ReflectionTestUtils; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.Mockito.*; + +class PlatformAdminControllerDefaultTaskTest { + + @Test + void queuesDefaultTaskInitializationAfterSchoolCreationSucceeds() { + PlatformAdminController controller = new PlatformAdminController(); + PlatformAdminService platformAdminService = mock(PlatformAdminService.class); + SchoolProductConfigService configService = mock(SchoolProductConfigService.class); + SchoolDefaultTaskService defaultTaskService = mock(SchoolDefaultTaskService.class); + SchoolMapper schoolMapper = mock(SchoolMapper.class); + ReflectionTestUtils.setField(controller, "platformAdminService", platformAdminService); + ReflectionTestUtils.setField(controller, "schoolProductConfigService", configService); + ReflectionTestUtils.setField(controller, "schoolDefaultTaskService", defaultTaskService); + ReflectionTestUtils.setField(controller, "schoolMapper", schoolMapper); + when(schoolMapper.insertSelective(any())).thenReturn(1); + + School school = new School(); + school.setSchoolName("异步初始化学校"); + controller.addSchool(school, platformAdminRequest()); + + verify(defaultTaskService).enqueueInitialization(school.getSchoolId()); + } + + private MockHttpServletRequest platformAdminRequest() { + JwtUser user = new JwtUser(); + user.setUserId("platform-1"); + user.setRoleId(1); + user.setUsername("platform-admin"); + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setUserPrincipal(new UsernamePasswordAuthenticationToken(user, null)); + request.addHeader("Authorization", "Bearer " + TokenProvider.createToken(user)); + return request; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImplTest.java index eb425fc..0a4e5aa 100644 --- a/src/test/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImplTest.java +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/SchoolDefaultTaskServiceImplTest.java @@ -3,9 +3,11 @@ package com.sztzjy.linkCommerce.service.impl; import com.sztzjy.linkCommerce.entity.SchoolClass; import com.sztzjy.linkCommerce.entity.TaskAllocation; import com.sztzjy.linkCommerce.mapper.SchoolClassMapper; +import com.sztzjy.linkCommerce.mapper.SchoolDefaultTaskInitializationMapper; import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper; import org.junit.jupiter.api.Test; import org.springframework.test.util.ReflectionTestUtils; +import org.springframework.core.task.AsyncTaskExecutor; import java.util.Collections; import java.util.List; @@ -50,6 +52,22 @@ class SchoolDefaultTaskServiceImplTest { verify(taskMapper, never()).insert(any()); } + @Test + void enqueueInitializationRecordsPendingStateBeforeSubmittingBackgroundWork() { + TaskAllocationMapper taskMapper = mock(TaskAllocationMapper.class); + SchoolDefaultTaskInitializationMapper statusMapper = mock(SchoolDefaultTaskInitializationMapper.class); + AsyncTaskExecutor executor = mock(AsyncTaskExecutor.class); + SchoolDefaultTaskServiceImpl service = new SchoolDefaultTaskServiceImpl(); + ReflectionTestUtils.setField(service, "taskAllocationMapper", taskMapper); + ReflectionTestUtils.setField(service, "initializationMapper", statusMapper); + ReflectionTestUtils.setField(service, "taskExecutor", executor); + + service.enqueueInitialization("school-1"); + + verify(statusMapper).insert(argThat(row -> "school-1".equals(row.getSchoolId()) && "PENDING".equals(row.getStatus()))); + verify(executor).execute(any(Runnable.class)); + } + private TaskAllocation task(String module) { TaskAllocation task = new TaskAllocation(); task.setModule(module);