diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTask.java b/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTask.java index 096438e..f17a7cf 100644 --- a/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTask.java +++ b/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTask.java @@ -32,6 +32,9 @@ public class TrainingTask { @ApiModelProperty("步骤名称JSON数组") private String steps; + @ApiModelProperty("第一步选择题JSON数组") + private String stepOneQuestions; + @ApiModelProperty("资料名称") private String materialName; @@ -125,6 +128,14 @@ public class TrainingTask { this.steps = steps == null ? null : steps.trim(); } + public String getStepOneQuestions() { + return stepOneQuestions; + } + + public void setStepOneQuestions(String stepOneQuestions) { + this.stepOneQuestions = stepOneQuestions == null ? null : stepOneQuestions.trim(); + } + public String getMaterialName() { return materialName; } diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTaskClassConfig.java b/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTaskClassConfig.java index d85e43e..0ddf4cb 100644 --- a/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTaskClassConfig.java +++ b/src/main/java/com/sztzjy/linkCommerce/entity/TrainingTaskClassConfig.java @@ -35,6 +35,9 @@ public class TrainingTaskClassConfig { @ApiModelProperty("Step names JSON array") private String steps; + @ApiModelProperty("Step-one choice questions JSON array") + private String stepOneQuestions; + @ApiModelProperty("Material name") private String materialName; @@ -136,6 +139,14 @@ public class TrainingTaskClassConfig { this.steps = steps == null ? null : steps.trim(); } + public String getStepOneQuestions() { + return stepOneQuestions; + } + + public void setStepOneQuestions(String stepOneQuestions) { + this.stepOneQuestions = stepOneQuestions == null ? null : stepOneQuestions.trim(); + } + public String getMaterialName() { return materialName; } 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 5a02bad..ac18e92 100644 --- a/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TrainingTaskServiceImpl.java @@ -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 STEP_ONE_CHOICE_TASK_KEYS = new LinkedHashSet<>(Arrays.asList( + "new-product-survey", "industry-demand-analysis", "competitive-environment-analysis")); + private static final List 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 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 source = JSON.parseArray(value, JSONObject.class); + if (source == null || source.isEmpty()) { + return "[]"; + } + List 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 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 list) { return JSON.toJSONString(list == null ? Collections.emptyList() : list); } diff --git a/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml b/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml index 65d414c..fe1a58e 100644 --- a/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml +++ b/src/main/resources/mappers/TrainingTaskClassConfigMapper.xml @@ -12,6 +12,7 @@ + @@ -23,7 +24,7 @@ id, teaching_class_id, task_key, project_name, task_name, knowledge, background, objectives, requirements, - steps, material_name, material_url, sort, enabled, created_by, create_time, update_time + steps, step_one_questions, material_name, material_url, sort, enabled, created_by, create_time, update_time @@ -75,6 +76,7 @@ objectives, requirements, steps, + step_one_questions, material_name, material_url, sort, @@ -93,6 +95,7 @@ #{objectives}, #{requirements}, #{steps}, + #{stepOneQuestions}, #{materialName,jdbcType=VARCHAR}, #{materialUrl,jdbcType=VARCHAR}, #{sort,jdbcType=INTEGER}, @@ -114,6 +117,7 @@ objectives = #{objectives}, requirements = #{requirements}, steps = #{steps}, + step_one_questions = #{stepOneQuestions}, material_name = #{materialName,jdbcType=VARCHAR}, material_url = #{materialUrl,jdbcType=VARCHAR}, sort = #{sort,jdbcType=INTEGER},