Merge branch 'codex/target-user-profile-step1'
# Conflicts: # src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.javabugfix/ai-assessment-evidence-rescore
commit
d70eae296c
@ -0,0 +1,32 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepFiveValidationRequest {
|
||||||
|
private List<MarketSegment> segments;
|
||||||
|
private String marketSelection;
|
||||||
|
|
||||||
|
public List<MarketSegment> getSegments() { return segments; }
|
||||||
|
public void setSegments(List<MarketSegment> segments) { this.segments = segments; }
|
||||||
|
public String getMarketSelection() { return marketSelection; }
|
||||||
|
public void setMarketSelection(String marketSelection) { this.marketSelection = marketSelection; }
|
||||||
|
|
||||||
|
public static class MarketSegment {
|
||||||
|
private String market;
|
||||||
|
private String attractiveness;
|
||||||
|
private String painPoints;
|
||||||
|
private String behaviorCharacteristics;
|
||||||
|
private String productOpportunity;
|
||||||
|
|
||||||
|
public String getMarket() { return market; }
|
||||||
|
public void setMarket(String market) { this.market = market; }
|
||||||
|
public String getAttractiveness() { return attractiveness; }
|
||||||
|
public void setAttractiveness(String attractiveness) { this.attractiveness = attractiveness; }
|
||||||
|
public String getPainPoints() { return painPoints; }
|
||||||
|
public void setPainPoints(String painPoints) { this.painPoints = painPoints; }
|
||||||
|
public String getBehaviorCharacteristics() { return behaviorCharacteristics; }
|
||||||
|
public void setBehaviorCharacteristics(String behaviorCharacteristics) { this.behaviorCharacteristics = behaviorCharacteristics; }
|
||||||
|
public String getProductOpportunity() { return productOpportunity; }
|
||||||
|
public void setProductOpportunity(String productOpportunity) { this.productOpportunity = productOpportunity; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepFiveValidationResult {
|
||||||
|
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,35 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepFourValidationRequest {
|
||||||
|
private List<UserPortrait> records;
|
||||||
|
|
||||||
|
public List<UserPortrait> getRecords() { return records; }
|
||||||
|
public void setRecords(List<UserPortrait> records) { this.records = records; }
|
||||||
|
|
||||||
|
public static class UserPortrait {
|
||||||
|
private String code;
|
||||||
|
private String user;
|
||||||
|
private String basicAttributes;
|
||||||
|
private String tags;
|
||||||
|
private String behavior;
|
||||||
|
private String painPoints;
|
||||||
|
private String psychology;
|
||||||
|
|
||||||
|
public String getCode() { return code; }
|
||||||
|
public void setCode(String code) { this.code = code; }
|
||||||
|
public String getUser() { return user; }
|
||||||
|
public void setUser(String user) { this.user = user; }
|
||||||
|
public String getBasicAttributes() { return basicAttributes; }
|
||||||
|
public void setBasicAttributes(String basicAttributes) { this.basicAttributes = basicAttributes; }
|
||||||
|
public String getTags() { return tags; }
|
||||||
|
public void setTags(String tags) { this.tags = tags; }
|
||||||
|
public String getBehavior() { return behavior; }
|
||||||
|
public void setBehavior(String behavior) { this.behavior = behavior; }
|
||||||
|
public String getPainPoints() { return painPoints; }
|
||||||
|
public void setPainPoints(String painPoints) { this.painPoints = painPoints; }
|
||||||
|
public String getPsychology() { return psychology; }
|
||||||
|
public void setPsychology(String psychology) { this.psychology = psychology; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepFourValidationResult {
|
||||||
|
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,17 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepOneValidationRequest {
|
||||||
|
private String excelUrl;
|
||||||
|
private String excelName;
|
||||||
|
private Integer previewRowCount;
|
||||||
|
private Integer previewColumnCount;
|
||||||
|
|
||||||
|
public String getExcelUrl() { return excelUrl; }
|
||||||
|
public void setExcelUrl(String excelUrl) { this.excelUrl = excelUrl; }
|
||||||
|
public String getExcelName() { return excelName; }
|
||||||
|
public void setExcelName(String excelName) { this.excelName = excelName; }
|
||||||
|
public Integer getPreviewRowCount() { return previewRowCount; }
|
||||||
|
public void setPreviewRowCount(Integer previewRowCount) { this.previewRowCount = previewRowCount; }
|
||||||
|
public Integer getPreviewColumnCount() { return previewColumnCount; }
|
||||||
|
public void setPreviewColumnCount(Integer previewColumnCount) { this.previewColumnCount = previewColumnCount; }
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepOneValidationResult {
|
||||||
|
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,20 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepThreeValidationRequest {
|
||||||
|
private List<Insight> insights;
|
||||||
|
|
||||||
|
public List<Insight> getInsights() { return insights; }
|
||||||
|
public void setInsights(List<Insight> insights) { this.insights = insights; }
|
||||||
|
|
||||||
|
public static class Insight {
|
||||||
|
private String dimension;
|
||||||
|
private String analysis;
|
||||||
|
|
||||||
|
public String getDimension() { return dimension; }
|
||||||
|
public void setDimension(String dimension) { this.dimension = dimension; }
|
||||||
|
public String getAnalysis() { return analysis; }
|
||||||
|
public void setAnalysis(String analysis) { this.analysis = analysis; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepThreeValidationResult {
|
||||||
|
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 TargetUserProfileStepTwoValidationRequest {
|
||||||
|
private List<ChartUpload> charts;
|
||||||
|
|
||||||
|
public List<ChartUpload> getCharts() { return charts; }
|
||||||
|
public void setCharts(List<ChartUpload> charts) { this.charts = charts; }
|
||||||
|
|
||||||
|
public static class ChartUpload {
|
||||||
|
private String type;
|
||||||
|
private String url;
|
||||||
|
private String name;
|
||||||
|
|
||||||
|
public String getType() { return type; }
|
||||||
|
public void setType(String type) { this.type = type; }
|
||||||
|
public String getUrl() { return url; }
|
||||||
|
public void setUrl(String url) { this.url = url; }
|
||||||
|
public String getName() { return name; }
|
||||||
|
public void setName(String name) { this.name = name; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,11 @@
|
|||||||
|
package com.sztzjy.linkCommerce.entity.dto;
|
||||||
|
|
||||||
|
public class TargetUserProfileStepTwoValidationResult {
|
||||||
|
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,9 @@
|
|||||||
|
package com.sztzjy.linkCommerce.service;
|
||||||
|
|
||||||
|
import com.sztzjy.linkCommerce.config.security.JwtUser;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationResult;
|
||||||
|
|
||||||
|
public interface TargetUserProfileStepFiveService {
|
||||||
|
TargetUserProfileStepFiveValidationResult validate(TargetUserProfileStepFiveValidationRequest 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.TargetUserProfileStepFourValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFourValidationResult;
|
||||||
|
|
||||||
|
public interface TargetUserProfileStepFourService {
|
||||||
|
TargetUserProfileStepFourValidationResult validate(TargetUserProfileStepFourValidationRequest 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.TargetUserProfileStepOneValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepOneValidationResult;
|
||||||
|
|
||||||
|
public interface TargetUserProfileStepOneService {
|
||||||
|
TargetUserProfileStepOneValidationResult validate(TargetUserProfileStepOneValidationRequest 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.TargetUserProfileStepThreeValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationResult;
|
||||||
|
|
||||||
|
public interface TargetUserProfileStepThreeService {
|
||||||
|
TargetUserProfileStepThreeValidationResult validate(TargetUserProfileStepThreeValidationRequest 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.TargetUserProfileStepTwoValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationResult;
|
||||||
|
|
||||||
|
public interface TargetUserProfileStepTwoService {
|
||||||
|
TargetUserProfileStepTwoValidationResult validate(TargetUserProfileStepTwoValidationRequest request, JwtUser user);
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationResult;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import com.sztzjy.linkCommerce.service.TargetUserProfileStepFiveService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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 TargetUserProfileStepFiveServiceImpl implements TargetUserProfileStepFiveService {
|
||||||
|
private static final String TASK_KEY = "target-user-profile";
|
||||||
|
private static final int MAX_SEGMENTS = 6;
|
||||||
|
private static final Set<String> ATTRACTIVENESS_OPTIONS = new LinkedHashSet<>(Arrays.asList("高", "中", "低"));
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StudentTrainingAnswerService studentTrainingAnswerService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetUserProfileStepFiveValidationResult validate(TargetUserProfileStepFiveValidationRequest request, JwtUser user) {
|
||||||
|
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||||
|
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
|
||||||
|
}
|
||||||
|
if (user.getRoleId() != 4) {
|
||||||
|
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
|
||||||
|
}
|
||||||
|
StudentTrainingAnswer savedAnswer = studentTrainingAnswerService.get(TASK_KEY, null, user);
|
||||||
|
String savedContent = savedAnswer == null ? "" : savedAnswer.getStep1Answer();
|
||||||
|
if (StringUtils.isBlank(savedContent) || !StringUtils.contains(savedContent, "keyUserRecords")
|
||||||
|
|| !StringUtils.contains(savedContent, "U01") || !StringUtils.contains(savedContent, "U02") || !StringUtils.contains(savedContent, "U03")) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成并保存第 4 步重点用户画像");
|
||||||
|
}
|
||||||
|
if (request == null || request.getSegments() == null || request.getSegments().isEmpty() || request.getSegments().size() > MAX_SEGMENTS) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成细分市场分析表,最多可填写 6 行");
|
||||||
|
}
|
||||||
|
for (TargetUserProfileStepFiveValidationRequest.MarketSegment segment : request.getSegments()) {
|
||||||
|
if (segment == null || StringUtils.isBlank(segment.getMarket()) || !ATTRACTIVENESS_OPTIONS.contains(StringUtils.trim(segment.getAttractiveness()))
|
||||||
|
|| StringUtils.isBlank(segment.getPainPoints()) || StringUtils.isBlank(segment.getBehaviorCharacteristics())
|
||||||
|
|| StringUtils.isBlank(segment.getProductOpportunity())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写每个细分市场的吸引力、需求痛点、行为特征和产品机会");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (StringUtils.isBlank(request.getMarketSelection())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成市场选择结论");
|
||||||
|
}
|
||||||
|
TargetUserProfileStepFiveValidationResult result = new TargetUserProfileStepFiveValidationResult();
|
||||||
|
result.setValid(true);
|
||||||
|
result.setMessage("校验通过");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,65 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFourValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFourValidationResult;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import com.sztzjy.linkCommerce.service.TargetUserProfileStepFourService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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 TargetUserProfileStepFourServiceImpl implements TargetUserProfileStepFourService {
|
||||||
|
private static final String TASK_KEY = "target-user-profile";
|
||||||
|
private static final Set<String> REQUIRED_CODES = new LinkedHashSet<>(Arrays.asList("U01", "U02", "U03"));
|
||||||
|
private static final Set<String> REQUIRED_INSIGHT_DIMENSIONS = new LinkedHashSet<>(Arrays.asList("高价值用户占比", "用户活跃度", "消费能力", "用户忠诚度", "重点客户群特征"));
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StudentTrainingAnswerService studentTrainingAnswerService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetUserProfileStepFourValidationResult validate(TargetUserProfileStepFourValidationRequest request, JwtUser user) {
|
||||||
|
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||||
|
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
|
||||||
|
}
|
||||||
|
if (user.getRoleId() != 4) {
|
||||||
|
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
|
||||||
|
}
|
||||||
|
StudentTrainingAnswer savedAnswer = studentTrainingAnswerService.get(TASK_KEY, null, user);
|
||||||
|
if (savedAnswer == null || StringUtils.isBlank(savedAnswer.getStep1Answer())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成并保存第 3 步 RFM 用户群体特点分析");
|
||||||
|
}
|
||||||
|
String savedContent = savedAnswer.getStep1Answer();
|
||||||
|
if (REQUIRED_INSIGHT_DIMENSIONS.stream().anyMatch(dimension -> !StringUtils.contains(savedContent, dimension))) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完整保存第 3 步 RFM 用户群体特点分析");
|
||||||
|
}
|
||||||
|
if (request == null || request.getRecords() == null || request.getRecords().size() != REQUIRED_CODES.size()) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成三位重点用户的完整画像");
|
||||||
|
}
|
||||||
|
Set<String> codes = new LinkedHashSet<>();
|
||||||
|
for (TargetUserProfileStepFourValidationRequest.UserPortrait record : request.getRecords()) {
|
||||||
|
String code = record == null ? "" : StringUtils.trimToEmpty(record.getCode());
|
||||||
|
if (!REQUIRED_CODES.contains(code) || !codes.add(code) || record == null
|
||||||
|
|| StringUtils.isBlank(record.getUser()) || StringUtils.isBlank(record.getBasicAttributes())
|
||||||
|
|| StringUtils.isBlank(record.getTags()) || StringUtils.isBlank(record.getBehavior())
|
||||||
|
|| StringUtils.isBlank(record.getPainPoints()) || StringUtils.isBlank(record.getPsychology())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成三位重点用户的完整画像");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!codes.equals(REQUIRED_CODES)) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "重点用户访谈记录不完整");
|
||||||
|
}
|
||||||
|
TargetUserProfileStepFourValidationResult result = new TargetUserProfileStepFourValidationResult();
|
||||||
|
result.setValid(true);
|
||||||
|
result.setMessage("校验通过");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,40 @@
|
|||||||
|
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.TargetUserProfileStepOneValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepOneValidationResult;
|
||||||
|
import com.sztzjy.linkCommerce.service.TargetUserProfileStepOneService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.http.HttpStatus;
|
||||||
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
|
import java.util.Locale;
|
||||||
|
|
||||||
|
@Service
|
||||||
|
public class TargetUserProfileStepOneServiceImpl implements TargetUserProfileStepOneService {
|
||||||
|
@Override
|
||||||
|
public TargetUserProfileStepOneValidationResult validate(TargetUserProfileStepOneValidationRequest request, JwtUser user) {
|
||||||
|
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||||
|
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
|
||||||
|
}
|
||||||
|
if (user.getRoleId() != 4) {
|
||||||
|
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
|
||||||
|
}
|
||||||
|
if (request == null || StringUtils.isBlank(request.getExcelUrl()) || StringUtils.isBlank(request.getExcelName())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请上传 RFM 用户分层分析结果 Excel 数据");
|
||||||
|
}
|
||||||
|
String excelName = request.getExcelName().trim().toLowerCase(Locale.ROOT);
|
||||||
|
if (!excelName.endsWith(".xls") && !excelName.endsWith(".xlsx")) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "仅支持 .xls、.xlsx 格式的 Excel 文件");
|
||||||
|
}
|
||||||
|
if (request.getPreviewRowCount() == null || request.getPreviewRowCount() < 1
|
||||||
|
|| request.getPreviewColumnCount() == null || request.getPreviewColumnCount() < 1) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "Excel 文件需要包含可预览的表头和数据");
|
||||||
|
}
|
||||||
|
TargetUserProfileStepOneValidationResult result = new TargetUserProfileStepOneValidationResult();
|
||||||
|
result.setValid(true);
|
||||||
|
result.setMessage("校验通过");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,63 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationResult;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import com.sztzjy.linkCommerce.service.TargetUserProfileStepThreeService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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 TargetUserProfileStepThreeServiceImpl implements TargetUserProfileStepThreeService {
|
||||||
|
private static final String TASK_KEY = "target-user-profile";
|
||||||
|
private static final Set<String> REQUIRED_DIMENSIONS = new LinkedHashSet<>(Arrays.asList("高价值用户占比", "用户活跃度", "消费能力", "用户忠诚度", "重点客户群特征"));
|
||||||
|
private static final Set<String> REQUIRED_CHART_TYPES = new LinkedHashSet<>(Arrays.asList("segment-share", "average-spend", "score-heatmap"));
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StudentTrainingAnswerService studentTrainingAnswerService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetUserProfileStepThreeValidationResult validate(TargetUserProfileStepThreeValidationRequest request, JwtUser user) {
|
||||||
|
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||||
|
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
|
||||||
|
}
|
||||||
|
if (user.getRoleId() != 4) {
|
||||||
|
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
|
||||||
|
}
|
||||||
|
StudentTrainingAnswer savedAnswer = studentTrainingAnswerService.get(TASK_KEY, null, user);
|
||||||
|
if (savedAnswer == null || StringUtils.isBlank(savedAnswer.getStep1Answer())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成并保存第 2 步 RFM 图表");
|
||||||
|
}
|
||||||
|
String savedContent = savedAnswer.getStep1Answer();
|
||||||
|
if (REQUIRED_CHART_TYPES.stream().anyMatch(type -> !StringUtils.contains(savedContent, type))) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完整保存第 2 步的三类 RFM 图表");
|
||||||
|
}
|
||||||
|
if (request == null || request.getInsights() == null || request.getInsights().size() != REQUIRED_DIMENSIONS.size()) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成五个 RFM 用户群体特点分析维度");
|
||||||
|
}
|
||||||
|
Set<String> dimensions = new LinkedHashSet<>();
|
||||||
|
for (TargetUserProfileStepThreeValidationRequest.Insight insight : request.getInsights()) {
|
||||||
|
String dimension = insight == null ? "" : StringUtils.trimToEmpty(insight.getDimension());
|
||||||
|
String analysis = insight == null ? "" : StringUtils.trimToEmpty(insight.getAnalysis());
|
||||||
|
if (!REQUIRED_DIMENSIONS.contains(dimension) || !dimensions.add(dimension) || StringUtils.isBlank(analysis)) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完成五个 RFM 用户群体特点分析维度");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!dimensions.equals(REQUIRED_DIMENSIONS)) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "提交的分析维度不完整");
|
||||||
|
}
|
||||||
|
TargetUserProfileStepThreeValidationResult result = new TargetUserProfileStepThreeValidationResult();
|
||||||
|
result.setValid(true);
|
||||||
|
result.setMessage("校验通过");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,60 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationResult;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import com.sztzjy.linkCommerce.service.TargetUserProfileStepTwoService;
|
||||||
|
import org.apache.commons.lang3.StringUtils;
|
||||||
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
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 TargetUserProfileStepTwoServiceImpl implements TargetUserProfileStepTwoService {
|
||||||
|
private static final String TASK_KEY = "target-user-profile";
|
||||||
|
private static final Set<String> REQUIRED_CHART_TYPES = new LinkedHashSet<>(Arrays.asList("segment-share", "average-spend", "score-heatmap"));
|
||||||
|
|
||||||
|
@Autowired
|
||||||
|
private StudentTrainingAnswerService studentTrainingAnswerService;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TargetUserProfileStepTwoValidationResult validate(TargetUserProfileStepTwoValidationRequest request, JwtUser user) {
|
||||||
|
if (user == null || StringUtils.isBlank(user.getUserId())) {
|
||||||
|
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
|
||||||
|
}
|
||||||
|
if (user.getRoleId() != 4) {
|
||||||
|
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
|
||||||
|
}
|
||||||
|
StudentTrainingAnswer savedAnswer = studentTrainingAnswerService.get(TASK_KEY, null, user);
|
||||||
|
if (savedAnswer == null || StringUtils.isBlank(savedAnswer.getStep1Answer())) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成并保存第 1 步 RFM 用户分层分析");
|
||||||
|
}
|
||||||
|
if (request == null || request.getCharts() == null || request.getCharts().size() != REQUIRED_CHART_TYPES.size()) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请上传三类 RFM 可视化图表");
|
||||||
|
}
|
||||||
|
Set<String> chartTypes = new LinkedHashSet<>();
|
||||||
|
for (TargetUserProfileStepTwoValidationRequest.ChartUpload chart : request.getCharts()) {
|
||||||
|
String type = chart == null ? "" : StringUtils.trimToEmpty(chart.getType());
|
||||||
|
String url = chart == null ? "" : StringUtils.trimToEmpty(chart.getUrl());
|
||||||
|
String name = chart == null ? "" : StringUtils.trimToEmpty(chart.getName()).toLowerCase();
|
||||||
|
if (!REQUIRED_CHART_TYPES.contains(type) || !chartTypes.add(type) || StringUtils.isBlank(url)
|
||||||
|
|| !(name.endsWith(".png") || name.endsWith(".jpg") || name.endsWith(".jpeg") || name.endsWith(".webp"))) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整上传客户分层占比饼图、平均消费对比柱状图和 RFM 分数分布热力图");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!chartTypes.equals(REQUIRED_CHART_TYPES)) {
|
||||||
|
throw new ServiceException(HttpStatus.BAD_REQUEST, "提交的 RFM 图表类型不完整");
|
||||||
|
}
|
||||||
|
TargetUserProfileStepTwoValidationResult result = new TargetUserProfileStepTwoValidationResult();
|
||||||
|
result.setValid(true);
|
||||||
|
result.setMessage("校验通过");
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,72 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
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.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class TargetUserProfileStepFiveServiceImplTest {
|
||||||
|
@Test
|
||||||
|
void validatesCompleteMarketSegmentsAfterPortraitsAreSaved() {
|
||||||
|
assertTrue(serviceWithPortraitHistory().validate(request(), student()).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsIncompleteSegmentAndMissingPortraitHistory() {
|
||||||
|
TargetUserProfileStepFiveValidationRequest incomplete = request();
|
||||||
|
incomplete.getSegments().get(0).setAttractiveness("");
|
||||||
|
assertThrows(ServiceException.class, () -> serviceWithPortraitHistory().validate(incomplete, student()));
|
||||||
|
|
||||||
|
TargetUserProfileStepFiveServiceImpl service = new TargetUserProfileStepFiveServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(new StudentTrainingAnswer());
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request(), student()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFiveServiceImpl serviceWithPortraitHistory() {
|
||||||
|
TargetUserProfileStepFiveServiceImpl service = new TargetUserProfileStepFiveServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"keyUserRecords\":[{\"code\":\"U01\"},{\"code\":\"U02\"},{\"code\":\"U03\"}]}");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFiveValidationRequest request() {
|
||||||
|
TargetUserProfileStepFiveValidationRequest request = new TargetUserProfileStepFiveValidationRequest();
|
||||||
|
request.setSegments(Arrays.asList(segment("职场白领"), segment("精致妈妈")));
|
||||||
|
request.setMarketSelection("优先服务有明确助眠需求的职场白领,并开发便捷的智能助眠产品。");
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFiveValidationRequest.MarketSegment segment(String market) {
|
||||||
|
TargetUserProfileStepFiveValidationRequest.MarketSegment segment = new TargetUserProfileStepFiveValidationRequest.MarketSegment();
|
||||||
|
segment.setMarket(market);
|
||||||
|
segment.setAttractiveness("高");
|
||||||
|
segment.setPainPoints("睡眠质量差,需要便捷的改善方案");
|
||||||
|
segment.setBehaviorCharacteristics("愿意尝试智能设备,并会关注健康数据");
|
||||||
|
segment.setProductOpportunity("提供智能灯光、睡眠内容和数据报告的一体化产品");
|
||||||
|
return segment;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtUser student() {
|
||||||
|
JwtUser user = new JwtUser();
|
||||||
|
user.setUserId("stu-1");
|
||||||
|
user.setRoleId(4);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFourValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
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.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class TargetUserProfileStepFourServiceImplTest {
|
||||||
|
@Test
|
||||||
|
void validatesThreeCompletePortraitsAfterInsightsAreSaved() {
|
||||||
|
assertTrue(serviceWithInsights().validate(request(), student()).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsIncompletePortraitAndMissingInsightHistory() {
|
||||||
|
TargetUserProfileStepFourValidationRequest incomplete = request();
|
||||||
|
incomplete.setRecords(new ArrayList<>(incomplete.getRecords()));
|
||||||
|
incomplete.getRecords().get(1).setPsychology("");
|
||||||
|
assertThrows(ServiceException.class, () -> serviceWithInsights().validate(incomplete, student()));
|
||||||
|
|
||||||
|
TargetUserProfileStepFourServiceImpl service = new TargetUserProfileStepFourServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"rfmInsights\":[{\"dimension\":\"用户活跃度\"}]} ");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request(), student()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFourServiceImpl serviceWithInsights() {
|
||||||
|
TargetUserProfileStepFourServiceImpl service = new TargetUserProfileStepFourServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"rfmInsights\":[\"高价值用户占比\",\"用户活跃度\",\"消费能力\",\"用户忠诚度\",\"重点客户群特征\"]}");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFourValidationRequest request() {
|
||||||
|
TargetUserProfileStepFourValidationRequest request = new TargetUserProfileStepFourValidationRequest();
|
||||||
|
request.setRecords(Arrays.asList(record("U01"), record("U02"), record("U03")));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepFourValidationRequest.UserPortrait record(String code) {
|
||||||
|
TargetUserProfileStepFourValidationRequest.UserPortrait record = new TargetUserProfileStepFourValidationRequest.UserPortrait();
|
||||||
|
record.setCode(code);
|
||||||
|
record.setUser(code + " 用户");
|
||||||
|
record.setBasicAttributes("基础属性");
|
||||||
|
record.setTags("用户标签");
|
||||||
|
record.setBehavior("行为特征");
|
||||||
|
record.setPainPoints("需求痛点");
|
||||||
|
record.setPsychology("消费心理");
|
||||||
|
return record;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtUser student() {
|
||||||
|
JwtUser user = new JwtUser();
|
||||||
|
user.setUserId("stu-1");
|
||||||
|
user.setRoleId(4);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,39 @@
|
|||||||
|
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.TargetUserProfileStepOneValidationRequest;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertThrows;
|
||||||
|
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||||
|
|
||||||
|
class TargetUserProfileStepOneServiceImplTest {
|
||||||
|
@Test
|
||||||
|
void validatesStudentRfmExcelWithPreview() {
|
||||||
|
assertTrue(new TargetUserProfileStepOneServiceImpl().validate(request("rfm.xlsx", 20, 6), student()).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsNonExcelFileAndUnpreviewableWorkbook() {
|
||||||
|
TargetUserProfileStepOneServiceImpl service = new TargetUserProfileStepOneServiceImpl();
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request("rfm.pdf", 2, 6), student()));
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request("rfm.xlsx", 0, 0), student()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepOneValidationRequest request(String excelName, int rows, int columns) {
|
||||||
|
TargetUserProfileStepOneValidationRequest request = new TargetUserProfileStepOneValidationRequest();
|
||||||
|
request.setExcelName(excelName);
|
||||||
|
request.setExcelUrl("/profile/" + excelName);
|
||||||
|
request.setPreviewRowCount(rows);
|
||||||
|
request.setPreviewColumnCount(columns);
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtUser student() {
|
||||||
|
JwtUser user = new JwtUser();
|
||||||
|
user.setUserId("stu-1");
|
||||||
|
user.setRoleId(4);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,74 @@
|
|||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
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.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class TargetUserProfileStepThreeServiceImplTest {
|
||||||
|
@Test
|
||||||
|
void validatesAllInsightsAfterChartsAreSaved() {
|
||||||
|
assertTrue(serviceWithCharts().validate(request(), student()).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsMissingInsightAndMissingChartHistory() {
|
||||||
|
TargetUserProfileStepThreeValidationRequest incomplete = request();
|
||||||
|
incomplete.setInsights(new ArrayList<>(incomplete.getInsights()));
|
||||||
|
incomplete.getInsights().remove(4);
|
||||||
|
assertThrows(ServiceException.class, () -> serviceWithCharts().validate(incomplete, student()));
|
||||||
|
|
||||||
|
TargetUserProfileStepThreeServiceImpl service = new TargetUserProfileStepThreeServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"rfmCharts\":{\"segment-share\":{}}}");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request(), student()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepThreeServiceImpl serviceWithCharts() {
|
||||||
|
TargetUserProfileStepThreeServiceImpl service = new TargetUserProfileStepThreeServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"rfmCharts\":{\"segment-share\":{},\"average-spend\":{},\"score-heatmap\":{}}}");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepThreeValidationRequest request() {
|
||||||
|
TargetUserProfileStepThreeValidationRequest request = new TargetUserProfileStepThreeValidationRequest();
|
||||||
|
request.setInsights(Arrays.asList(
|
||||||
|
insight("高价值用户占比"), insight("用户活跃度"), insight("消费能力"), insight("用户忠诚度"), insight("重点客户群特征")
|
||||||
|
));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepThreeValidationRequest.Insight insight(String dimension) {
|
||||||
|
TargetUserProfileStepThreeValidationRequest.Insight insight = new TargetUserProfileStepThreeValidationRequest.Insight();
|
||||||
|
insight.setDimension(dimension);
|
||||||
|
insight.setAnalysis("已完成" + dimension + "分析");
|
||||||
|
return insight;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtUser student() {
|
||||||
|
JwtUser user = new JwtUser();
|
||||||
|
user.setUserId("stu-1");
|
||||||
|
user.setRoleId(4);
|
||||||
|
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.StudentTrainingAnswer;
|
||||||
|
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationRequest;
|
||||||
|
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
|
||||||
|
import org.junit.jupiter.api.Test;
|
||||||
|
import org.springframework.test.util.ReflectionTestUtils;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
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.anyString;
|
||||||
|
import static org.mockito.Mockito.mock;
|
||||||
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
|
class TargetUserProfileStepTwoServiceImplTest {
|
||||||
|
@Test
|
||||||
|
void validatesAllThreeRfmChartsAfterStepOneIsSaved() {
|
||||||
|
assertTrue(serviceWithStepOne().validate(request(), student()).isValid());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void rejectsIncompleteChartsAndMissingStepOne() {
|
||||||
|
TargetUserProfileStepTwoValidationRequest incomplete = request();
|
||||||
|
incomplete.setCharts(new ArrayList<>(incomplete.getCharts()));
|
||||||
|
incomplete.getCharts().remove(2);
|
||||||
|
assertThrows(ServiceException.class, () -> serviceWithStepOne().validate(incomplete, student()));
|
||||||
|
|
||||||
|
TargetUserProfileStepTwoServiceImpl service = new TargetUserProfileStepTwoServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(null);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
assertThrows(ServiceException.class, () -> service.validate(request(), student()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepTwoServiceImpl serviceWithStepOne() {
|
||||||
|
TargetUserProfileStepTwoServiceImpl service = new TargetUserProfileStepTwoServiceImpl();
|
||||||
|
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
|
||||||
|
StudentTrainingAnswer answer = new StudentTrainingAnswer();
|
||||||
|
answer.setStep1Answer("{\"profileFileUrl\":\"/rfm.xlsx\"}");
|
||||||
|
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
|
||||||
|
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
|
||||||
|
return service;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepTwoValidationRequest request() {
|
||||||
|
TargetUserProfileStepTwoValidationRequest request = new TargetUserProfileStepTwoValidationRequest();
|
||||||
|
request.setCharts(Arrays.asList(
|
||||||
|
chart("segment-share", "share.png"),
|
||||||
|
chart("average-spend", "spend.jpg"),
|
||||||
|
chart("score-heatmap", "heatmap.webp")
|
||||||
|
));
|
||||||
|
return request;
|
||||||
|
}
|
||||||
|
|
||||||
|
private TargetUserProfileStepTwoValidationRequest.ChartUpload chart(String type, String name) {
|
||||||
|
TargetUserProfileStepTwoValidationRequest.ChartUpload chart = new TargetUserProfileStepTwoValidationRequest.ChartUpload();
|
||||||
|
chart.setType(type);
|
||||||
|
chart.setName(name);
|
||||||
|
chart.setUrl("/profile/" + name);
|
||||||
|
return chart;
|
||||||
|
}
|
||||||
|
|
||||||
|
private JwtUser student() {
|
||||||
|
JwtUser user = new JwtUser();
|
||||||
|
user.setUserId("stu-1");
|
||||||
|
user.setRoleId(4);
|
||||||
|
return user;
|
||||||
|
}
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue