feat: validate target user rfm insights

main
chenyuan 3 weeks ago
parent 7909d4799c
commit 3a3db65129

@ -32,6 +32,8 @@ import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepOneValidationRequ
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepOneValidationResult;
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationResult;
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepThreeValidationResult;
import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService;
@ -46,6 +48,7 @@ import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepThreeService
import com.sztzjy.linkCommerce.service.ProductDevelopmentFactorsStepFourService;
import com.sztzjy.linkCommerce.service.TargetUserProfileStepOneService;
import com.sztzjy.linkCommerce.service.TargetUserProfileStepTwoService;
import com.sztzjy.linkCommerce.service.TargetUserProfileStepThreeService;
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
import com.sztzjy.linkCommerce.util.ResultEntity;
import io.swagger.annotations.Api;
@ -103,6 +106,9 @@ public class StudentTrainingAnswerController {
@Autowired
private TargetUserProfileStepTwoService targetUserProfileStepTwoService;
@Autowired
private TargetUserProfileStepThreeService targetUserProfileStepThreeService;
@GetMapping("/{taskKey}")
@ApiOperation("查询当前学生指定实训任务答案")
public ResultEntity<StudentTrainingAnswer> detail(@PathVariable String taskKey,
@ -285,6 +291,17 @@ public class StudentTrainingAnswerController {
}
}
@PostMapping("/target-user-profile/step-3/validate")
@ApiOperation("目标用户画像第三步校验")
public ResultEntity<TargetUserProfileStepThreeValidationResult> validateTargetUserProfileStepThree(
@RequestBody TargetUserProfileStepThreeValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", targetUserProfileStepThreeService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@DeleteMapping("/{taskKey}")
@ApiOperation("删除当前学生指定实训任务答案")
public ResultEntity delete(@PathVariable String taskKey,

@ -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…
Cancel
Save