|
|
|
|
@ -46,6 +46,9 @@ import java.util.stream.Collectors;
|
|
|
|
|
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";
|
|
|
|
|
private static final Set<String> STEP_ONE_CHOICE_TASK_KEYS = new LinkedHashSet<>(Arrays.asList(
|
|
|
|
|
"new-product-survey", "industry-demand-analysis", "competitive-environment-analysis"));
|
|
|
|
|
private static final List<String> STEP_ONE_CHOICE_OPTION_KEYS = Arrays.asList("A", "B", "C", "D", "E", "F");
|
|
|
|
|
|
|
|
|
|
@Autowired
|
|
|
|
|
public TrainingTaskMapper trainingTaskMapper;
|
|
|
|
|
@ -140,6 +143,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
task.setSort(existing.getSort());
|
|
|
|
|
task.setEnabled(existing.getEnabled());
|
|
|
|
|
task.setSteps(normalizeSteps(task.getSteps(), builtInTask));
|
|
|
|
|
task.setStepOneQuestions(normalizeStepOneQuestions(existing.getTaskKey(), task.getStepOneQuestions()));
|
|
|
|
|
task.setUpdateTime(new Date());
|
|
|
|
|
trainingTaskMapper.updateByPrimaryKeySelective(task);
|
|
|
|
|
synchronizeClassTaskDefaults(existing, task);
|
|
|
|
|
@ -276,6 +280,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
task.setSort(baseTask.getSort());
|
|
|
|
|
task.setEnabled(task.getEnabled() == null ? Boolean.TRUE : task.getEnabled());
|
|
|
|
|
task.setSteps(normalizeSteps(task.getSteps(), builtInTask));
|
|
|
|
|
task.setStepOneQuestions(normalizeStepOneQuestions(baseTask.getTaskKey(), task.getStepOneQuestions()));
|
|
|
|
|
|
|
|
|
|
TrainingTaskClassConfig existing = trainingTaskClassConfigMapper.selectByTeachingClassAndTaskKey(teachingClassId, normalizedTaskKey);
|
|
|
|
|
TrainingTaskClassConfig config = toConfig(teachingClassId, task, operatorId);
|
|
|
|
|
@ -593,6 +598,10 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
update.setSteps(updatedTask.getSteps());
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if (shouldSynchronize(classTask.getStepOneQuestions(), previousTask.getStepOneQuestions(), updatedTask.getStepOneQuestions())) {
|
|
|
|
|
update.setStepOneQuestions(updatedTask.getStepOneQuestions());
|
|
|
|
|
changed = true;
|
|
|
|
|
}
|
|
|
|
|
if (shouldSynchronize(classTask.getMaterialName(), previousTask.getMaterialName(), updatedTask.getMaterialName())) {
|
|
|
|
|
update.setMaterialName(updatedTask.getMaterialName());
|
|
|
|
|
changed = true;
|
|
|
|
|
@ -668,6 +677,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
task.setObjectives(config.getObjectives());
|
|
|
|
|
task.setRequirements(config.getRequirements());
|
|
|
|
|
task.setSteps(config.getSteps());
|
|
|
|
|
task.setStepOneQuestions(config.getStepOneQuestions());
|
|
|
|
|
task.setMaterialName(config.getMaterialName());
|
|
|
|
|
task.setMaterialUrl(config.getMaterialUrl());
|
|
|
|
|
task.setSort(config.getSort());
|
|
|
|
|
@ -689,6 +699,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
config.setObjectives(task.getObjectives());
|
|
|
|
|
config.setRequirements(task.getRequirements());
|
|
|
|
|
config.setSteps(task.getSteps());
|
|
|
|
|
config.setStepOneQuestions(task.getStepOneQuestions());
|
|
|
|
|
config.setMaterialName(task.getMaterialName());
|
|
|
|
|
config.setMaterialUrl(task.getMaterialUrl());
|
|
|
|
|
config.setSort(task.getSort());
|
|
|
|
|
@ -711,6 +722,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
"objectives text NULL COMMENT '实训目标JSON数组'," +
|
|
|
|
|
"requirements text NULL COMMENT '实训要求'," +
|
|
|
|
|
"steps text NULL COMMENT '步骤名称JSON数组'," +
|
|
|
|
|
"step_one_questions text NULL COMMENT '第一步选择题JSON数组'," +
|
|
|
|
|
"material_name varchar(255) NULL COMMENT '资料名称'," +
|
|
|
|
|
"material_url varchar(500) NULL COMMENT '资料地址'," +
|
|
|
|
|
"sort int DEFAULT 0 COMMENT '排序'," +
|
|
|
|
|
@ -724,6 +736,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
"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");
|
|
|
|
|
ensureTextColumn("training_task", "step_one_questions", "第一步选择题JSON数组");
|
|
|
|
|
trainingTaskTableChecked = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@ -742,6 +755,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
"objectives text NULL," +
|
|
|
|
|
"requirements text NULL," +
|
|
|
|
|
"steps text NULL," +
|
|
|
|
|
"step_one_questions text NULL," +
|
|
|
|
|
"material_name varchar(255) NULL," +
|
|
|
|
|
"material_url varchar(500) NULL," +
|
|
|
|
|
"sort int DEFAULT 0," +
|
|
|
|
|
@ -754,9 +768,26 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
"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'");
|
|
|
|
|
ensureTextColumn("training_task_class_config", "step_one_questions", "第一步选择题JSON数组");
|
|
|
|
|
trainingTaskClassConfigTableChecked = true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** Add the field for deployments that already created the task tables before this feature existed. */
|
|
|
|
|
private void ensureTextColumn(String tableName, String columnName, String comment) {
|
|
|
|
|
if (jdbcTemplate == null) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
Integer count = jdbcTemplate.queryForObject(
|
|
|
|
|
"SELECT COUNT(1) FROM information_schema.COLUMNS " +
|
|
|
|
|
"WHERE TABLE_SCHEMA = DATABASE() AND TABLE_NAME = ? AND COLUMN_NAME = ?",
|
|
|
|
|
Integer.class, tableName, columnName);
|
|
|
|
|
if (count != null && count > 0) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
jdbcTemplate.execute("ALTER TABLE " + tableName + " ADD COLUMN " + columnName
|
|
|
|
|
+ " text NULL COMMENT '" + comment + "' AFTER steps");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
TrainingTask buildTask(TrainingTaskImportDTO row, int rowNumber, List<String> errors) {
|
|
|
|
|
String projectName = StringUtils.trimToEmpty(row.getProjectName());
|
|
|
|
|
String taskKey = StringUtils.trimToEmpty(row.getTaskKey());
|
|
|
|
|
@ -828,6 +859,7 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
task.setObjectives(normalizeJsonArray(task.getObjectives()));
|
|
|
|
|
task.setRequirements(StringUtils.trimToEmpty(task.getRequirements()));
|
|
|
|
|
task.setSteps(normalizeJsonArray(task.getSteps()));
|
|
|
|
|
task.setStepOneQuestions(normalizeStepOneQuestions(task.getTaskKey(), task.getStepOneQuestions()));
|
|
|
|
|
task.setMaterialName(StringUtils.trimToEmpty(task.getMaterialName()));
|
|
|
|
|
task.setMaterialUrl(StringUtils.trimToEmpty(task.getMaterialUrl()));
|
|
|
|
|
if (task.getEnabled() == null) {
|
|
|
|
|
@ -936,6 +968,62 @@ public class TrainingTaskServiceImpl implements TrainingTaskService {
|
|
|
|
|
return toJsonArray(splitList(trimmed));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String normalizeStepOneQuestions(String taskKey, String value) {
|
|
|
|
|
if (!STEP_ONE_CHOICE_TASK_KEYS.contains(StringUtils.trimToEmpty(taskKey))) {
|
|
|
|
|
return "[]";
|
|
|
|
|
}
|
|
|
|
|
if (StringUtils.isBlank(value)) {
|
|
|
|
|
return "[]";
|
|
|
|
|
}
|
|
|
|
|
try {
|
|
|
|
|
List<JSONObject> source = JSON.parseArray(value, JSONObject.class);
|
|
|
|
|
if (source == null || source.isEmpty()) {
|
|
|
|
|
return "[]";
|
|
|
|
|
}
|
|
|
|
|
List<JSONObject> normalized = new ArrayList<>();
|
|
|
|
|
for (int questionIndex = 0; questionIndex < source.size(); questionIndex++) {
|
|
|
|
|
JSONObject sourceQuestion = source.get(questionIndex);
|
|
|
|
|
String title = StringUtils.trimToEmpty(sourceQuestion == null ? null : sourceQuestion.getString("title"));
|
|
|
|
|
if (StringUtils.isBlank(title)) {
|
|
|
|
|
throw new IllegalArgumentException("第 " + (questionIndex + 1) + " 道选择题题干不能为空");
|
|
|
|
|
}
|
|
|
|
|
List<?> sourceOptions = sourceQuestion == null ? Collections.emptyList() : sourceQuestion.getJSONArray("options");
|
|
|
|
|
List<JSONObject> options = new ArrayList<>();
|
|
|
|
|
for (int optionIndex = 0; optionIndex < sourceOptions.size() && optionIndex < STEP_ONE_CHOICE_OPTION_KEYS.size(); optionIndex++) {
|
|
|
|
|
Object rawOption = sourceOptions.get(optionIndex);
|
|
|
|
|
String content = rawOption instanceof JSONObject
|
|
|
|
|
? StringUtils.trimToEmpty(((JSONObject) rawOption).getString("content"))
|
|
|
|
|
: StringUtils.trimToEmpty(String.valueOf(rawOption));
|
|
|
|
|
if (StringUtils.isNotBlank(content)) {
|
|
|
|
|
JSONObject option = new JSONObject();
|
|
|
|
|
option.put("key", STEP_ONE_CHOICE_OPTION_KEYS.get(options.size()));
|
|
|
|
|
option.put("content", content);
|
|
|
|
|
options.add(option);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (options.size() < 2) {
|
|
|
|
|
throw new IllegalArgumentException("第 " + (questionIndex + 1) + " 道选择题至少需要两个选项");
|
|
|
|
|
}
|
|
|
|
|
String answer = StringUtils.upperCase(StringUtils.trimToEmpty(sourceQuestion.getString("answer")));
|
|
|
|
|
boolean validAnswer = options.stream().anyMatch(option -> answer.equals(option.getString("key")));
|
|
|
|
|
if (!validAnswer) {
|
|
|
|
|
throw new IllegalArgumentException("请设置第 " + (questionIndex + 1) + " 道选择题的正确答案");
|
|
|
|
|
}
|
|
|
|
|
JSONObject question = new JSONObject();
|
|
|
|
|
question.put("id", StringUtils.defaultIfBlank(sourceQuestion.getString("id"), "step-one-question-" + (questionIndex + 1)));
|
|
|
|
|
question.put("title", title);
|
|
|
|
|
question.put("options", options);
|
|
|
|
|
question.put("answer", answer);
|
|
|
|
|
normalized.add(question);
|
|
|
|
|
}
|
|
|
|
|
return JSON.toJSONString(normalized);
|
|
|
|
|
} catch (IllegalArgumentException e) {
|
|
|
|
|
throw e;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new IllegalArgumentException("第一步选择题配置格式不正确");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private String toJsonArray(List<String> list) {
|
|
|
|
|
return JSON.toJSONString(list == null ? Collections.emptyList() : list);
|
|
|
|
|
}
|
|
|
|
|
|