feat: validate consumer behavior questionnaire
parent
1e409dfa69
commit
fc5b3e7b96
@ -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,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,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,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);
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue