feat: grade market opportunity selection step one
parent
50bf9265d5
commit
38aa2bb68d
@ -0,0 +1,17 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public class MarketOpportunitySelectionStepOneCheckRequest {
|
||||
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,30 @@
|
||||
package com.sztzjy.linkCommerce.entity.dto;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
public class MarketOpportunitySelectionStepOneCheckResult {
|
||||
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.MarketOpportunitySelectionStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckResult;
|
||||
|
||||
public interface MarketOpportunitySelectionStepOneService {
|
||||
MarketOpportunitySelectionStepOneCheckResult check(MarketOpportunitySelectionStepOneCheckRequest request, JwtUser user);
|
||||
}
|
||||
@ -0,0 +1,78 @@
|
||||
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.MarketOpportunitySelectionStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckResult;
|
||||
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService;
|
||||
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 MarketOpportunitySelectionStepOneServiceImpl implements MarketOpportunitySelectionStepOneService {
|
||||
private static final String TASK_KEY = "market-opportunity-selection";
|
||||
private static final int STEP_NO = 1;
|
||||
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 MarketOpportunitySelectionStepOneServiceImpl(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,update_time datetime NULL,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())", "market-opportunity-selection-step-1", TASK_KEY, STEP_NO, JSON.toJSONString(DEFAULT_ANSWERS));
|
||||
}
|
||||
|
||||
@Override
|
||||
public MarketOpportunitySelectionStepOneCheckResult check(MarketOpportunitySelectionStepOneCheckRequest request, JwtUser user) {
|
||||
requireStudent(user);
|
||||
Map<String, String> submitted = validateSubmittedItems(request == null ? null : request.getItems());
|
||||
Map<String, String> standardAnswers = loadStandardAnswers();
|
||||
MarketOpportunitySelectionStepOneCheckResult result = new MarketOpportunitySelectionStepOneCheckResult();
|
||||
int correctCount = 0;
|
||||
for (String id : standardAnswers.keySet()) {
|
||||
boolean correct = standardAnswers.get(id).equals(submitted.get(id));
|
||||
MarketOpportunitySelectionStepOneCheckResult.ItemResult item = new MarketOpportunitySelectionStepOneCheckResult.ItemResult(); item.setId(id); item.setCorrect(correct); item.setMessage(correct ? "分类正确" : "分类有误,请重新选择"); result.getItems().add(item);
|
||||
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<MarketOpportunitySelectionStepOneCheckRequest.Item> items) {
|
||||
if (items == null || items.size() != DEFAULT_ANSWERS.size()) throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成全部指标分类后再判题");
|
||||
Map<String, String> submitted = new LinkedHashMap<>();
|
||||
for (MarketOpportunitySelectionStepOneCheckRequest.Item item : items) {
|
||||
String id = item == null ? "" : StringUtils.trimToEmpty(item.getId()); String category = item == 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, "第 1 步标准答案未正确配置"); answers.put(id, String.valueOf(category)); }
|
||||
if (raw == null || raw.size() != answers.size()) throw new ServiceException(HttpStatus.INTERNAL_SERVER_ERROR, "第 1 步标准答案未正确配置");
|
||||
return answers;
|
||||
}
|
||||
private static Map<String, String> createDefaultAnswers() {
|
||||
Map<String, String> answers = new LinkedHashMap<>();
|
||||
answers.put("search-count", "市场规模"); answers.put("demand-supply-ratio", "竞争强度"); answers.put("transaction-amount", "市场规模"); answers.put("transaction-growth", "市场潜力"); answers.put("category-transaction-growth", "市场潜力"); answers.put("online-products", "竞争强度"); answers.put("online-merchants", "竞争强度"); answers.put("organic-traffic-ratio", "运营难度");
|
||||
return answers;
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
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.MarketOpportunitySelectionStepOneCheckRequest;
|
||||
import com.sztzjy.linkCommerce.entity.dto.MarketOpportunitySelectionStepOneCheckResult;
|
||||
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 MarketOpportunitySelectionStepOneServiceImplTest {
|
||||
@Test
|
||||
void checkReturnsPerIndicatorFeedbackWithoutExposingTheAnswer() {
|
||||
MarketOpportunitySelectionStepOneServiceImpl service = new MarketOpportunitySelectionStepOneServiceImpl(standardAnswerJdbcTemplate());
|
||||
MarketOpportunitySelectionStepOneCheckResult result = service.check(requestWithChangedSecondCategory("市场规模"), student());
|
||||
assertEquals(7, result.getCorrectCount()); assertEquals(8, result.getTotalCount()); assertFalse(result.isAllCorrect()); assertTrue(result.getItems().get(0).isCorrect()); assertFalse(result.getItems().get(1).isCorrect()); assertFalse(JSON.toJSONString(result).contains("correctCategory"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void checkRejectsIncompleteAnswersInvalidCategoriesAndNonStudents() {
|
||||
MarketOpportunitySelectionStepOneServiceImpl service = new MarketOpportunitySelectionStepOneServiceImpl(standardAnswerJdbcTemplate());
|
||||
assertThrows(ServiceException.class, () -> service.check(request(Arrays.asList(item("search-count", "市场规模"))), student()));
|
||||
assertThrows(ServiceException.class, () -> service.check(requestWithChangedSecondCategory("错误分类"), student()));
|
||||
assertThrows(ServiceException.class, () -> service.check(requestWithChangedSecondCategory("市场规模"), teacher()));
|
||||
}
|
||||
|
||||
@Test
|
||||
void initializationCreatesTheStandardAnswerTableWithoutOverwritingAnswers() {
|
||||
JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); MarketOpportunitySelectionStepOneServiceImpl service = new MarketOpportunitySelectionStepOneServiceImpl(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 (MarketOpportunitySelectionStepOneCheckRequest.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<MarketOpportunitySelectionStepOneCheckRequest.Item> defaultAnswers() { return Arrays.asList(item("search-count", "市场规模"), item("demand-supply-ratio", "竞争强度"), item("transaction-amount", "市场规模"), item("transaction-growth", "市场潜力"), item("category-transaction-growth", "市场潜力"), item("online-products", "竞争强度"), item("online-merchants", "竞争强度"), item("organic-traffic-ratio", "运营难度")); }
|
||||
private MarketOpportunitySelectionStepOneCheckRequest requestWithChangedSecondCategory(String category) { List<MarketOpportunitySelectionStepOneCheckRequest.Item> items = defaultAnswers(); items.get(1).setCategory(category); return request(items); }
|
||||
private MarketOpportunitySelectionStepOneCheckRequest request(List<MarketOpportunitySelectionStepOneCheckRequest.Item> items) { MarketOpportunitySelectionStepOneCheckRequest request = new MarketOpportunitySelectionStepOneCheckRequest(); request.setItems(items); return request; }
|
||||
private MarketOpportunitySelectionStepOneCheckRequest.Item item(String id, String category) { MarketOpportunitySelectionStepOneCheckRequest.Item item = new MarketOpportunitySelectionStepOneCheckRequest.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