feat: validate target user rfm insights
parent
7909d4799c
commit
3a3db65129
@ -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,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,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,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;
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue