feat: 平台题库归属项目(任务)改为可选

- 屏蔽归属任务功能:保存/导入/AI出题不再强制选择归属项目
- task_key 为空时跳过存在性校验,入库存空值;后续归属规则调整(如按学校/项目)预留
fix/exam-question-access-material-excel-delete-draft
chenyuan 6 days ago
parent 92bd2fc162
commit 134fe299ba

@ -73,7 +73,7 @@ public class PlatformQuestionBankService {
public Map<String, Object> save(JwtUser user, Long id, Map<String, Object> payload) { public Map<String, Object> save(JwtUser user, Long id, Map<String, Object> payload) {
platformAdminService.requirePlatformAdmin(user); platformAdminService.requirePlatformAdmin(user);
ValidQuestion q = validate(payload); ValidQuestion q = validate(payload);
requireTask(q.taskKey); if (StringUtils.isNotBlank(q.taskKey)) requireTask(q.taskKey);
if (id == null) { if (id == null) {
jdbc.update("insert into exam_question(school_id,teacher_id,source_type,task_key,question_type,stem,options_json,answer_json,answer_analysis,attachment_name,attachment_url) values(?,?,?,?,?,?,?,?,?,?,?)", jdbc.update("insert into exam_question(school_id,teacher_id,source_type,task_key,question_type,stem,options_json,answer_json,answer_analysis,attachment_name,attachment_url) values(?,?,?,?,?,?,?,?,?,?,?)",
PLATFORM, PLATFORM, PLATFORM, q.taskKey, q.type, q.stem, json(q.options), json(q.answer), q.analysis, text(payload.get("attachmentName")), text(payload.get("attachmentUrl"))); PLATFORM, PLATFORM, PLATFORM, q.taskKey, q.type, q.stem, json(q.options), json(q.answer), q.analysis, text(payload.get("attachmentName")), text(payload.get("attachmentUrl")));
@ -93,8 +93,7 @@ public class PlatformQuestionBankService {
public Map<String, Object> aiPreview(JwtUser user, Map<String, Object> request) { public Map<String, Object> aiPreview(JwtUser user, Map<String, Object> request) {
platformAdminService.requirePlatformAdmin(user); platformAdminService.requirePlatformAdmin(user);
String taskKey = required(request, "taskKey", "请选择归属项目"); String taskKey = text(request.get("taskKey")).trim();
requireTask(taskKey);
String type = required(request, "type", "请选择题型"); validateType(type); String type = required(request, "type", "请选择题型"); validateType(type);
int count; int count;
try { count = Integer.parseInt(text(request.get("count"))); } catch (Exception e) { throw new IllegalArgumentException("题目数量必须是数字"); } try { count = Integer.parseInt(text(request.get("count"))); } catch (Exception e) { throw new IllegalArgumentException("题目数量必须是数字"); }
@ -126,7 +125,7 @@ public class PlatformQuestionBankService {
@Transactional @Transactional
public List<Map<String, Object>> importExcel(JwtUser user, MultipartFile file, String taskKey) { public List<Map<String, Object>> importExcel(JwtUser user, MultipartFile file, String taskKey) {
platformAdminService.requirePlatformAdmin(user); requireTask(taskKey); platformAdminService.requirePlatformAdmin(user); if (StringUtils.isNotBlank(taskKey)) requireTask(taskKey);
if (file == null || file.isEmpty()) throw new IllegalArgumentException("请选择Word或Excel文件"); if (file == null || file.isEmpty()) throw new IllegalArgumentException("请选择Word或Excel文件");
String name = StringUtils.defaultString(file.getOriginalFilename()).toLowerCase(Locale.ROOT); String name = StringUtils.defaultString(file.getOriginalFilename()).toLowerCase(Locale.ROOT);
if (name.endsWith(".docx")) return importWord(user, file, taskKey); if (name.endsWith(".docx")) return importWord(user, file, taskKey);
@ -158,7 +157,7 @@ public class PlatformQuestionBankService {
} catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException("Word导入失败,请检查模板"); } } catch (IllegalArgumentException e) { throw e; } catch (Exception e) { throw new IllegalArgumentException("Word导入失败,请检查模板"); }
} }
private ValidQuestion validate(Map<String,Object> p) { String type=required(p,"type","请选择题型"); validateType(type); String stem=required(p,"stem","题干不能为空"); if(StringUtils.isBlank(plain(stem))) throw new IllegalArgumentException("题干不能为空"); List<Map<String,Object>> options=objectMapper.convertValue(p.getOrDefault("options",Collections.emptyList()),new TypeReference<List<Map<String,Object>>>(){}); Object answer=p.get("answer"); if(type.equals("SINGLE")||type.equals("MULTIPLE")){ if(options.size()<2) throw new IllegalArgumentException("选择题至少需要两个选项"); Set<String> labels=new LinkedHashSet<>(); List<Map<String,Object>> normalized=new ArrayList<>(); for(Map<String,Object> o:options){String label=text(o.get("label")).trim().toUpperCase();String content=text(o.get("content")).trim();if(!label.matches("[A-H]")||StringUtils.isBlank(plain(content))||!labels.add(label)) throw new IllegalArgumentException("选项格式不正确"); normalized.add(map("label",label,"content",content));} List<String> answers=list(answer); if(type.equals("SINGLE")&&answers.size()!=1) throw new IllegalArgumentException("单选题只能设置一个正确答案"); if(answers.isEmpty()||!labels.containsAll(answers)) throw new IllegalArgumentException("正确答案必须来自题目选项"); return new ValidQuestion(type,stem,normalized,answers,text(p.get("analysis")),required(p,"taskKey","请选择归属项目")); } if(type.equals("TRUE_FALSE")){String v=text(answer);if(!"true".equalsIgnoreCase(v)&&!"false".equalsIgnoreCase(v)&&!"正确".equals(v)&&!"错误".equals(v))throw new IllegalArgumentException("判断题答案格式不正确");return new ValidQuestion(type,stem,Collections.emptyList(),"正确".equals(v)||"true".equalsIgnoreCase(v),text(p.get("analysis")),required(p,"taskKey","请选择归属项目"));} if(StringUtils.isBlank(plain(text(answer)))) throw new IllegalArgumentException("参考答案不能为空"); return new ValidQuestion(type,stem,Collections.emptyList(),text(answer),text(p.get("analysis")),required(p,"taskKey","请选择归属项目")); } private ValidQuestion validate(Map<String,Object> p) { String type=required(p,"type","请选择题型"); validateType(type); String stem=required(p,"stem","题干不能为空"); if(StringUtils.isBlank(plain(stem))) throw new IllegalArgumentException("题干不能为空"); List<Map<String,Object>> options=objectMapper.convertValue(p.getOrDefault("options",Collections.emptyList()),new TypeReference<List<Map<String,Object>>>(){}); Object answer=p.get("answer"); if(type.equals("SINGLE")||type.equals("MULTIPLE")){ if(options.size()<2) throw new IllegalArgumentException("选择题至少需要两个选项"); Set<String> labels=new LinkedHashSet<>(); List<Map<String,Object>> normalized=new ArrayList<>(); for(Map<String,Object> o:options){String label=text(o.get("label")).trim().toUpperCase();String content=text(o.get("content")).trim();if(!label.matches("[A-H]")||StringUtils.isBlank(plain(content))||!labels.add(label)) throw new IllegalArgumentException("选项格式不正确"); normalized.add(map("label",label,"content",content));} List<String> answers=list(answer); if(type.equals("SINGLE")&&answers.size()!=1) throw new IllegalArgumentException("单选题只能设置一个正确答案"); if(answers.isEmpty()||!labels.containsAll(answers)) throw new IllegalArgumentException("正确答案必须来自题目选项"); return new ValidQuestion(type,stem,normalized,answers,text(p.get("analysis")),text(p.get("taskKey")).trim()); } if(type.equals("TRUE_FALSE")){String v=text(answer);if(!"true".equalsIgnoreCase(v)&&!"false".equalsIgnoreCase(v)&&!"正确".equals(v)&&!"错误".equals(v))throw new IllegalArgumentException("判断题答案格式不正确");return new ValidQuestion(type,stem,Collections.emptyList(),"正确".equals(v)||"true".equalsIgnoreCase(v),text(p.get("analysis")),text(p.get("taskKey")).trim());} if(StringUtils.isBlank(plain(text(answer)))) throw new IllegalArgumentException("参考答案不能为空"); return new ValidQuestion(type,stem,Collections.emptyList(),text(answer),text(p.get("analysis")),text(p.get("taskKey")).trim()); }
private Map<String,Object> normalize(Map<String,Object> p){ValidQuestion q=validate(p);return map("type",q.type,"stem",q.stem,"options",q.options,"answer",q.answer,"analysis",q.analysis,"taskKey",q.taskKey);} private Map<String,Object> normalize(Map<String,Object> p){ValidQuestion q=validate(p);return map("type",q.type,"stem",q.stem,"options",q.options,"answer",q.answer,"analysis",q.analysis,"taskKey",q.taskKey);}
private void requireTask(String key){if(jdbc.queryForObject("select count(*) from training_task where task_key=?",Integer.class,key)==0)throw new IllegalArgumentException("归属项目不存在");} private void requireTask(String key){if(jdbc.queryForObject("select count(*) from training_task where task_key=?",Integer.class,key)==0)throw new IllegalArgumentException("归属项目不存在");}
private Map<String,Object> row(java.sql.ResultSet rs)throws java.sql.SQLException{return map("id",rs.getLong("id"),"type",rs.getString("question_type"),"stem",rs.getString("stem"),"options",parse(rs.getString("options_json")),"answer",parse(rs.getString("answer_json")),"analysis",rs.getString("answer_analysis"),"attachmentName",rs.getString("attachment_name"),"attachmentUrl",rs.getString("attachment_url"),"sourceType",rs.getString("source_type"),"taskKey",rs.getString("task_key"),"projectName",rs.getString("project_name"),"taskName",rs.getString("task_name"),"updatedAt",rs.getTimestamp("update_time"));} private Map<String,Object> row(java.sql.ResultSet rs)throws java.sql.SQLException{return map("id",rs.getLong("id"),"type",rs.getString("question_type"),"stem",rs.getString("stem"),"options",parse(rs.getString("options_json")),"answer",parse(rs.getString("answer_json")),"analysis",rs.getString("answer_analysis"),"attachmentName",rs.getString("attachment_name"),"attachmentUrl",rs.getString("attachment_url"),"sourceType",rs.getString("source_type"),"taskKey",rs.getString("task_key"),"projectName",rs.getString("project_name"),"taskName",rs.getString("task_name"),"updatedAt",rs.getTimestamp("update_time"));}

Loading…
Cancel
Save