feat: grade new product survey step one
parent
0e354b3abb
commit
90018ba287
@ -0,0 +1,36 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class NewProductSurveyStepOneCheckRequest {
|
||||
private List<Item> items;
|
||||
|
||||
public List<Item> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<Item> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public static class Item {
|
||||
private String id;
|
||||
private String category;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,73 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class NewProductSurveyStepOneCheckResult {
|
||||
private List<ItemResult> items = new ArrayList<>();
|
||||
private int correctCount;
|
||||
private int totalCount;
|
||||
private boolean allCorrect;
|
||||
|
||||
public List<ItemResult> getItems() {
|
||||
return items;
|
||||
}
|
||||
|
||||
public void setItems(List<ItemResult> items) {
|
||||
this.items = items;
|
||||
}
|
||||
|
||||
public int getCorrectCount() {
|
||||
return correctCount;
|
||||
}
|
||||
|
||||
public void setCorrectCount(int correctCount) {
|
||||
this.correctCount = correctCount;
|
||||
}
|
||||
|
||||
public int getTotalCount() {
|
||||
return totalCount;
|
||||
}
|
||||
|
||||
public void setTotalCount(int totalCount) {
|
||||
this.totalCount = totalCount;
|
||||
}
|
||||
|
||||
public boolean isAllCorrect() {
|
||||
return allCorrect;
|
||||
}
|
||||
|
||||
public void setAllCorrect(boolean allCorrect) {
|
||||
this.allCorrect = allCorrect;
|
||||
}
|
||||
|
||||
public static class ItemResult {
|
||||
private String id;
|
||||
private boolean correct;
|
||||
private String message;
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public boolean isCorrect() {
|
||||
return correct;
|
||||
}
|
||||
|
||||
public void setCorrect(boolean correct) {
|
||||
this.correct = correct;
|
||||
}
|
||||
|
||||
public String getMessage() {
|
||||
return message;
|
||||
}
|
||||
|
||||
public void setMessage(String message) {
|
||||
this.message = message;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,9 @@
|
||||
package com.sztzjy.linkCommerce.service;
|
||||
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckResult;
|
||||
|
||||
public interface NewProductSurveyStepOneService {
|
||||
NewProductSurveyStepOneCheckResult check(NewProductSurveyStepOneCheckRequest request, JwtUser user);
|
||||
}
|
||||
@ -0,0 +1,143 @@
|
||||
package com.sztzjy.linkCommerce.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckResult;
|
||||
import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import javax.annotation.PostConstruct;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Service
|
||||
public class NewProductSurveyStepOneServiceImpl implements NewProductSurveyStepOneService {
|
||||
private static final String TASK_KEY = "new-product-survey";
|
||||
private static final int STEP_NO = 1;
|
||||
private static final String CORRECT_MESSAGE = "分类正确";
|
||||
private static final String INCORRECT_MESSAGE = "分类有误,请重新选择";
|
||||
private static final Set<String> ALLOWED_CATEGORIES = new LinkedHashSet<>(Arrays.asList("核心层", "有形层", "延伸层"));
|
||||
private static final Map<String, String> DEFAULT_ANSWERS = createDefaultAnswers();
|
||||
|
||||
private final JdbcTemplate jdbcTemplate;
|
||||
|
||||
@Autowired
|
||||
public NewProductSurveyStepOneServiceImpl(JdbcTemplate jdbcTemplate) {
|
||||
this.jdbcTemplate = jdbcTemplate;
|
||||
}
|
||||
|
||||
@PostConstruct
|
||||
public void initializeStandardAnswers() {
|
||||
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS training_step_standard_answer ("
|
||||
+ "id varchar(64) NOT NULL COMMENT 'primary id',"
|
||||
+ "task_key varchar(128) NOT NULL COMMENT 'training task key',"
|
||||
+ "step_no int NOT NULL COMMENT 'step number',"
|
||||
+ "answer_json longtext NOT NULL COMMENT 'standard answer json',"
|
||||
+ "create_time datetime NULL COMMENT 'create time',"
|
||||
+ "update_time datetime NULL COMMENT 'update time',"
|
||||
+ "PRIMARY KEY (id),"
|
||||
+ "UNIQUE KEY uk_training_step_standard_answer (task_key, step_no)"
|
||||
+ ") ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='training step standard answer'");
|
||||
jdbcTemplate.update("INSERT IGNORE INTO training_step_standard_answer "
|
||||
+ "(id, task_key, step_no, answer_json, create_time, update_time) VALUES (?, ?, ?, ?, NOW(), NOW())",
|
||||
"new-product-survey-step-1", TASK_KEY, STEP_NO, JSON.toJSONString(DEFAULT_ANSWERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NewProductSurveyStepOneCheckResult check(NewProductSurveyStepOneCheckRequest request, JwtUser user) {
|
||||
requireStudent(user);
|
||||
Map<String, String> submitted = validateSubmittedItems(request == null ? null : request.getItems());
|
||||
Map<String, String> standardAnswers = loadStandardAnswers();
|
||||
|
||||
NewProductSurveyStepOneCheckResult result = new NewProductSurveyStepOneCheckResult();
|
||||
int correctCount = 0;
|
||||
for (String id : standardAnswers.keySet()) {
|
||||
boolean correct = standardAnswers.get(id).equals(submitted.get(id));
|
||||
NewProductSurveyStepOneCheckResult.ItemResult itemResult = new NewProductSurveyStepOneCheckResult.ItemResult();
|
||||
itemResult.setId(id);
|
||||
itemResult.setCorrect(correct);
|
||||
itemResult.setMessage(correct ? CORRECT_MESSAGE : INCORRECT_MESSAGE);
|
||||
result.getItems().add(itemResult);
|
||||
if (correct) {
|
||||
correctCount++;
|
||||
}
|
||||
}
|
||||
result.setCorrectCount(correctCount);
|
||||
result.setTotalCount(standardAnswers.size());
|
||||
result.setAllCorrect(correctCount == standardAnswers.size());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void requireStudent(JwtUser user) {
|
||||
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再判题");
|
||||
}
|
||||
if (user.getRoleId() != 4) {
|
||||
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训判题");
|
||||
}
|
||||
}
|
||||
|
||||
private Map<String, String> validateSubmittedItems(List<NewProductSurveyStepOneCheckRequest.Item> items) {
|
||||
if (items == null || items.size() != DEFAULT_ANSWERS.size()) {
|
||||
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成全部卖点分类后再判题");
|
||||
}
|
||||
Map<String, String> submitted = new LinkedHashMap<>();
|
||||
for (NewProductSurveyStepOneCheckRequest.Item item : items) {
|
||||
String id = item == null ? null : StringUtils.trimToEmpty(item.getId());
|
||||
String category = item == null ? null : StringUtils.trimToEmpty(item.getCategory());
|
||||
if (!DEFAULT_ANSWERS.containsKey(id) || !ALLOWED_CATEGORIES.contains(category) || submitted.put(id, category) != null) {
|
||||
throw new ServiceException(HttpStatus.BAD_REQUEST, "提交的卖点分类数据不合法");
|
||||
}
|
||||
}
|
||||
if (!submitted.keySet().equals(DEFAULT_ANSWERS.keySet())) {
|
||||
throw new ServiceException(HttpStatus.BAD_REQUEST, "提交的卖点分类数据不完整");
|
||||
}
|
||||
return submitted;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String, String> loadStandardAnswers() {
|
||||
String answerJson = jdbcTemplate.queryForObject(
|
||||
"SELECT answer_json FROM training_step_standard_answer WHERE task_key = ? AND step_no = ?",
|
||||
String.class, TASK_KEY, STEP_NO);
|
||||
Map<String, Object> raw = JSON.parseObject(answerJson, LinkedHashMap.class);
|
||||
Map<String, String> answers = new LinkedHashMap<>();
|
||||
for (String id : DEFAULT_ANSWERS.keySet()) {
|
||||
Object category = raw == null ? null : raw.get(id);
|
||||
if (category == null || !ALLOWED_CATEGORIES.contains(String.valueOf(category))) {
|
||||
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, "第一步标准答案未正确配置");
|
||||
}
|
||||
answers.put(id, String.valueOf(category));
|
||||
}
|
||||
if (raw == null || raw.size() != answers.size()) {
|
||||
throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, "第一步标准答案未正确配置");
|
||||
}
|
||||
return answers;
|
||||
}
|
||||
|
||||
private static Map<String, String> createDefaultAnswers() {
|
||||
Map<String, String> answers = new LinkedHashMap<>();
|
||||
answers.put("selling-point-1", "核心层");
|
||||
answers.put("selling-point-2", "有形层");
|
||||
answers.put("selling-point-3", "核心层");
|
||||
answers.put("selling-point-4", "核心层");
|
||||
answers.put("selling-point-5", "有形层");
|
||||
answers.put("selling-point-6", "有形层");
|
||||
answers.put("selling-point-7", "有形层");
|
||||
answers.put("selling-point-8", "延伸层");
|
||||
answers.put("selling-point-9", "延伸层");
|
||||
answers.put("selling-point-10", "延伸层");
|
||||
answers.put("selling-point-11", "有形层");
|
||||
return answers;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
package com.sztzjy.linkCommerce.service.impl;
|
||||
|
||||
import com.alibaba.fastjson.JSON;
|
||||
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
|
||||
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.NewProductSurveyStepOneCheckResult;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.jdbc.core.JdbcTemplate;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.any;
|
||||
import static org.mockito.ArgumentMatchers.contains;
|
||||
import static org.mockito.ArgumentMatchers.startsWith;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.verify;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
class NewProductSurveyStepOneServiceImplTest {
|
||||
|
||||
@Test
|
||||
void checkReturnsPerItemFeedbackWithoutExposingCorrectCategories() {
|
||||
JdbcTemplate jdbcTemplate = standardAnswerJdbcTemplate();
|
||||
NewProductSurveyStepOneServiceImpl service = new NewProductSurveyStepOneServiceImpl(jdbcTemplate);
|
||||
|
||||
NewProductSurveyStepOneCheckResult result = service.check(requestWithSecondCategory("核心层"), student());
|
||||
|
||||
assertEquals(10, result.getCorrectCount());
|
||||
assertEquals(11, result.getTotalCount());
|
||||
assertFalse(result.isAllCorrect());
|
||||
assertTrue(result.getItems().get(0).isCorrect());
|
||||
assertFalse(result.getItems().get(1).isCorrect());
|
||||
assertEquals("分类有误,请重新选择", result.getItems().get(1).getMessage());
|
||||
assertFalse(JSON.toJSONString(result).contains("correctCategory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkRejectsIncompleteAnswersInvalidCategoriesAndNonStudents() {
|
||||
NewProductSurveyStepOneServiceImpl service = new NewProductSurveyStepOneServiceImpl(standardAnswerJdbcTemplate());
|
||||
|
||||
assertThrows(ServiceException.class, () -> service.check(request(Arrays.asList(item("selling-point-1", "核心层"))), student()));
|
||||
assertThrows(ServiceException.class, () -> service.check(requestWithSecondCategory("错误层级"), student()));
|
||||
assertThrows(ServiceException.class, () -> service.check(requestWithSecondCategory("有形层"), teacher()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializationCreatesTheTableAndDoesNotOverwriteExistingAnswers() {
|
||||
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
|
||||
NewProductSurveyStepOneServiceImpl service = new NewProductSurveyStepOneServiceImpl(jdbcTemplate);
|
||||
|
||||
service.initializeStandardAnswers();
|
||||
|
||||
verify(jdbcTemplate).execute(contains("CREATE TABLE IF NOT EXISTS training_step_standard_answer"));
|
||||
verify(jdbcTemplate).update(startsWith("INSERT IGNORE INTO training_step_standard_answer"), any(), any(), any(), any());
|
||||
}
|
||||
|
||||
private JdbcTemplate standardAnswerJdbcTemplate() {
|
||||
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class);
|
||||
Map<String, String> answers = new LinkedHashMap<>();
|
||||
for (NewProductSurveyStepOneCheckRequest.Item item : defaultAnswers()) {
|
||||
answers.put(item.getId(), item.getCategory());
|
||||
}
|
||||
when(jdbcTemplate.queryForObject(any(String.class), any(Class.class), any(), any())).thenReturn(JSON.toJSONString(answers));
|
||||
return jdbcTemplate;
|
||||
}
|
||||
|
||||
private List<NewProductSurveyStepOneCheckRequest.Item> defaultAnswers() {
|
||||
return Arrays.asList(
|
||||
item("selling-point-1", "核心层"), item("selling-point-2", "有形层"), item("selling-point-3", "核心层"),
|
||||
item("selling-point-4", "核心层"), item("selling-point-5", "有形层"), item("selling-point-6", "有形层"),
|
||||
item("selling-point-7", "有形层"), item("selling-point-8", "延伸层"), item("selling-point-9", "延伸层"),
|
||||
item("selling-point-10", "延伸层"), item("selling-point-11", "有形层"));
|
||||
}
|
||||
|
||||
private NewProductSurveyStepOneCheckRequest requestWithSecondCategory(String category) {
|
||||
List<NewProductSurveyStepOneCheckRequest.Item> items = defaultAnswers();
|
||||
items.get(1).setCategory(category);
|
||||
return request(items);
|
||||
}
|
||||
|
||||
private NewProductSurveyStepOneCheckRequest request(List<NewProductSurveyStepOneCheckRequest.Item> items) {
|
||||
NewProductSurveyStepOneCheckRequest request = new NewProductSurveyStepOneCheckRequest();
|
||||
request.setItems(items);
|
||||
return request;
|
||||
}
|
||||
|
||||
private NewProductSurveyStepOneCheckRequest.Item item(String id, String category) {
|
||||
NewProductSurveyStepOneCheckRequest.Item item = new NewProductSurveyStepOneCheckRequest.Item();
|
||||
item.setId(id);
|
||||
item.setCategory(category);
|
||||
return item;
|
||||
}
|
||||
|
||||
private JwtUser student() {
|
||||
JwtUser user = new JwtUser();
|
||||
user.setUserId("stu-1");
|
||||
user.setRoleId(4);
|
||||
return user;
|
||||
}
|
||||
|
||||
private JwtUser teacher() {
|
||||
JwtUser user = student();
|
||||
user.setRoleId(3);
|
||||
return user;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue