diff --git a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java index b76b8a6..e78b7c9 100644 --- a/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java +++ b/src/main/java/com/sztzjy/linkCommerce/controller/stu/StudentTrainingAnswerController.java @@ -408,6 +408,11 @@ public class StudentTrainingAnswerController { @Autowired private com.sztzjy.linkCommerce.service.IndustryDemandAnalysisStepTwoService industryDemandAnalysisStepTwoService; @Autowired private com.sztzjy.linkCommerce.service.IndustryDemandAnalysisStepThreeService industryDemandAnalysisStepThreeService; @Autowired private com.sztzjy.linkCommerce.service.IndustryDemandAnalysisStepFourService industryDemandAnalysisStepFourService; + @Autowired private com.sztzjy.linkCommerce.service.TargetUserProfileStepOneService targetUserProfileStepOneService; + @Autowired private com.sztzjy.linkCommerce.service.TargetUserProfileStepTwoService targetUserProfileStepTwoService; + @Autowired private com.sztzjy.linkCommerce.service.TargetUserProfileStepThreeService targetUserProfileStepThreeService; + @Autowired private com.sztzjy.linkCommerce.service.TargetUserProfileStepFourService targetUserProfileStepFourService; + @Autowired private com.sztzjy.linkCommerce.service.TargetUserProfileStepFiveService targetUserProfileStepFiveService; @Autowired private SupplierEvaluationStepTwoService supplierEvaluationStepTwoService; @@ -1215,6 +1220,16 @@ public class StudentTrainingAnswerController { public ResultEntity validateIndustryDemandAnalysisStepThree(@RequestBody com.sztzjy.linkCommerce.entity.dto.IndustryDemandAnalysisStepThreeValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验通过", industryDemandAnalysisStepThreeService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } @PostMapping("/industry-demand-analysis/step-4/validate") @ApiOperation("行业需求分析第四步校验") public ResultEntity validateIndustryDemandAnalysisStepFour(@RequestBody com.sztzjy.linkCommerce.entity.dto.IndustryDemandAnalysisStepFourValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验通过", industryDemandAnalysisStepFourService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } + @PostMapping("/target-user-profile/step-1/validate") @ApiOperation("目标用户画像第一步校验") + public ResultEntity validateTargetUserProfileStepOne(@RequestBody com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepOneValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验通过", targetUserProfileStepOneService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } + @PostMapping("/target-user-profile/step-2/validate") @ApiOperation("目标用户画像第二步校验") + public ResultEntity validateTargetUserProfileStepTwo(@RequestBody com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepTwoValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验通过", targetUserProfileStepTwoService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } + @PostMapping("/target-user-profile/step-3/validate") @ApiOperation("目标用户画像第三步校验") + public ResultEntity validateTargetUserProfileStepThree(@RequestBody com.sztzjy.linkCommerce.entity.dto.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()); } } + @PostMapping("/target-user-profile/step-4/validate") @ApiOperation("目标用户画像第四步校验") + public ResultEntity validateTargetUserProfileStepFour(@RequestBody com.sztzjy.linkCommerce.entity.dto.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()); } } + @PostMapping("/target-user-profile/step-5/validate") @ApiOperation("目标用户画像第五步校验") + public ResultEntity validateTargetUserProfileStepFive(@RequestBody com.sztzjy.linkCommerce.entity.dto.TargetUserProfileStepFiveValidationRequest answer, HttpServletRequest request) { try { return new ResultEntity<>(HttpStatus.OK, "校验通过", targetUserProfileStepFiveService.validate(answer, TokenProvider.getJWTUser(request))); } catch (ServiceException e) { return new ResultEntity<>(e.getCode(), e.getMessage()); } } @DeleteMapping("/{taskKey}") @ApiOperation("删除当前学生指定实训任务答案") public ResultEntity delete(@PathVariable String taskKey, diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationRequest.java new file mode 100644 index 0000000..d67e3bc --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationRequest.java @@ -0,0 +1,32 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class TargetUserProfileStepFiveValidationRequest { + private List segments; + private String marketSelection; + + public List getSegments() { return segments; } + public void setSegments(List 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; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationResult.java new file mode 100644 index 0000000..f9e43e3 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFiveValidationResult.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationRequest.java new file mode 100644 index 0000000..0775d0d --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationRequest.java @@ -0,0 +1,35 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class TargetUserProfileStepFourValidationRequest { + private List records; + + public List getRecords() { return records; } + public void setRecords(List 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; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationResult.java new file mode 100644 index 0000000..a7daf69 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepFourValidationResult.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationRequest.java new file mode 100644 index 0000000..4fc9cb9 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationRequest.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationResult.java new file mode 100644 index 0000000..fd04039 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepOneValidationResult.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationRequest.java new file mode 100644 index 0000000..4d03eee --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationRequest.java @@ -0,0 +1,20 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class TargetUserProfileStepThreeValidationRequest { + private List insights; + + public List getInsights() { return insights; } + public void setInsights(List 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; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationResult.java new file mode 100644 index 0000000..6344fad --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepThreeValidationResult.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationRequest.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationRequest.java new file mode 100644 index 0000000..b937b47 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationRequest.java @@ -0,0 +1,23 @@ +package com.sztzjy.linkCommerce.entity.dto; + +import java.util.List; + +public class TargetUserProfileStepTwoValidationRequest { + private List charts; + + public List getCharts() { return charts; } + public void setCharts(List 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; } + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationResult.java b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationResult.java new file mode 100644 index 0000000..db4f160 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/entity/dto/TargetUserProfileStepTwoValidationResult.java @@ -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; } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFiveService.java b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFiveService.java new file mode 100644 index 0000000..7a40b23 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFiveService.java @@ -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); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFourService.java b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFourService.java new file mode 100644 index 0000000..9cb1847 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepFourService.java @@ -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); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepOneService.java b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepOneService.java new file mode 100644 index 0000000..1f5f119 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepOneService.java @@ -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); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepThreeService.java b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepThreeService.java new file mode 100644 index 0000000..426d019 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepThreeService.java @@ -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); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepTwoService.java b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepTwoService.java new file mode 100644 index 0000000..3c2b49a --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/TargetUserProfileStepTwoService.java @@ -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); +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImpl.java new file mode 100644 index 0000000..ac8cd26 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImpl.java @@ -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 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; + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImpl.java new file mode 100644 index 0000000..f1268aa --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImpl.java @@ -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 REQUIRED_CODES = new LinkedHashSet<>(Arrays.asList("U01", "U02", "U03")); + private static final Set 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 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; + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImpl.java new file mode 100644 index 0000000..c1d349e --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImpl.java @@ -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; + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImpl.java new file mode 100644 index 0000000..f94547c --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImpl.java @@ -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 REQUIRED_DIMENSIONS = new LinkedHashSet<>(Arrays.asList("高价值用户占比", "用户活跃度", "消费能力", "用户忠诚度", "重点客户群特征")); + private static final Set 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 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; + } +} diff --git a/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImpl.java b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImpl.java new file mode 100644 index 0000000..b6cdda4 --- /dev/null +++ b/src/main/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImpl.java @@ -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 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 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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImplTest.java new file mode 100644 index 0000000..b8e4ab5 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFiveServiceImplTest.java @@ -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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImplTest.java new file mode 100644 index 0000000..339d278 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepFourServiceImplTest.java @@ -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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImplTest.java new file mode 100644 index 0000000..6ab8f7a --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepOneServiceImplTest.java @@ -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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImplTest.java new file mode 100644 index 0000000..86eed67 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepThreeServiceImplTest.java @@ -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; + } +} diff --git a/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImplTest.java b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImplTest.java new file mode 100644 index 0000000..1d2a123 --- /dev/null +++ b/src/test/java/com/sztzjy/linkCommerce/service/impl/TargetUserProfileStepTwoServiceImplTest.java @@ -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; + } +}