|
|
package com.sztzjy.linkCommerce.service.impl;
|
|
|
|
|
|
import com.alibaba.fastjson.JSON;
|
|
|
import com.alibaba.fastjson.JSONObject;
|
|
|
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
|
|
import com.sztzjy.linkCommerce.entity.SchoolClass;
|
|
|
import com.sztzjy.linkCommerce.entity.SchoolClassExample;
|
|
|
import com.sztzjy.linkCommerce.entity.TaskAllocation;
|
|
|
import com.sztzjy.linkCommerce.entity.TaskAllocationExample;
|
|
|
import com.sztzjy.linkCommerce.entity.TeachingClassStudent;
|
|
|
import com.sztzjy.linkCommerce.entity.TrainingTask;
|
|
|
import com.sztzjy.linkCommerce.entity.TrainingTaskClassConfig;
|
|
|
import com.sztzjy.linkCommerce.entity.TrainingTaskStep;
|
|
|
import com.sztzjy.linkCommerce.entity.importDto.TrainingTaskImportDTO;
|
|
|
import com.sztzjy.linkCommerce.mapper.SchoolClassMapper;
|
|
|
import com.sztzjy.linkCommerce.mapper.TaskAllocationMapper;
|
|
|
import com.sztzjy.linkCommerce.mapper.TeachingClassStudentMapper;
|
|
|
import com.sztzjy.linkCommerce.mapper.TrainingTaskClassConfigMapper;
|
|
|
import com.sztzjy.linkCommerce.mapper.TrainingTaskMapper;
|
|
|
import com.sztzjy.linkCommerce.service.TrainingTaskService;
|
|
|
import com.sztzjy.linkCommerce.service.StudentTeachingClassResolver;
|
|
|
import org.apache.commons.lang3.StringUtils;
|
|
|
import org.springframework.beans.factory.annotation.Autowired;
|
|
|
import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
import java.util.ArrayList;
|
|
|
import java.util.Arrays;
|
|
|
import java.util.Collections;
|
|
|
import java.util.Comparator;
|
|
|
import java.util.Date;
|
|
|
import java.util.LinkedHashMap;
|
|
|
import java.util.LinkedHashSet;
|
|
|
import java.util.List;
|
|
|
import java.util.Map;
|
|
|
import java.util.Set;
|
|
|
import java.util.UUID;
|
|
|
import java.util.regex.Pattern;
|
|
|
import java.util.stream.Collectors;
|
|
|
|
|
|
@Service
|
|
|
public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
private static final Pattern SPLIT_PATTERN = Pattern.compile("[;;||]");
|
|
|
private static final String COMPREHENSIVE_CASE_TRAINING_TASK_KEY = "comprehensive-case-training";
|
|
|
|
|
|
@Autowired
|
|
|
public TrainingTaskMapper trainingTaskMapper;
|
|
|
|
|
|
@Autowired
|
|
|
public TrainingTaskClassConfigMapper trainingTaskClassConfigMapper;
|
|
|
|
|
|
@Autowired
|
|
|
public SchoolClassMapper schoolClassMapper;
|
|
|
|
|
|
@Autowired
|
|
|
public TeachingClassStudentMapper teachingClassStudentMapper;
|
|
|
|
|
|
@Autowired(required = false)
|
|
|
public TaskAllocationMapper taskAllocationMapper;
|
|
|
|
|
|
@Autowired(required = false)
|
|
|
public StudentTeachingClassResolver studentTeachingClassResolver;
|
|
|
|
|
|
@Autowired(required = false)
|
|
|
public JdbcTemplate jdbcTemplate;
|
|
|
|
|
|
private boolean trainingTaskTableChecked = false;
|
|
|
private boolean trainingTaskClassConfigTableChecked = false;
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTask> list(String projectName, String taskName, Boolean enabledOnly) {
|
|
|
return listDefaults(projectName, taskName, enabledOnly);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTask> listDefaults(String projectName, String taskName, Boolean enabledOnly) {
|
|
|
ensureDefaultTasks(null);
|
|
|
Boolean enabledFilter = Boolean.TRUE.equals(enabledOnly) ? Boolean.TRUE : null;
|
|
|
return trainingTaskMapper.selectList(StringUtils.trimToNull(projectName), StringUtils.trimToNull(taskName), enabledFilter);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask getById(String id) {
|
|
|
ensureDefaultTasks(null);
|
|
|
return trainingTaskMapper.selectByPrimaryKey(id);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask getByTaskKey(String taskKey) {
|
|
|
ensureDefaultTasks(null);
|
|
|
return trainingTaskMapper.selectByTaskKey(StringUtils.trimToEmpty(taskKey));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask create(TrainingTask task, String operatorId) {
|
|
|
ensureTrainingTaskTable();
|
|
|
normalizeTask(task);
|
|
|
TrainingTask builtInTask = requireBuiltInTask(task.getTaskKey());
|
|
|
task.setProjectName(builtInTask.getProjectName());
|
|
|
task.setSteps(normalizeSteps(task.getSteps(), builtInTask));
|
|
|
task.setId(UUID.randomUUID().toString());
|
|
|
task.setCreatedBy(operatorId);
|
|
|
task.setCreateTime(new Date());
|
|
|
task.setUpdateTime(new Date());
|
|
|
trainingTaskMapper.insertSelective(task);
|
|
|
return task;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask update(String id, TrainingTask task) {
|
|
|
ensureTrainingTaskTable();
|
|
|
TrainingTask existing = trainingTaskMapper.selectByPrimaryKey(id);
|
|
|
if (existing == null) {
|
|
|
throw new IllegalArgumentException("实训任务不存在");
|
|
|
}
|
|
|
normalizeTask(task);
|
|
|
TrainingTask builtInTask = requireBuiltInTask(existing.getTaskKey());
|
|
|
task.setId(id);
|
|
|
task.setTaskKey(existing.getTaskKey());
|
|
|
task.setProjectName(existing.getProjectName());
|
|
|
task.setSort(existing.getSort());
|
|
|
task.setEnabled(existing.getEnabled());
|
|
|
task.setSteps(normalizeSteps(task.getSteps(), builtInTask));
|
|
|
task.setUpdateTime(new Date());
|
|
|
trainingTaskMapper.updateByPrimaryKeySelective(task);
|
|
|
return trainingTaskMapper.selectByPrimaryKey(id);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public void delete(String id) {
|
|
|
ensureTrainingTaskTable();
|
|
|
throw new IllegalArgumentException("系统内置实训任务数量固定,不允许删除");
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public int importTasks(List<TrainingTaskImportDTO> rows, String operatorId, List<String> errors) {
|
|
|
ensureTrainingTaskTable();
|
|
|
if (rows == null || rows.isEmpty()) {
|
|
|
errors.add("Excel中没有数据");
|
|
|
return 0;
|
|
|
}
|
|
|
int count = 0;
|
|
|
for (int i = 0; i < rows.size(); i++) {
|
|
|
TrainingTaskImportDTO row = rows.get(i);
|
|
|
int rowNumber = i + 2;
|
|
|
if (isBlankRow(row)) {
|
|
|
continue;
|
|
|
}
|
|
|
TrainingTask task = buildTask(row, rowNumber, errors);
|
|
|
if (task == null) {
|
|
|
continue;
|
|
|
}
|
|
|
TrainingTask existing = trainingTaskMapper.selectByTaskKey(task.getTaskKey());
|
|
|
if (existing == null) {
|
|
|
errors.add("第" + rowNumber + "行:实训任务为系统内置任务,不允许通过导入新增");
|
|
|
continue;
|
|
|
}
|
|
|
update(existing.getId(), task);
|
|
|
count++;
|
|
|
}
|
|
|
return count;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTaskImportDTO> templateRows() {
|
|
|
TrainingTaskImportDTO row = new TrainingTaskImportDTO();
|
|
|
row.setProjectName("互联网产品开发基础认知");
|
|
|
row.setTaskKey("new-product-survey");
|
|
|
row.setTaskName("新产品调查与分析");
|
|
|
row.setKnowledge("产品三层次理论用于拆解互联网新产品的价值构成。");
|
|
|
row.setBackground("围绕真实电商产品完成调研、分析与方案输出。");
|
|
|
row.setObjectives("掌握产品价值调查方法;能够完成三层次分析;形成调研结论");
|
|
|
row.setRequirements("按步骤完成信息收集、分析记录和结论输出。");
|
|
|
row.setSteps("选定产品;三层次分析;AI技术价值;总结建议");
|
|
|
row.setMaterialName("");
|
|
|
row.setMaterialUrl("");
|
|
|
row.setSort(1);
|
|
|
row.setEnabled("是");
|
|
|
return Collections.singletonList(row);
|
|
|
}
|
|
|
|
|
|
@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);
|
|
|
}
|
|
|
sort++;
|
|
|
}
|
|
|
ensureAllTeachingClassTasks(trainingTaskMapper.selectList(null, null, null), operatorId);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTask> listForTeachingClass(String teachingClassId, Boolean enabledOnly, JwtUser operator) {
|
|
|
requireReadableTeachingClass(teachingClassId, operator);
|
|
|
List<TrainingTask> defaults = trainingTaskMapper.selectList(null, null, null);
|
|
|
return filterEnabled(readClassTasks(teachingClassId, defaults), enabledOnly);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask getClassTaskByTaskKey(String teachingClassId, String taskKey, String operatorId) {
|
|
|
requireOwnedTeachingClass(teachingClassId, operatorId);
|
|
|
ensureDefaultTasks(null);
|
|
|
String normalizedTaskKey = StringUtils.trimToEmpty(taskKey);
|
|
|
List<TrainingTask> defaults = trainingTaskMapper.selectList(null, null, null);
|
|
|
ensureClassTasksInitialized(teachingClassId, defaults, operatorId);
|
|
|
TrainingTaskClassConfig classTask = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
|
|
|
if (classTask != null) {
|
|
|
return toTask(classTask);
|
|
|
}
|
|
|
return trainingTaskMapper.selectByTaskKey(normalizedTaskKey);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public TrainingTask saveClassTask(String teachingClassId, String taskKey, TrainingTask task, String operatorId) {
|
|
|
requireOwnedTeachingClass(teachingClassId, operatorId);
|
|
|
ensureDefaultTasks(null);
|
|
|
String normalizedTaskKey = StringUtils.trimToEmpty(taskKey);
|
|
|
TrainingTask builtInTask = requireBuiltInTask(normalizedTaskKey);
|
|
|
TrainingTask defaultTask = trainingTaskMapper.selectByTaskKey(normalizedTaskKey);
|
|
|
TrainingTask baseTask = defaultTask == null ? builtInTask : defaultTask;
|
|
|
ensureClassTasksInitialized(teachingClassId, trainingTaskMapper.selectList(null, null, null), operatorId);
|
|
|
if (task == null) {
|
|
|
throw new IllegalArgumentException("Training task cannot be null");
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getTaskKey())) {
|
|
|
task.setTaskKey(baseTask.getTaskKey());
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getProjectName())) {
|
|
|
task.setProjectName(baseTask.getProjectName());
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getTaskName())) {
|
|
|
task.setTaskName(baseTask.getTaskName());
|
|
|
}
|
|
|
normalizeTask(task);
|
|
|
task.setTaskKey(baseTask.getTaskKey());
|
|
|
task.setProjectName(baseTask.getProjectName());
|
|
|
task.setSort(baseTask.getSort());
|
|
|
task.setEnabled(task.getEnabled() == null ? Boolean.TRUE : task.getEnabled());
|
|
|
task.setSteps(normalizeSteps(task.getSteps(), builtInTask));
|
|
|
|
|
|
TrainingTaskClassConfig existing = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
|
|
|
TrainingTaskClassConfig config = toConfig(teachingClassId, task, operatorId);
|
|
|
config.setUpdateTime(new Date());
|
|
|
if (existing == null) {
|
|
|
config.setId(UUID.randomUUID().toString());
|
|
|
config.setCreateTime(new Date());
|
|
|
trainingTaskClassConfigMapper.insertSelective(config);
|
|
|
return toTask(config);
|
|
|
}
|
|
|
config.setId(existing.getId());
|
|
|
trainingTaskClassConfigMapper.updateByPrimaryKeySelective(config);
|
|
|
TrainingTaskClassConfig saved = trainingTaskClassConfigMapper.selectByPrimaryKey(existing.getId());
|
|
|
return toTask(saved == null ? config : saved);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void restoreClassTaskDefault(String teachingClassId, String taskKey, String operatorId) {
|
|
|
requireOwnedTeachingClass(teachingClassId, operatorId);
|
|
|
trainingTaskClassConfigMapper.deleteByTeachingClassAndTaskKey(
|
|
|
StringUtils.trimToEmpty(teachingClassId), StringUtils.trimToEmpty(taskKey));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
@Transactional(rollbackFor = Exception.class)
|
|
|
public void restoreClassTasksDefaults(String teachingClassId, String operatorId) {
|
|
|
requireOwnedTeachingClass(teachingClassId, operatorId);
|
|
|
trainingTaskClassConfigMapper.deleteByTeachingClass(StringUtils.trimToEmpty(teachingClassId));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask getStudentTaskByTaskKey(String taskKey, String studentUserId) {
|
|
|
return getStudentTaskByTeachingClass(taskKey, resolveStudentTeachingClassId(studentUserId));
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public TrainingTask getStudentTaskByTaskKey(String taskKey, JwtUser student) {
|
|
|
return getStudentTaskByTeachingClass(taskKey, requireStudentTeachingClass(student));
|
|
|
}
|
|
|
|
|
|
private TrainingTask getStudentTaskByTeachingClass(String taskKey, String teachingClassId) {
|
|
|
ensureDefaultTasks(null);
|
|
|
String normalizedTaskKey = StringUtils.trimToEmpty(taskKey);
|
|
|
TrainingTask defaultTask = trainingTaskMapper.selectByTaskKey(normalizedTaskKey);
|
|
|
if (StringUtils.isBlank(teachingClassId) && requiresExplicitPublication(normalizedTaskKey)) {
|
|
|
return null;
|
|
|
}
|
|
|
if (StringUtils.isNotBlank(teachingClassId)) {
|
|
|
Set<String> publishedTaskKeys = getPublishedTaskKeys(teachingClassId);
|
|
|
if ((publishedTaskKeys != null && !publishedTaskKeys.contains(normalizedTaskKey))
|
|
|
|| (requiresExplicitPublication(normalizedTaskKey) && publishedTaskKeys == null)) {
|
|
|
return null;
|
|
|
}
|
|
|
ensureClassTasksInitialized(teachingClassId, trainingTaskMapper.selectList(null, null, null), null);
|
|
|
TrainingTaskClassConfig classTask = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
|
|
|
if (classTask != null) {
|
|
|
return Boolean.TRUE.equals(classTask.getEnabled()) ? toTask(classTask) : null;
|
|
|
}
|
|
|
if (requiresExplicitPublication(normalizedTaskKey)) {
|
|
|
return null;
|
|
|
}
|
|
|
}
|
|
|
return defaultTask;
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTask> listForStudent(String studentUserId, Boolean enabledOnly) {
|
|
|
return listForStudentTeachingClass(resolveStudentTeachingClassId(studentUserId), enabledOnly);
|
|
|
}
|
|
|
|
|
|
@Override
|
|
|
public List<TrainingTask> listForStudent(JwtUser student, Boolean enabledOnly) {
|
|
|
return listForStudentTeachingClass(requireStudentTeachingClass(student), enabledOnly);
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> listForStudentTeachingClass(String teachingClassId, Boolean enabledOnly) {
|
|
|
ensureDefaultTasks(null);
|
|
|
List<TrainingTask> defaults = trainingTaskMapper.selectList(null, null, null);
|
|
|
if (StringUtils.isBlank(teachingClassId)) {
|
|
|
return filterEnabled(sortTasks(defaults).stream()
|
|
|
.filter(task -> !requiresExplicitPublication(task.getTaskKey()))
|
|
|
.collect(Collectors.toList()), enabledOnly);
|
|
|
}
|
|
|
List<TrainingTaskClassConfig> classTasks = ensureClassTasksInitialized(teachingClassId, defaults, null);
|
|
|
List<TrainingTask> tasks = filterEnabled(toTasks(classTasks), enabledOnly);
|
|
|
Set<String> publishedTaskKeys = getPublishedTaskKeys(teachingClassId);
|
|
|
if (publishedTaskKeys == null) {
|
|
|
return tasks.stream()
|
|
|
.filter(task -> !requiresExplicitPublication(task.getTaskKey()))
|
|
|
.collect(Collectors.toList());
|
|
|
}
|
|
|
return tasks.stream()
|
|
|
.filter(task -> publishedTaskKeys.contains(task.getTaskKey()))
|
|
|
.filter(task -> !requiresExplicitPublication(task.getTaskKey()) || Boolean.TRUE.equals(task.getEnabled()))
|
|
|
.collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
private boolean requiresExplicitPublication(String taskKey) {
|
|
|
return COMPREHENSIVE_CASE_TRAINING_TASK_KEY.equals(StringUtils.trimToEmpty(taskKey));
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
* @return null when this teaching class has never been configured (default: all tasks),
|
|
|
* otherwise the explicitly published task keys, which may be empty.
|
|
|
*/
|
|
|
private Set<String> getPublishedTaskKeys(String teachingClassId) {
|
|
|
if (taskAllocationMapper == null || StringUtils.isBlank(teachingClassId)) {
|
|
|
return null;
|
|
|
}
|
|
|
TaskAllocationExample example = new TaskAllocationExample();
|
|
|
example.createCriteria().andClassIdEqualTo(teachingClassId);
|
|
|
List<TaskAllocation> rows = taskAllocationMapper.selectByExample(example);
|
|
|
boolean configured = false;
|
|
|
Set<String> taskKeys = new LinkedHashSet<>();
|
|
|
if (rows != null) {
|
|
|
for (TaskAllocation row : rows) {
|
|
|
if (TaskAllocation.PUBLICATION_MARKER.equals(row.getModule())) {
|
|
|
configured = true;
|
|
|
} else if (row.getDisabledStatus() == null || row.getDisabledStatus() == 0) {
|
|
|
taskKeys.add(row.getModule());
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return configured ? taskKeys : null;
|
|
|
}
|
|
|
|
|
|
private String requireStudentTeachingClass(JwtUser student) {
|
|
|
if (studentTeachingClassResolver == null) {
|
|
|
return resolveStudentTeachingClassId(student == null ? null : student.getUserId());
|
|
|
}
|
|
|
return studentTeachingClassResolver.resolveRequired(student);
|
|
|
}
|
|
|
|
|
|
private SchoolClass requireOwnedTeachingClass(String teachingClassId, String operatorId) {
|
|
|
SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(StringUtils.trimToEmpty(teachingClassId));
|
|
|
if (schoolClass == null || !"TEACHING".equals(schoolClass.getClassType())) {
|
|
|
throw new IllegalArgumentException("Teaching class not found");
|
|
|
}
|
|
|
if (!StringUtils.equals(schoolClass.getCreatedBy(), operatorId)) {
|
|
|
throw new IllegalArgumentException("Only owned teaching classes can be configured");
|
|
|
}
|
|
|
return schoolClass;
|
|
|
}
|
|
|
|
|
|
SchoolClass requireReadableTeachingClass(String teachingClassId, JwtUser operator) {
|
|
|
SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(StringUtils.trimToEmpty(teachingClassId));
|
|
|
if (schoolClass == null || !"TEACHING".equals(schoolClass.getClassType())) {
|
|
|
throw new IllegalArgumentException("Teaching class not found");
|
|
|
}
|
|
|
if (operator == null || StringUtils.isBlank(operator.getUserId())
|
|
|
|| StringUtils.isBlank(operator.getSchoolId()) || StringUtils.isBlank(schoolClass.getSchoolId())
|
|
|
|| !StringUtils.equals(operator.getSchoolId(), schoolClass.getSchoolId())) {
|
|
|
throw new IllegalArgumentException("Teaching class is unavailable");
|
|
|
}
|
|
|
return schoolClass;
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> readClassTasks(String teachingClassId, List<TrainingTask> defaults) {
|
|
|
Map<String, TrainingTask> tasksByKey = new LinkedHashMap<>();
|
|
|
List<TrainingTask> sourceTasks = defaults == null || defaults.isEmpty() ? defaultTasks() : defaults;
|
|
|
for (TrainingTask task : sourceTasks) {
|
|
|
if (task != null && StringUtils.isNotBlank(task.getTaskKey())) {
|
|
|
tasksByKey.put(task.getTaskKey(), task);
|
|
|
}
|
|
|
}
|
|
|
if (trainingTaskClassConfigMapper != null) {
|
|
|
List<TrainingTaskClassConfig> classTasks = trainingTaskClassConfigMapper.selectListByTeachingClass(teachingClassId, null);
|
|
|
if (classTasks != null) {
|
|
|
for (TrainingTaskClassConfig classTask : classTasks) {
|
|
|
if (classTask != null && StringUtils.isNotBlank(classTask.getTaskKey())) {
|
|
|
tasksByKey.put(classTask.getTaskKey(), toTask(classTask));
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return sortTasks(new ArrayList<>(tasksByKey.values()));
|
|
|
}
|
|
|
|
|
|
private String resolveStudentTeachingClassId(String studentUserId) {
|
|
|
if (StringUtils.isBlank(studentUserId) || teachingClassStudentMapper == null) {
|
|
|
return null;
|
|
|
}
|
|
|
TeachingClassStudent membership = teachingClassStudentMapper.selectActiveByStudentUserId(StringUtils.trimToEmpty(studentUserId));
|
|
|
return membership == null ? null : membership.getTeachingClassId();
|
|
|
}
|
|
|
|
|
|
private List<TrainingTaskClassConfig> ensureClassTasksInitialized(String teachingClassId, List<TrainingTask> defaults, String operatorId) {
|
|
|
if (StringUtils.isBlank(teachingClassId) || trainingTaskClassConfigMapper == null) {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
ensureTrainingTaskClassConfigTable();
|
|
|
List<TrainingTask> sourceTasks = defaults == null || defaults.isEmpty() ? defaultTasks() : defaults;
|
|
|
List<TrainingTaskClassConfig> existing = trainingTaskClassConfigMapper.selectListByTeachingClass(teachingClassId, null);
|
|
|
Map<String, TrainingTaskClassConfig> existingByKey = new LinkedHashMap<>();
|
|
|
if (existing != null) {
|
|
|
for (TrainingTaskClassConfig config : existing) {
|
|
|
if (config != null && StringUtils.isNotBlank(config.getTaskKey())) {
|
|
|
existingByKey.put(config.getTaskKey(), config);
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
String creator = resolveClassTaskCreator(teachingClassId, operatorId);
|
|
|
boolean inserted = false;
|
|
|
for (TrainingTask task : sourceTasks) {
|
|
|
if (task == null || StringUtils.isBlank(task.getTaskKey()) || existingByKey.containsKey(task.getTaskKey())) {
|
|
|
continue;
|
|
|
}
|
|
|
TrainingTaskClassConfig config = toConfig(teachingClassId, task, creator);
|
|
|
Date now = new Date();
|
|
|
config.setId(UUID.randomUUID().toString());
|
|
|
config.setCreateTime(now);
|
|
|
config.setUpdateTime(now);
|
|
|
trainingTaskClassConfigMapper.insertSelective(config);
|
|
|
existingByKey.put(config.getTaskKey(), config);
|
|
|
inserted = true;
|
|
|
}
|
|
|
if (inserted) {
|
|
|
List<TrainingTaskClassConfig> refreshed = trainingTaskClassConfigMapper.selectListByTeachingClass(teachingClassId, null);
|
|
|
if (refreshed != null) {
|
|
|
return refreshed;
|
|
|
}
|
|
|
}
|
|
|
return new ArrayList<>(existingByKey.values());
|
|
|
}
|
|
|
|
|
|
private void ensureAllTeachingClassTasks(List<TrainingTask> defaults, String operatorId) {
|
|
|
if (schoolClassMapper == null || trainingTaskClassConfigMapper == null) {
|
|
|
return;
|
|
|
}
|
|
|
SchoolClassExample example = new SchoolClassExample();
|
|
|
example.createCriteria().andClassTypeEqualTo("TEACHING");
|
|
|
List<SchoolClass> teachingClasses = schoolClassMapper.selectByExample(example);
|
|
|
if (teachingClasses == null || teachingClasses.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
for (SchoolClass teachingClass : teachingClasses) {
|
|
|
if (teachingClass == null || StringUtils.isBlank(teachingClass.getSchoolClassId())) {
|
|
|
continue;
|
|
|
}
|
|
|
String creator = StringUtils.defaultIfBlank(operatorId, teachingClass.getCreatedBy());
|
|
|
ensureClassTasksInitialized(teachingClass.getSchoolClassId(), defaults, creator);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private String resolveClassTaskCreator(String teachingClassId, String operatorId) {
|
|
|
if (StringUtils.isNotBlank(operatorId)) {
|
|
|
return operatorId;
|
|
|
}
|
|
|
if (schoolClassMapper == null) {
|
|
|
return null;
|
|
|
}
|
|
|
SchoolClass schoolClass = schoolClassMapper.selectByPrimaryKey(StringUtils.trimToEmpty(teachingClassId));
|
|
|
return schoolClass == null ? null : schoolClass.getCreatedBy();
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> toTasks(List<TrainingTaskClassConfig> configs) {
|
|
|
if (configs == null) {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
return sortTasks(configs.stream()
|
|
|
.map(this::toTask)
|
|
|
.filter(task -> task != null)
|
|
|
.collect(Collectors.toList()));
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> sortTasks(List<TrainingTask> tasks) {
|
|
|
if (tasks == null) {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
return tasks.stream()
|
|
|
.sorted(Comparator.comparing(task -> task.getSort() == null ? 0 : task.getSort()))
|
|
|
.collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> filterEnabled(List<TrainingTask> tasks, Boolean enabledOnly) {
|
|
|
if (!Boolean.TRUE.equals(enabledOnly)) {
|
|
|
return tasks;
|
|
|
}
|
|
|
return tasks.stream()
|
|
|
.filter(task -> Boolean.TRUE.equals(task.getEnabled()))
|
|
|
.collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
private TrainingTask toTask(TrainingTaskClassConfig config) {
|
|
|
if (config == null) {
|
|
|
return null;
|
|
|
}
|
|
|
TrainingTask task = new TrainingTask();
|
|
|
task.setId(config.getId());
|
|
|
task.setTaskKey(config.getTaskKey());
|
|
|
task.setProjectName(config.getProjectName());
|
|
|
task.setTaskName(config.getTaskName());
|
|
|
task.setKnowledge(config.getKnowledge());
|
|
|
task.setBackground(config.getBackground());
|
|
|
task.setObjectives(config.getObjectives());
|
|
|
task.setRequirements(config.getRequirements());
|
|
|
task.setSteps(config.getSteps());
|
|
|
task.setMaterialName(config.getMaterialName());
|
|
|
task.setMaterialUrl(config.getMaterialUrl());
|
|
|
task.setSort(config.getSort());
|
|
|
task.setEnabled(config.getEnabled());
|
|
|
task.setCreatedBy(config.getCreatedBy());
|
|
|
task.setCreateTime(config.getCreateTime());
|
|
|
task.setUpdateTime(config.getUpdateTime());
|
|
|
return task;
|
|
|
}
|
|
|
|
|
|
private TrainingTaskClassConfig toConfig(String teachingClassId, TrainingTask task, String operatorId) {
|
|
|
TrainingTaskClassConfig config = new TrainingTaskClassConfig();
|
|
|
config.setTeachingClassId(teachingClassId);
|
|
|
config.setTaskKey(task.getTaskKey());
|
|
|
config.setProjectName(task.getProjectName());
|
|
|
config.setTaskName(task.getTaskName());
|
|
|
config.setKnowledge(task.getKnowledge());
|
|
|
config.setBackground(task.getBackground());
|
|
|
config.setObjectives(task.getObjectives());
|
|
|
config.setRequirements(task.getRequirements());
|
|
|
config.setSteps(task.getSteps());
|
|
|
config.setMaterialName(task.getMaterialName());
|
|
|
config.setMaterialUrl(task.getMaterialUrl());
|
|
|
config.setSort(task.getSort());
|
|
|
config.setEnabled(task.getEnabled());
|
|
|
config.setCreatedBy(operatorId);
|
|
|
return config;
|
|
|
}
|
|
|
|
|
|
private void ensureTrainingTaskTable() {
|
|
|
if (trainingTaskTableChecked || jdbcTemplate == null) {
|
|
|
return;
|
|
|
}
|
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS training_task (" +
|
|
|
"id varchar(64) NOT NULL COMMENT '主键ID'," +
|
|
|
"task_key varchar(128) NOT NULL COMMENT '学生端页面任务标识'," +
|
|
|
"project_name varchar(128) NOT NULL COMMENT '所属项目'," +
|
|
|
"task_name varchar(128) NOT NULL COMMENT '实训任务名称'," +
|
|
|
"knowledge text NULL COMMENT '任务分析'," +
|
|
|
"background text NULL COMMENT '实训背景'," +
|
|
|
"objectives text NULL COMMENT '实训目标JSON数组'," +
|
|
|
"requirements text NULL COMMENT '实训要求'," +
|
|
|
"steps text NULL COMMENT '步骤名称JSON数组'," +
|
|
|
"material_name varchar(255) NULL COMMENT '资料名称'," +
|
|
|
"material_url varchar(500) NULL COMMENT '资料地址'," +
|
|
|
"sort int DEFAULT 0 COMMENT '排序'," +
|
|
|
"enabled bit(1) DEFAULT b'1' COMMENT '是否启用'," +
|
|
|
"created_by varchar(64) NULL COMMENT '创建人'," +
|
|
|
"create_time datetime NULL COMMENT '创建时间'," +
|
|
|
"update_time datetime NULL COMMENT '更新时间'," +
|
|
|
"PRIMARY KEY (id)," +
|
|
|
"UNIQUE KEY uk_training_task_key (task_key)," +
|
|
|
"KEY idx_training_task_project (project_name)," +
|
|
|
"KEY idx_training_task_sort (sort)" +
|
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='实训任务配置'");
|
|
|
jdbcTemplate.execute("ALTER TABLE training_task CONVERT TO CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci");
|
|
|
trainingTaskTableChecked = true;
|
|
|
}
|
|
|
|
|
|
private void ensureTrainingTaskClassConfigTable() {
|
|
|
if (trainingTaskClassConfigTableChecked || jdbcTemplate == null) {
|
|
|
return;
|
|
|
}
|
|
|
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS training_task_class_config (" +
|
|
|
"id varchar(64) NOT NULL," +
|
|
|
"teaching_class_id varchar(64) NOT NULL," +
|
|
|
"task_key varchar(128) NOT NULL," +
|
|
|
"project_name varchar(128) NOT NULL," +
|
|
|
"task_name varchar(128) NOT NULL," +
|
|
|
"knowledge text NULL," +
|
|
|
"background text NULL," +
|
|
|
"objectives text NULL," +
|
|
|
"requirements text NULL," +
|
|
|
"steps text NULL," +
|
|
|
"material_name varchar(255) NULL," +
|
|
|
"material_url varchar(500) NULL," +
|
|
|
"sort int DEFAULT 0," +
|
|
|
"enabled bit(1) DEFAULT b'1'," +
|
|
|
"created_by varchar(64) NULL," +
|
|
|
"create_time datetime NULL," +
|
|
|
"update_time datetime NULL," +
|
|
|
"PRIMARY KEY (id)," +
|
|
|
"UNIQUE KEY uk_training_task_class_key (teaching_class_id, task_key)," +
|
|
|
"KEY idx_training_task_class_enabled_sort (teaching_class_id, enabled, sort)," +
|
|
|
"KEY idx_training_task_class_created_by (created_by)" +
|
|
|
") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='Class scoped training task config'");
|
|
|
trainingTaskClassConfigTableChecked = true;
|
|
|
}
|
|
|
|
|
|
TrainingTask buildTask(TrainingTaskImportDTO row, int rowNumber, List<String> errors) {
|
|
|
String projectName = StringUtils.trimToEmpty(row.getProjectName());
|
|
|
String taskKey = StringUtils.trimToEmpty(row.getTaskKey());
|
|
|
String taskName = StringUtils.trimToEmpty(row.getTaskName());
|
|
|
if (StringUtils.isBlank(projectName)) {
|
|
|
errors.add("第 " + rowNumber + " 行:所属项目不能为空");
|
|
|
}
|
|
|
if (StringUtils.isBlank(taskKey)) {
|
|
|
errors.add("第 " + rowNumber + " 行:任务标识不能为空");
|
|
|
}
|
|
|
if (StringUtils.isBlank(taskName)) {
|
|
|
errors.add("第 " + rowNumber + " 行:实训任务名称不能为空");
|
|
|
}
|
|
|
TrainingTask builtInTask = getBuiltInTask(taskKey);
|
|
|
if (StringUtils.isNotBlank(taskKey) && builtInTask == null) {
|
|
|
errors.add("第 " + rowNumber + " 行:任务标识不是系统内置实训任务");
|
|
|
}
|
|
|
Boolean enabled = parseEnabled(row.getEnabled());
|
|
|
if (StringUtils.isNotBlank(row.getEnabled()) && enabled == null) {
|
|
|
errors.add("第 " + rowNumber + " 行:是否启用请填写是或否");
|
|
|
}
|
|
|
if (hasRowError(errors, rowNumber)) {
|
|
|
return null;
|
|
|
}
|
|
|
TrainingTask task = new TrainingTask();
|
|
|
task.setProjectName(projectName);
|
|
|
task.setTaskKey(taskKey);
|
|
|
task.setTaskName(taskName);
|
|
|
task.setKnowledge(StringUtils.trimToEmpty(row.getKnowledge()));
|
|
|
task.setBackground(StringUtils.trimToEmpty(row.getBackground()));
|
|
|
task.setObjectives(toJsonArray(splitList(row.getObjectives())));
|
|
|
task.setRequirements(StringUtils.trimToEmpty(row.getRequirements()));
|
|
|
task.setSteps(normalizeSteps(row.getSteps(), builtInTask));
|
|
|
task.setMaterialName(StringUtils.trimToEmpty(row.getMaterialName()));
|
|
|
task.setMaterialUrl(StringUtils.trimToEmpty(row.getMaterialUrl()));
|
|
|
task.setSort(row.getSort());
|
|
|
task.setEnabled(enabled == null ? Boolean.TRUE : enabled);
|
|
|
return task;
|
|
|
}
|
|
|
|
|
|
public List<String> splitList(String value) {
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
return Arrays.stream(SPLIT_PATTERN.split(value))
|
|
|
.map(StringUtils::trimToEmpty)
|
|
|
.filter(StringUtils::isNotBlank)
|
|
|
.collect(Collectors.toList());
|
|
|
}
|
|
|
|
|
|
private void normalizeTask(TrainingTask task) {
|
|
|
if (task == null) {
|
|
|
throw new IllegalArgumentException("实训任务不能为空");
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getProjectName())) {
|
|
|
throw new IllegalArgumentException("所属项目不能为空");
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getTaskKey())) {
|
|
|
throw new IllegalArgumentException("任务标识不能为空");
|
|
|
}
|
|
|
if (StringUtils.isBlank(task.getTaskName())) {
|
|
|
throw new IllegalArgumentException("实训任务名称不能为空");
|
|
|
}
|
|
|
task.setProjectName(StringUtils.trimToEmpty(task.getProjectName()));
|
|
|
task.setTaskKey(StringUtils.trimToEmpty(task.getTaskKey()));
|
|
|
task.setTaskName(StringUtils.trimToEmpty(task.getTaskName()));
|
|
|
task.setKnowledge(StringUtils.trimToEmpty(task.getKnowledge()));
|
|
|
task.setBackground(StringUtils.trimToEmpty(task.getBackground()));
|
|
|
task.setObjectives(normalizeJsonArray(task.getObjectives()));
|
|
|
task.setRequirements(StringUtils.trimToEmpty(task.getRequirements()));
|
|
|
task.setSteps(normalizeJsonArray(task.getSteps()));
|
|
|
task.setMaterialName(StringUtils.trimToEmpty(task.getMaterialName()));
|
|
|
task.setMaterialUrl(StringUtils.trimToEmpty(task.getMaterialUrl()));
|
|
|
if (task.getEnabled() == null) {
|
|
|
task.setEnabled(Boolean.TRUE);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
private TrainingTask requireBuiltInTask(String taskKey) {
|
|
|
TrainingTask builtInTask = getBuiltInTask(taskKey);
|
|
|
if (builtInTask == null) {
|
|
|
throw new IllegalArgumentException("任务标识不是系统内置实训任务");
|
|
|
}
|
|
|
return builtInTask;
|
|
|
}
|
|
|
|
|
|
private TrainingTask getBuiltInTask(String taskKey) {
|
|
|
String normalizedTaskKey = StringUtils.trimToEmpty(taskKey);
|
|
|
for (TrainingTask task : defaultTasks()) {
|
|
|
if (normalizedTaskKey.equals(task.getTaskKey())) {
|
|
|
return task;
|
|
|
}
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
private String normalizeSteps(String value, TrainingTask builtInTask) {
|
|
|
List<TrainingTaskStep> steps = parseSteps(value, builtInTask);
|
|
|
if (steps.isEmpty()) {
|
|
|
throw new IllegalArgumentException("至少保留一个步骤");
|
|
|
}
|
|
|
return JSON.toJSONString(steps);
|
|
|
}
|
|
|
|
|
|
private List<TrainingTaskStep> parseSteps(String value, TrainingTask builtInTask) {
|
|
|
List<TrainingTaskStep> result = new ArrayList<>();
|
|
|
String source = StringUtils.isBlank(value) && builtInTask != null ? builtInTask.getSteps() : value;
|
|
|
if (StringUtils.isBlank(source)) {
|
|
|
return result;
|
|
|
}
|
|
|
String trimmed = StringUtils.trimToEmpty(source);
|
|
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
|
try {
|
|
|
List<?> values = JSON.parseArray(trimmed, Object.class);
|
|
|
if (values != null) {
|
|
|
for (Object valueItem : values) {
|
|
|
if (valueItem instanceof JSONObject) {
|
|
|
JSONObject item = (JSONObject) valueItem;
|
|
|
addStep(result, item.getString("id"), item.getString("name"), item.getString("kind"), builtInTask);
|
|
|
} else {
|
|
|
addStep(result, null, String.valueOf(valueItem), null, builtInTask);
|
|
|
}
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
} catch (Exception ignored) {
|
|
|
// Fall through to legacy delimiter parsing.
|
|
|
}
|
|
|
}
|
|
|
for (String name : parseJsonOrDelimitedList(source)) {
|
|
|
addStep(result, null, name, null, builtInTask);
|
|
|
}
|
|
|
return result;
|
|
|
}
|
|
|
|
|
|
private void addStep(List<TrainingTaskStep> steps, String id, String name, String kind, TrainingTask builtInTask) {
|
|
|
String normalizedName = StringUtils.trimToEmpty(name);
|
|
|
if (StringUtils.isBlank(normalizedName)) {
|
|
|
return;
|
|
|
}
|
|
|
int position = steps.size() + 1;
|
|
|
int builtInCount = parseJsonOrDelimitedList(builtInTask == null ? null : builtInTask.getSteps()).size();
|
|
|
boolean builtIn = "builtin".equalsIgnoreCase(StringUtils.trimToEmpty(kind))
|
|
|
|| (StringUtils.isBlank(kind) && position <= builtInCount);
|
|
|
String normalizedKind = builtIn ? "builtin" : "custom";
|
|
|
String normalizedId = StringUtils.trimToEmpty(id);
|
|
|
if (StringUtils.isBlank(normalizedId)) {
|
|
|
normalizedId = (builtIn ? "builtin-" : "custom-") + position;
|
|
|
}
|
|
|
steps.add(new TrainingTaskStep(normalizedId, normalizedName, normalizedKind));
|
|
|
}
|
|
|
|
|
|
private List<String> parseJsonOrDelimitedList(String value) {
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
return new ArrayList<>();
|
|
|
}
|
|
|
String trimmed = StringUtils.trimToEmpty(value);
|
|
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
|
try {
|
|
|
List<String> parsed = JSON.parseArray(trimmed, String.class);
|
|
|
return parsed == null ? new ArrayList<>() : parsed;
|
|
|
} catch (Exception ignored) {
|
|
|
return splitList(trimmed);
|
|
|
}
|
|
|
}
|
|
|
return splitList(trimmed);
|
|
|
}
|
|
|
|
|
|
private String normalizeJsonArray(String value) {
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
return "[]";
|
|
|
}
|
|
|
String trimmed = StringUtils.trimToEmpty(value);
|
|
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
|
|
|
return trimmed;
|
|
|
}
|
|
|
return toJsonArray(splitList(trimmed));
|
|
|
}
|
|
|
|
|
|
private String toJsonArray(List<String> list) {
|
|
|
return JSON.toJSONString(list == null ? Collections.emptyList() : list);
|
|
|
}
|
|
|
|
|
|
private Boolean parseEnabled(String value) {
|
|
|
String normalized = StringUtils.trimToEmpty(value);
|
|
|
if (StringUtils.isBlank(normalized)) {
|
|
|
return Boolean.TRUE;
|
|
|
}
|
|
|
if ("是".equals(normalized) || "启用".equals(normalized) || "true".equalsIgnoreCase(normalized) || "1".equals(normalized)) {
|
|
|
return Boolean.TRUE;
|
|
|
}
|
|
|
if ("否".equals(normalized) || "禁用".equals(normalized) || "false".equalsIgnoreCase(normalized) || "0".equals(normalized)) {
|
|
|
return Boolean.FALSE;
|
|
|
}
|
|
|
return null;
|
|
|
}
|
|
|
|
|
|
private boolean isBlankRow(TrainingTaskImportDTO row) {
|
|
|
if (row == null) {
|
|
|
return true;
|
|
|
}
|
|
|
return StringUtils.isBlank(row.getProjectName())
|
|
|
&& StringUtils.isBlank(row.getTaskKey())
|
|
|
&& StringUtils.isBlank(row.getTaskName())
|
|
|
&& StringUtils.isBlank(row.getKnowledge())
|
|
|
&& StringUtils.isBlank(row.getBackground())
|
|
|
&& StringUtils.isBlank(row.getObjectives())
|
|
|
&& StringUtils.isBlank(row.getRequirements())
|
|
|
&& StringUtils.isBlank(row.getSteps())
|
|
|
&& StringUtils.isBlank(row.getMaterialName())
|
|
|
&& StringUtils.isBlank(row.getMaterialUrl())
|
|
|
&& row.getSort() == null
|
|
|
&& StringUtils.isBlank(row.getEnabled());
|
|
|
}
|
|
|
|
|
|
private boolean hasRowError(List<String> errors, int rowNumber) {
|
|
|
String prefix = "第 " + rowNumber + " 行:";
|
|
|
for (String error : errors) {
|
|
|
if (error.startsWith(prefix)) {
|
|
|
return true;
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
private List<TrainingTask> defaultTasks() {
|
|
|
List<TrainingTask> tasks = new ArrayList<>();
|
|
|
addDefault(tasks, "综合实训", COMPREHENSIVE_CASE_TRAINING_TASK_KEY, "综合案例实训");
|
|
|
addDefault(tasks, "互联网产品开发基础认知", "new-product-survey", "新产品调查与分析");
|
|
|
addDefault(tasks, "互联网产品开发基础认知", "product-development-factors", "产品开发关键因素");
|
|
|
addDefault(tasks, "互联网产品开发基础认知", "product-development-process", "产品开发主要流程");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "industry-demand-analysis", "行业需求分析");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "competitive-environment-analysis", "竞争环境分析");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "market-opportunity-selection", "市场机会识别与选品");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "business-feasibility", "商业可行性论证");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "target-user-profile", "目标用户画像");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "consumer-behavior", "消费者需求与行为");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "consumer-scenario", "消费场景解构");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "feature-conversion", "需求转化为功能");
|
|
|
addDefault(tasks, "市场洞察与需求分析", "product-value-positioning", "产品价值主张及定位");
|
|
|
addDefault(tasks, "产品规划与设计", "product-structure", "产品架构设计");
|
|
|
addDefault(tasks, "产品规划与设计", "after-sales-service", "售后服务流程设计");
|
|
|
addDefault(tasks, "产品规划与设计", "requirements-doc", "产品需求说明书");
|
|
|
addDefault(tasks, "产品开发与测试验证", "hardware-cost", "智能硬件成本估算");
|
|
|
addDefault(tasks, "产品开发与测试验证", "supplier-evaluation", "供应渠道评估");
|
|
|
addDefault(tasks, "产品开发与测试验证", "purchase-demand-forecast", "采购需求预测");
|
|
|
addDefault(tasks, "产品开发与测试验证", "product-pricing", "产品定价");
|
|
|
addDefault(tasks, "产品上线与运营推广", "category-optimization", "品类结构优化");
|
|
|
addDefault(tasks, "产品上线与运营推广", "release-channel-evaluation", "产品发布渠道评估");
|
|
|
addDefault(tasks, "产品上线与运营推广", "promotion-effect-optimization", "投放效果评估与优化");
|
|
|
addDefault(tasks, "产品上线与运营推广", "product-diagnosis-iteration", "产品问题诊断与迭代");
|
|
|
return tasks;
|
|
|
}
|
|
|
|
|
|
private void addDefault(List<TrainingTask> tasks, String projectName, String taskKey, String taskName) {
|
|
|
TrainingTask task = new TrainingTask();
|
|
|
task.setProjectName(projectName);
|
|
|
task.setTaskKey(taskKey);
|
|
|
task.setTaskName(taskName);
|
|
|
task.setKnowledge(taskName + "任务分析由教师在实训任务管理中维护。");
|
|
|
task.setBackground(taskName + "围绕真实业务场景展开,要求学生完成调研、分析与成果输出。");
|
|
|
task.setObjectives(toJsonArray(Arrays.asList("理解" + taskName + "的核心方法", "能够按步骤完成实训成果")));
|
|
|
task.setRequirements("按任务步骤完成信息收集、分析记录和结论输出,提交内容需要结构清晰、依据充分。");
|
|
|
task.setSteps(toJsonArray(Arrays.asList("任务理解", "资料收集", "分析整理", "成果提交")));
|
|
|
task.setMaterialName("");
|
|
|
task.setMaterialUrl("");
|
|
|
task.setEnabled(Boolean.TRUE);
|
|
|
tasks.add(task);
|
|
|
}
|
|
|
}
|