feat: validate target user rfm charts
parent
4929b26861
commit
7909d4799c
@ -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.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.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,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