feat: validate target user portraits

main
chenyuan 3 weeks ago
parent 3a3db65129
commit 18e51aec03

@ -34,6 +34,8 @@ import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationRequ
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.entity.dto.TargetUserProfileStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFourValidationResult;
import com.sztzjy.linkCommerce.service.NewProductSurveyStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepOneService;
import com.sztzjy.linkCommerce.service.MarketOpportunitySelectionStepTwoService;
@ -49,6 +51,7 @@ 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.TargetUserProfileStepFourService;
import com.sztzjy.linkCommerce.service.StudentTrainingAnswerService;
import com.sztzjy.linkCommerce.util.ResultEntity;
import io.swagger.annotations.Api;
@ -109,6 +112,9 @@ public class StudentTrainingAnswerController {
@Autowired
private TargetUserProfileStepThreeService targetUserProfileStepThreeService;
@Autowired
private TargetUserProfileStepFourService targetUserProfileStepFourService;
@GetMapping("/{taskKey}")
@ApiOperation("查询当前学生指定实训任务答案")
public ResultEntity<StudentTrainingAnswer> detail(@PathVariable String taskKey,
@ -302,6 +308,17 @@ public class StudentTrainingAnswerController {
}
}
@PostMapping("/target-user-profile/step-4/validate")
@ApiOperation("目标用户画像第四步校验")
public ResultEntity<TargetUserProfileStepFourValidationResult> validateTargetUserProfileStepFour(
@RequestBody TargetUserProfileStepFourValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过", targetUserProfileStepFourService.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,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,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,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,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;
}
}
Loading…
Cancel
Save