merge: consumer behavior module

main
chenyuan 3 weeks ago
commit 4f4798c42d

@ -28,6 +28,15 @@ import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepThreeVali
import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepThreeValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValidationRequest; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValidationResult; import com.sztzjy.linkCommerce.entity.dto.ProductDevelopmentFactorsStepFourValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorWordPreviewResult;
import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService; import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService; import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService;
@ -40,6 +49,10 @@ import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepOneService;
import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepTwoService; import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepTwoService;
import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepThreeService; import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepThreeService;
import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepFourService; import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepFourService;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepOneService;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepTwoService;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepThreeService;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepFourService;
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService; import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
import com.sztzjy.linkCommerce.util.ResultEntity; import com.sztzjy.linkCommerce.util.ResultEntity;
import io.swagger.annotations.Api; import io.swagger.annotations.Api;
@ -47,6 +60,7 @@ import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus; import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequest;
@ -91,6 +105,18 @@ public class StudentTrainingAnswerController {
@Autowired @Autowired
private ProductDevelopmentFactorsStepFourService productDevelopmentFactorsStepFourService; private ProductDevelopmentFactorsStepFourService productDevelopmentFactorsStepFourService;
@Autowired
private ConsumerBehaviorStepOneService consumerBehaviorStepOneService;
@Autowired
private ConsumerBehaviorStepTwoService consumerBehaviorStepTwoService;
@Autowired
private ConsumerBehaviorStepThreeService consumerBehaviorStepThreeService;
@Autowired
private ConsumerBehaviorStepFourService consumerBehaviorStepFourService;
@GetMapping("/{taskKey}") @GetMapping("/{taskKey}")
@ApiOperation("查询当前学生指定实训任务答案") @ApiOperation("查询当前学生指定实训任务答案")
public ResultEntity<StudentTrainingAnswer> detail(@PathVariable String taskKey, public ResultEntity<StudentTrainingAnswer> detail(@PathVariable String taskKey,
@ -251,6 +277,61 @@ public class StudentTrainingAnswerController {
catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); }
} }
@PostMapping("/consumer-behavior/step-1/validate")
@ApiOperation("消费者需求与行为第一步校验")
public ResultEntity<ConsumerBehaviorStepOneValidationResult> validateConsumerBehaviorStepOne(
@RequestBody ConsumerBehaviorStepOneValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", consumerBehaviorStepOneService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/consumer-behavior/step-2/validate")
@ApiOperation("消费者需求与行为第二步校验")
public ResultEntity<ConsumerBehaviorStepTwoValidationResult> validateConsumerBehaviorStepTwo(
@RequestBody ConsumerBehaviorStepTwoValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", consumerBehaviorStepTwoService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/consumer-behavior/step-3/validate")
@ApiOperation("消费者需求与行为第三步校验")
public ResultEntity<ConsumerBehaviorStepThreeValidationResult> validateConsumerBehaviorStepThree(
@RequestBody ConsumerBehaviorStepThreeValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", consumerBehaviorStepThreeService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/consumer-behavior/step-4/validate")
@ApiOperation("消费者需求与行为第四步校验")
public ResultEntity<ConsumerBehaviorStepFourValidationResult> validateConsumerBehaviorStepFour(
@RequestBody ConsumerBehaviorStepFourValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", consumerBehaviorStepFourService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping(value = "/consumer-behavior/step-3/word-preview", consumes = "multipart/form-data")
@ApiOperation("消费者需求与行为第三步Word预览")
public ResultEntity<ConsumerBehaviorWordPreviewResult> previewConsumerBehaviorStepThreeWord(
@RequestPart("file") MultipartFile file, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "预览成功", consumerBehaviorStepThreeService.previewWord(file, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@DeleteMapping("/{taskKey}") @DeleteMapping("/{taskKey}")
@ApiOperation("删除当前学生指定实训任务答案") @ApiOperation("删除当前学生指定实训任务答案")
public ResultEntity delete(@PathVariable String taskKey, public ResultEntity delete(@PathVariable String taskKey,

@ -0,0 +1,20 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ConsumerBehaviorStepFourValidationRequest {
private List<ReportRow> rows;
public List<ReportRow> getRows() { return rows; }
public void setRows(List<ReportRow> rows) { this.rows = rows; }
public static class ReportRow {
private String title;
private String content;
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
}
}

@ -0,0 +1,11 @@
package com.sztzjy.linkCommerce.entity.dto;
public class ConsumerBehaviorStepFourValidationResult {
private boolean valid;
private String message;
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

@ -0,0 +1,23 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ConsumerBehaviorStepOneValidationRequest {
private List<Hypothesis> hypotheses;
public List<Hypothesis> getHypotheses() { return hypotheses; }
public void setHypotheses(List<Hypothesis> hypotheses) { this.hypotheses = hypotheses; }
public static class Hypothesis {
private String targetGroup;
private String keyFinding;
private String coreHypothesis;
public String getTargetGroup() { return targetGroup; }
public void setTargetGroup(String targetGroup) { this.targetGroup = targetGroup; }
public String getKeyFinding() { return keyFinding; }
public void setKeyFinding(String keyFinding) { this.keyFinding = keyFinding; }
public String getCoreHypothesis() { return coreHypothesis; }
public void setCoreHypothesis(String coreHypothesis) { this.coreHypothesis = coreHypothesis; }
}
}

@ -0,0 +1,11 @@
package com.sztzjy.linkCommerce.entity.dto;
public class ConsumerBehaviorStepOneValidationResult {
private boolean valid;
private String message;
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

@ -0,0 +1,29 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ConsumerBehaviorStepThreeValidationRequest {
private List<Question> questions;
private String importedDocumentUrl;
public List<Question> getQuestions() { return questions; }
public void setQuestions(List<Question> questions) { this.questions = questions; }
public String getImportedDocumentUrl() { return importedDocumentUrl; }
public void setImportedDocumentUrl(String importedDocumentUrl) { this.importedDocumentUrl = importedDocumentUrl; }
public static class Question {
private String section;
private String type;
private String title;
private String options;
public String getSection() { return section; }
public void setSection(String section) { this.section = section; }
public String getType() { return type; }
public void setType(String type) { this.type = type; }
public String getTitle() { return title; }
public void setTitle(String title) { this.title = title; }
public String getOptions() { return options; }
public void setOptions(String options) { this.options = options; }
}
}

@ -0,0 +1,11 @@
package com.sztzjy.linkCommerce.entity.dto;
public class ConsumerBehaviorStepThreeValidationResult {
private boolean valid;
private String message;
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

@ -0,0 +1,23 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ConsumerBehaviorStepTwoValidationRequest {
private List<KanoNeed> needs;
public List<KanoNeed> getNeeds() { return needs; }
public void setNeeds(List<KanoNeed> needs) { this.needs = needs; }
public static class KanoNeed {
private String feature;
private String kanoType;
private String featureDirection;
public String getFeature() { return feature; }
public void setFeature(String feature) { this.feature = feature; }
public String getKanoType() { return kanoType; }
public void setKanoType(String kanoType) { this.kanoType = kanoType; }
public String getFeatureDirection() { return featureDirection; }
public void setFeatureDirection(String featureDirection) { this.featureDirection = featureDirection; }
}
}

@ -0,0 +1,11 @@
package com.sztzjy.linkCommerce.entity.dto;
public class ConsumerBehaviorStepTwoValidationResult {
private boolean valid;
private String message;
public boolean isValid() { return valid; }
public void setValid(boolean valid) { this.valid = valid; }
public String getMessage() { return message; }
public void setMessage(String message) { this.message = message; }
}

@ -0,0 +1,8 @@
package com.sztzjy.linkCommerce.entity.dto;
public class ConsumerBehaviorWordPreviewResult {
private String content;
public String getContent() { return content; }
public void setContent(String content) { this.content = content; }
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationResult;
public interface ConsumerBehaviorStepFourService {
ConsumerBehaviorStepFourValidationResult validate(ConsumerBehaviorStepFourValidationRequest request, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationResult;
public interface ConsumerBehaviorStepOneService {
ConsumerBehaviorStepOneValidationResult validate(ConsumerBehaviorStepOneValidationRequest request, JwtUser user);
}

@ -0,0 +1,12 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorWordPreviewResult;
import org.springframework.web.multipart.MultipartFile;
public interface ConsumerBehaviorStepThreeService {
ConsumerBehaviorStepThreeValidationResult validate(ConsumerBehaviorStepThreeValidationRequest request, JwtUser user);
ConsumerBehaviorWordPreviewResult previewWord(MultipartFile file, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationResult;
public interface ConsumerBehaviorStepTwoService {
ConsumerBehaviorStepTwoValidationResult validate(ConsumerBehaviorStepTwoValidationRequest request, JwtUser user);
}

@ -0,0 +1,48 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationResult;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepFourService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class ConsumerBehaviorStepFourServiceImpl implements ConsumerBehaviorStepFourService {
private static final int REQUIRED_REPORT_ROWS = 5;
@Override
public ConsumerBehaviorStepFourValidationResult validate(ConsumerBehaviorStepFourValidationRequest request, JwtUser user) {
requireStudent(user);
List<ConsumerBehaviorStepFourValidationRequest.ReportRow> rows = request == null ? null : request.getRows();
if (rows == null || rows.size() != REQUIRED_REPORT_ROWS) {
throw badRequest("问卷调研报告需填写 5 个报告模块");
}
for (ConsumerBehaviorStepFourValidationRequest.ReportRow row : rows) {
if (row == null || StringUtils.isBlank(row.getTitle()) || StringUtils.isBlank(row.getContent())) {
throw badRequest("请完整填写每个报告模块的标题和内容");
}
}
ConsumerBehaviorStepFourValidationResult result = new ConsumerBehaviorStepFourValidationResult();
result.setValid(true);
result.setMessage("校验通过");
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 ServiceException badRequest(String message) {
return new ServiceException(HttpStatus.BAD_REQUEST, message);
}
}

@ -0,0 +1,44 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationResult;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepOneService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
@Service
public class ConsumerBehaviorStepOneServiceImpl implements ConsumerBehaviorStepOneService {
@Override
public ConsumerBehaviorStepOneValidationResult validate(ConsumerBehaviorStepOneValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getHypotheses() == null || request.getHypotheses().isEmpty()) {
throw badRequest("请至少填写一条目标人群与问卷核心假设");
}
for (ConsumerBehaviorStepOneValidationRequest.Hypothesis hypothesis : request.getHypotheses()) {
if (hypothesis == null || StringUtils.isBlank(hypothesis.getTargetGroup())
|| StringUtils.isBlank(hypothesis.getKeyFinding()) || StringUtils.isBlank(hypothesis.getCoreHypothesis())) {
throw badRequest("请完整填写目标人群、实训8关键发现和问卷核心假设");
}
}
ConsumerBehaviorStepOneValidationResult result = new ConsumerBehaviorStepOneValidationResult();
result.setValid(true);
result.setMessage("校验通过");
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 ServiceException badRequest(String message) {
return new ServiceException(HttpStatus.BAD_REQUEST, message);
}
}

@ -0,0 +1,120 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorWordPreviewResult;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepThreeService;
import org.apache.commons.lang3.StringUtils;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.apache.poi.xwpf.usermodel.XWPFTableCell;
import org.apache.poi.xwpf.usermodel.XWPFTableRow;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Set;
@Service
public class ConsumerBehaviorStepThreeServiceImpl implements ConsumerBehaviorStepThreeService {
private static final Set<String> REQUIRED_SECTIONS = new LinkedHashSet<>(Arrays.asList("基本信息", "购买行为", "需求偏好", "开放建议"));
private static final Set<String> QUESTION_TYPES = new LinkedHashSet<>(Arrays.asList("SINGLE", "MULTIPLE", "LIKERT", "TEXT"));
private static final long MAX_WORD_FILE_SIZE = 10L * 1024 * 1024;
private static final int MAX_PREVIEW_LENGTH = 30000;
@Override
public ConsumerBehaviorStepThreeValidationResult validate(ConsumerBehaviorStepThreeValidationRequest request, JwtUser user) {
requireStudent(user);
if (request != null && StringUtils.isNotBlank(request.getImportedDocumentUrl())) {
return validResult();
}
List<ConsumerBehaviorStepThreeValidationRequest.Question> questions = request == null ? null : request.getQuestions();
if (questions == null || questions.isEmpty()) {
throw badRequest("请完整设计问卷,或导入 Word 问卷");
}
Set<String> completedSections = new LinkedHashSet<>();
boolean hasLikert = false;
for (ConsumerBehaviorStepThreeValidationRequest.Question question : questions) {
if (question == null || StringUtils.isBlank(question.getTitle())
|| !REQUIRED_SECTIONS.contains(StringUtils.trimToEmpty(question.getSection()))
|| !QUESTION_TYPES.contains(StringUtils.trimToEmpty(question.getType()))) {
throw badRequest("请完整填写每道问卷题目的分区、题型和内容");
}
if (("SINGLE".equals(question.getType()) || "MULTIPLE".equals(question.getType())) && StringUtils.isBlank(question.getOptions())) {
throw badRequest("单选题和多选题请填写选项");
}
completedSections.add(question.getSection());
hasLikert = hasLikert || ("需求偏好".equals(question.getSection()) && "LIKERT".equals(question.getType()));
}
if (!completedSections.containsAll(REQUIRED_SECTIONS) || !hasLikert) {
throw badRequest("问卷需包含基本信息、购买行为、需求偏好、开放建议四部分,并在需求偏好中包含 Likert 五级量表");
}
return validResult();
}
@Override
public ConsumerBehaviorWordPreviewResult previewWord(MultipartFile file, JwtUser user) {
requireStudent(user);
validateWordFile(file);
try (XWPFDocument document = new XWPFDocument(file.getInputStream())) {
StringBuilder content = new StringBuilder();
document.getParagraphs().forEach(paragraph -> appendLine(content, paragraph.getText()));
for (XWPFTable table : document.getTables()) {
for (XWPFTableRow row : table.getRows()) {
StringBuilder rowContent = new StringBuilder();
for (XWPFTableCell cell : row.getTableCells()) {
if (rowContent.length() > 0) rowContent.append(" | ");
rowContent.append(StringUtils.trimToEmpty(cell.getText()).replaceAll("\\s+", " "));
}
appendLine(content, rowContent.toString());
}
}
if (content.length() > MAX_PREVIEW_LENGTH) content.setLength(MAX_PREVIEW_LENGTH);
ConsumerBehaviorWordPreviewResult result = new ConsumerBehaviorWordPreviewResult();
result.setContent(content.toString().trim());
return result;
} catch (IOException e) {
throw badRequest("Word 文档解析失败,请确认文件未损坏");
}
}
private ConsumerBehaviorStepThreeValidationResult validResult() {
ConsumerBehaviorStepThreeValidationResult result = new ConsumerBehaviorStepThreeValidationResult();
result.setValid(true);
result.setMessage("校验通过");
return result;
}
private void validateWordFile(MultipartFile file) {
String fileName = file == null ? "" : StringUtils.defaultString(file.getOriginalFilename());
if (file == null || file.isEmpty()) throw badRequest("请上传 Word 问卷");
if (!StringUtils.endsWithIgnoreCase(fileName, ".docx")) throw badRequest("仅支持 .docx 格式的 Word 问卷预览");
if (file.getSize() > MAX_WORD_FILE_SIZE) throw badRequest("Word 问卷大小不能超过 10MB");
}
private void appendLine(StringBuilder content, String value) {
String line = StringUtils.trimToEmpty(value);
if (line.isEmpty() || content.length() >= MAX_PREVIEW_LENGTH) return;
if (content.length() > 0) content.append('\n');
content.append(line);
}
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 ServiceException badRequest(String message) {
return new ServiceException(HttpStatus.BAD_REQUEST, message);
}
}

@ -0,0 +1,54 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationResult;
import com.sztzjy.linkCommerce.service.ConsumerBehaviorStepTwoService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.util.Arrays;
import java.util.LinkedHashSet;
import java.util.Set;
@Service
public class ConsumerBehaviorStepTwoServiceImpl implements ConsumerBehaviorStepTwoService {
private static final int MAX_NEED_COUNT = 20;
private static final Set<String> KANO_TYPES = new LinkedHashSet<>(Arrays.asList("基本型", "期望型", "兴奋型", "无差异型", "反向型"));
@Override
public ConsumerBehaviorStepTwoValidationResult validate(ConsumerBehaviorStepTwoValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getNeeds() == null || request.getNeeds().isEmpty()) {
throw badRequest("请至少填写一项KANO需求分类");
}
if (request.getNeeds().size() > MAX_NEED_COUNT) {
throw badRequest("KANO需求分类最多填写20行");
}
for (ConsumerBehaviorStepTwoValidationRequest.KanoNeed need : request.getNeeds()) {
if (need == null || StringUtils.isBlank(need.getFeature())
|| StringUtils.isBlank(need.getFeatureDirection()) || !KANO_TYPES.contains(StringUtils.trimToEmpty(need.getKanoType()))) {
throw badRequest("请完整填写功能、KANO类型和对应功能方向");
}
}
ConsumerBehaviorStepTwoValidationResult result = new ConsumerBehaviorStepTwoValidationResult();
result.setValid(true);
result.setMessage("校验通过");
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 ServiceException badRequest(String message) {
return new ServiceException(HttpStatus.BAD_REQUEST, message);
}
}

@ -0,0 +1,64 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepFourValidationRequest;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConsumerBehaviorStepFourServiceImplTest {
@Test
void acceptsFiveCompletedReportModulesForStudent() {
ConsumerBehaviorStepFourServiceImpl service = new ConsumerBehaviorStepFourServiceImpl();
assertTrue(service.validate(validRequest(), student()).isValid());
}
@Test
void rejectsIncompleteReportRows() {
ConsumerBehaviorStepFourServiceImpl service = new ConsumerBehaviorStepFourServiceImpl();
ConsumerBehaviorStepFourValidationRequest request = validRequest();
request.getRows().get(2).setContent(" ");
assertThrows(ServiceException.class, () -> service.validate(request, student()));
}
@Test
void rejectsNonStudent() {
ConsumerBehaviorStepFourServiceImpl service = new ConsumerBehaviorStepFourServiceImpl();
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
}
private ConsumerBehaviorStepFourValidationRequest validRequest() {
ConsumerBehaviorStepFourValidationRequest request = new ConsumerBehaviorStepFourValidationRequest();
request.setRows(Arrays.asList(row("调研背景", "说明调研背景与目的"), row("调研方法", "说明样本与渠道"), row("决策因素", "分析购买决策因素"), row("群体差异", "分析不同群体差异"), row("结论建议", "提出产品建议")));
return request;
}
private ConsumerBehaviorStepFourValidationRequest.ReportRow row(String title, String content) {
ConsumerBehaviorStepFourValidationRequest.ReportRow row = new ConsumerBehaviorStepFourValidationRequest.ReportRow();
row.setTitle(title);
row.setContent(content);
return row;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("stu-1");
user.setRoleId(4);
return user;
}
private JwtUser teacher() {
JwtUser user = new JwtUser();
user.setUserId("teacher-1");
user.setRoleId(2);
return user;
}
}

@ -0,0 +1,71 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepOneValidationResult;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConsumerBehaviorStepOneServiceImplTest {
@Test
void acceptsStudentWithCompleteTargetHypotheses() {
ConsumerBehaviorStepOneServiceImpl service = new ConsumerBehaviorStepOneServiceImpl();
ConsumerBehaviorStepOneValidationResult result = service.validate(validRequest(), student());
assertTrue(result.isValid());
}
@Test
void rejectsIncompleteTargetHypothesis() {
ConsumerBehaviorStepOneServiceImpl service = new ConsumerBehaviorStepOneServiceImpl();
ConsumerBehaviorStepOneValidationRequest request = validRequest();
request.getHypotheses().get(1).setCoreHypothesis(" ");
assertThrows(ServiceException.class, () -> service.validate(request, student()));
}
@Test
void rejectsNonStudent() {
ConsumerBehaviorStepOneServiceImpl service = new ConsumerBehaviorStepOneServiceImpl();
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
}
private ConsumerBehaviorStepOneValidationRequest validRequest() {
ConsumerBehaviorStepOneValidationRequest request = new ConsumerBehaviorStepOneValidationRequest();
request.setHypotheses(Arrays.asList(
row("职场白领25-35岁", "高压工作、入睡困难、看重效率", "是否关注白噪声质量与智能场景联动"),
row("精致妈妈28-38岁", "关注宝宝睡眠、需要远程看护", "是否愿意为婴幼儿模式支付溢价"),
row("银发人群50-65岁", "睡眠浅易醒、担心操作复杂", "语音控制是否是刚需")));
return request;
}
private ConsumerBehaviorStepOneValidationRequest.Hypothesis row(String targetGroup, String keyFinding, String coreHypothesis) {
ConsumerBehaviorStepOneValidationRequest.Hypothesis row = new ConsumerBehaviorStepOneValidationRequest.Hypothesis();
row.setTargetGroup(targetGroup);
row.setKeyFinding(keyFinding);
row.setCoreHypothesis(coreHypothesis);
return row;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("stu-1");
user.setRoleId(4);
return user;
}
private JwtUser teacher() {
JwtUser user = new JwtUser();
user.setUserId("teacher-1");
user.setRoleId(2);
return user;
}
}

@ -0,0 +1,76 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepThreeValidationResult;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConsumerBehaviorStepThreeServiceImplTest {
@Test
void acceptsCompleteQuestionnaireForStudent() {
ConsumerBehaviorStepThreeServiceImpl service = new ConsumerBehaviorStepThreeServiceImpl();
ConsumerBehaviorStepThreeValidationResult result = service.validate(validRequest(), student());
assertTrue(result.isValid());
}
@Test
void rejectsQuestionnaireWithoutLikertOrOptions() {
ConsumerBehaviorStepThreeServiceImpl service = new ConsumerBehaviorStepThreeServiceImpl();
ConsumerBehaviorStepThreeValidationRequest noLikert = validRequest();
noLikert.getQuestions().get(2).setType("TEXT");
assertThrows(ServiceException.class, () -> service.validate(noLikert, student()));
ConsumerBehaviorStepThreeValidationRequest noOptions = validRequest();
noOptions.getQuestions().get(0).setOptions(" ");
assertThrows(ServiceException.class, () -> service.validate(noOptions, student()));
}
@Test
void rejectsNonStudent() {
ConsumerBehaviorStepThreeServiceImpl service = new ConsumerBehaviorStepThreeServiceImpl();
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
}
private ConsumerBehaviorStepThreeValidationRequest validRequest() {
ConsumerBehaviorStepThreeValidationRequest request = new ConsumerBehaviorStepThreeValidationRequest();
request.setQuestions(Arrays.asList(
question("基本信息", "SINGLE", "您的年龄处于以下哪个区间?", "18岁以下18-25岁26-35岁"),
question("购买行为", "MULTIPLE", "您通常通过什么渠道购买智能产品?", "电商平台,线下门店,社交平台"),
question("需求偏好", "LIKERT", "白噪声可定时关闭对您有多重要?", ""),
question("开放建议", "TEXT", "您希望智能助眠产品增加哪些功能?", "")));
return request;
}
private ConsumerBehaviorStepThreeValidationRequest.Question question(String section, String type, String title, String options) {
ConsumerBehaviorStepThreeValidationRequest.Question question = new ConsumerBehaviorStepThreeValidationRequest.Question();
question.setSection(section);
question.setType(type);
question.setTitle(title);
question.setOptions(options);
return question;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("stu-1");
user.setRoleId(4);
return user;
}
private JwtUser teacher() {
JwtUser user = new JwtUser();
user.setUserId("teacher-1");
user.setRoleId(2);
return user;
}
}

@ -0,0 +1,75 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.exception.handler.ServiceException;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ConsumerBehaviorStepTwoValidationResult;
import org.junit.jupiter.api.Test;
import java.util.Arrays;
import java.util.Collections;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ConsumerBehaviorStepTwoServiceImplTest {
@Test
void acceptsCompleteKanoNeedsForStudent() {
ConsumerBehaviorStepTwoServiceImpl service = new ConsumerBehaviorStepTwoServiceImpl();
ConsumerBehaviorStepTwoValidationResult result = service.validate(validRequest(), student());
assertTrue(result.isValid());
}
@Test
void rejectsInvalidKanoTypeAndMoreThanTwentyRows() {
ConsumerBehaviorStepTwoServiceImpl service = new ConsumerBehaviorStepTwoServiceImpl();
ConsumerBehaviorStepTwoValidationRequest invalidType = validRequest();
invalidType.getNeeds().get(0).setKanoType("未知类型");
assertThrows(ServiceException.class, () -> service.validate(invalidType, student()));
ConsumerBehaviorStepTwoValidationRequest tooMany = new ConsumerBehaviorStepTwoValidationRequest();
tooMany.setNeeds(Collections.nCopies(21, row("功能", "基本型", "功能方向")));
assertThrows(ServiceException.class, () -> service.validate(tooMany, student()));
}
@Test
void rejectsNonStudent() {
ConsumerBehaviorStepTwoServiceImpl service = new ConsumerBehaviorStepTwoServiceImpl();
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
}
private ConsumerBehaviorStepTwoValidationRequest validRequest() {
ConsumerBehaviorStepTwoValidationRequest request = new ConsumerBehaviorStepTwoValidationRequest();
request.setNeeds(Arrays.asList(
row("灯光亮度可无级调节", "期望型", "照明系统"),
row("根据时间自动调节灯光", "兴奋型", "智能场景"),
row("白噪声可定时关闭", "基本型", "音频系统")));
return request;
}
private ConsumerBehaviorStepTwoValidationRequest.KanoNeed row(String feature, String kanoType, String featureDirection) {
ConsumerBehaviorStepTwoValidationRequest.KanoNeed row = new ConsumerBehaviorStepTwoValidationRequest.KanoNeed();
row.setFeature(feature);
row.setKanoType(kanoType);
row.setFeatureDirection(featureDirection);
return row;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("stu-1");
user.setRoleId(4);
return user;
}
private JwtUser teacher() {
JwtUser user = new JwtUser();
user.setUserId("teacher-1");
user.setRoleId(2);
return user;
}
}
Loading…
Cancel
Save