feat: complete product pricing training steps

main
陈沅 6 days ago
parent 298d26da81
commit b4dd453afb

@ -84,6 +84,16 @@ import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidat
import com.sztzjy.linkCommerce.entity.dto.ProductValuePositioningStepFourValidationResult;
import com.sztzjy.linkCommerce.entity.dto.HardwareCostStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.HardwareCostStepOneValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationResult;
import com.sztzjy.linkCommerce.entity.dto.HardwareCostStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.HardwareCostStepTwoValidationResult;
import com.sztzjy.linkCommerce.entity.dto.HardwareCostStepThreeValidationRequest;
@ -167,6 +177,11 @@ import com.sztzjy.linkCommerce.service.ProductValuePositioningStepTwoService;
import com.sztzjy.linkCommerce.service.ProductValuePositioningStepThreeService;
import com.sztzjy.linkCommerce.service.ProductValuePositioningStepFourService;
import com.sztzjy.linkCommerce.service.HardwareCostStepOneService;
import com.sztzjy.linkCommerce.service.ProductPricingStepOneService;
import com.sztzjy.linkCommerce.service.ProductPricingStepTwoService;
import com.sztzjy.linkCommerce.service.ProductPricingStepThreeService;
import com.sztzjy.linkCommerce.service.ProductPricingStepFourService;
import com.sztzjy.linkCommerce.service.ProductPricingStepFiveService;
import com.sztzjy.linkCommerce.service.HardwareCostStepTwoService;
import com.sztzjy.linkCommerce.service.HardwareCostStepThreeService;
import com.sztzjy.linkCommerce.service.HardwareCostStepFourService;
@ -330,6 +345,21 @@ public class StudentTrainingAnswerController {
@Autowired
private HardwareCostStepOneService hardwareCostStepOneService;
@Autowired
private ProductPricingStepOneService productPricingStepOneService;
@Autowired
private ProductPricingStepTwoService productPricingStepTwoService;
@Autowired
private ProductPricingStepThreeService productPricingStepThreeService;
@Autowired
private ProductPricingStepFourService productPricingStepFourService;
@Autowired
private ProductPricingStepFiveService productPricingStepFiveService;
@Autowired
private HardwareCostStepTwoService hardwareCostStepTwoService;
@ -1109,6 +1139,66 @@ public class StudentTrainingAnswerController {
}
}
@PostMapping("/product-pricing/step-1/validate")
@ApiOperation("新产品定价第一步校验")
public ResultEntity<ProductPricingStepOneValidationResult> validateProductPricingStepOne(
@RequestBody ProductPricingStepOneValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过",
productPricingStepOneService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/product-pricing/step-2/validate")
@ApiOperation("新产品定价第二步校验")
public ResultEntity<ProductPricingStepTwoValidationResult> validateProductPricingStepTwo(
@RequestBody ProductPricingStepTwoValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过",
productPricingStepTwoService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/product-pricing/step-3/validate")
@ApiOperation("新产品定价第三步校验")
public ResultEntity<ProductPricingStepThreeValidationResult> validateProductPricingStepThree(
@RequestBody ProductPricingStepThreeValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过",
productPricingStepThreeService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/product-pricing/step-4/validate")
@ApiOperation("新产品定价第四步校验")
public ResultEntity<ProductPricingStepFourValidationResult> validateProductPricingStepFour(
@RequestBody ProductPricingStepFourValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过",
productPricingStepFourService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/product-pricing/step-5/validate")
@ApiOperation("新产品定价第五步校验")
public ResultEntity<ProductPricingStepFiveValidationResult> validateProductPricingStepFive(
@RequestBody ProductPricingStepFiveValidationRequest answer, HttpServletRequest request) {
try {
return new ResultEntity<>(HttpStatus.OK, "校验通过",
productPricingStepFiveService.validate(answer, TokenProvider.getJWTUser(request)));
} catch (ServiceException e) {
return new ResultEntity<>(e.getCode(), e.getMessage());
}
}
@PostMapping("/hardware-cost/step-2/validate")
@ApiOperation("智能硬件成本估算第二步校验")
public ResultEntity<HardwareCostStepTwoValidationResult> validateHardwareCostStepTwo(

@ -0,0 +1,19 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepFiveMethodResult {
private String key;
private String methodName;
private BigDecimal measuredPrice;
private BigDecimal grossMarginRate;
public String getKey() { return key; }
public void setKey(String key) { this.key = key; }
public String getMethodName() { return methodName; }
public void setMethodName(String methodName) { this.methodName = methodName; }
public BigDecimal getMeasuredPrice() { return measuredPrice; }
public void setMeasuredPrice(BigDecimal measuredPrice) { this.measuredPrice = measuredPrice; }
public BigDecimal getGrossMarginRate() { return grossMarginRate; }
public void setGrossMarginRate(BigDecimal grossMarginRate) { this.grossMarginRate = grossMarginRate; }
}

@ -0,0 +1,16 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ProductPricingStepFiveValidationRequest {
private String selectedCostMarkupChannel;
private List<ProductPricingStepFiveMethodResult> methodResults;
private String finalAnalysis;
public String getSelectedCostMarkupChannel() { return selectedCostMarkupChannel; }
public void setSelectedCostMarkupChannel(String selectedCostMarkupChannel) { this.selectedCostMarkupChannel = selectedCostMarkupChannel; }
public List<ProductPricingStepFiveMethodResult> getMethodResults() { return methodResults; }
public void setMethodResults(List<ProductPricingStepFiveMethodResult> methodResults) { this.methodResults = methodResults; }
public String getFinalAnalysis() { return finalAnalysis; }
public void setFinalAnalysis(String finalAnalysis) { this.finalAnalysis = finalAnalysis; }
}

@ -0,0 +1,22 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ProductPricingStepFiveValidationResult {
private boolean valid;
private String message;
private String selectedCostMarkupChannel;
private List<ProductPricingStepFiveMethodResult> methodResults;
private String finalAnalysis;
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; }
public String getSelectedCostMarkupChannel() { return selectedCostMarkupChannel; }
public void setSelectedCostMarkupChannel(String selectedCostMarkupChannel) { this.selectedCostMarkupChannel = selectedCostMarkupChannel; }
public List<ProductPricingStepFiveMethodResult> getMethodResults() { return methodResults; }
public void setMethodResults(List<ProductPricingStepFiveMethodResult> methodResults) { this.methodResults = methodResults; }
public String getFinalAnalysis() { return finalAnalysis; }
public void setFinalAnalysis(String finalAnalysis) { this.finalAnalysis = finalAnalysis; }
}

@ -0,0 +1,16 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepFourPricePoints {
private BigDecimal optimalPrice;
private BigDecimal acceptablePrice;
private String acceptableRange;
public BigDecimal getOptimalPrice() { return optimalPrice; }
public void setOptimalPrice(BigDecimal optimalPrice) { this.optimalPrice = optimalPrice; }
public BigDecimal getAcceptablePrice() { return acceptablePrice; }
public void setAcceptablePrice(BigDecimal acceptablePrice) { this.acceptablePrice = acceptablePrice; }
public String getAcceptableRange() { return acceptableRange; }
public void setAcceptableRange(String acceptableRange) { this.acceptableRange = acceptableRange; }
}

@ -0,0 +1,25 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepFourPsmRow {
private String id;
private String priceRange;
private BigDecimal tooExpensive;
private BigDecimal tooCheap;
private BigDecimal expensiveAcceptable;
private BigDecimal cheapValue;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getPriceRange() { return priceRange; }
public void setPriceRange(String priceRange) { this.priceRange = priceRange; }
public BigDecimal getTooExpensive() { return tooExpensive; }
public void setTooExpensive(BigDecimal tooExpensive) { this.tooExpensive = tooExpensive; }
public BigDecimal getTooCheap() { return tooCheap; }
public void setTooCheap(BigDecimal tooCheap) { this.tooCheap = tooCheap; }
public BigDecimal getExpensiveAcceptable() { return expensiveAcceptable; }
public void setExpensiveAcceptable(BigDecimal expensiveAcceptable) { this.expensiveAcceptable = expensiveAcceptable; }
public BigDecimal getCheapValue() { return cheapValue; }
public void setCheapValue(BigDecimal cheapValue) { this.cheapValue = cheapValue; }
}

@ -0,0 +1,13 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ProductPricingStepFourValidationRequest {
private List<ProductPricingStepFourPsmRow> psmRows;
private ProductPricingStepFourPricePoints pricePoints;
public List<ProductPricingStepFourPsmRow> getPsmRows() { return psmRows; }
public void setPsmRows(List<ProductPricingStepFourPsmRow> psmRows) { this.psmRows = psmRows; }
public ProductPricingStepFourPricePoints getPricePoints() { return pricePoints; }
public void setPricePoints(ProductPricingStepFourPricePoints pricePoints) { this.pricePoints = pricePoints; }
}

@ -0,0 +1,19 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ProductPricingStepFourValidationResult {
private boolean valid;
private String message;
private List<ProductPricingStepFourPsmRow> psmRows;
private ProductPricingStepFourPricePoints pricePoints;
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; }
public List<ProductPricingStepFourPsmRow> getPsmRows() { return psmRows; }
public void setPsmRows(List<ProductPricingStepFourPsmRow> psmRows) { this.psmRows = psmRows; }
public ProductPricingStepFourPricePoints getPricePoints() { return pricePoints; }
public void setPricePoints(ProductPricingStepFourPricePoints pricePoints) { this.pricePoints = pricePoints; }
}

@ -0,0 +1,16 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepOneChannel {
private String id;
private BigDecimal markupRate;
private BigDecimal salePrice;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public BigDecimal getMarkupRate() { return markupRate; }
public void setMarkupRate(BigDecimal markupRate) { this.markupRate = markupRate; }
public BigDecimal getSalePrice() { return salePrice; }
public void setSalePrice(BigDecimal salePrice) { this.salePrice = salePrice; }
}

@ -0,0 +1,14 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
import java.util.List;
public class ProductPricingStepOneValidationRequest {
private BigDecimal unitCost;
private List<ProductPricingStepOneChannel> pricingRows;
public BigDecimal getUnitCost() { return unitCost; }
public void setUnitCost(BigDecimal unitCost) { this.unitCost = unitCost; }
public List<ProductPricingStepOneChannel> getPricingRows() { return pricingRows; }
public void setPricingRows(List<ProductPricingStepOneChannel> pricingRows) { this.pricingRows = pricingRows; }
}

@ -0,0 +1,20 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
import java.util.List;
public class ProductPricingStepOneValidationResult {
private boolean valid;
private String message;
private BigDecimal unitCost;
private List<ProductPricingStepOneChannel> pricingRows;
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; }
public BigDecimal getUnitCost() { return unitCost; }
public void setUnitCost(BigDecimal unitCost) { this.unitCost = unitCost; }
public List<ProductPricingStepOneChannel> getPricingRows() { return pricingRows; }
public void setPricingRows(List<ProductPricingStepOneChannel> pricingRows) { this.pricingRows = pricingRows; }
}

@ -0,0 +1,14 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
import java.util.List;
public class ProductPricingStepThreeValidationRequest {
private List<ProductPricingStepThreeValueItem> valueItems;
private BigDecimal discountRate;
public List<ProductPricingStepThreeValueItem> getValueItems() { return valueItems; }
public void setValueItems(List<ProductPricingStepThreeValueItem> valueItems) { this.valueItems = valueItems; }
public BigDecimal getDiscountRate() { return discountRate; }
public void setDiscountRate(BigDecimal discountRate) { this.discountRate = discountRate; }
}

@ -0,0 +1,26 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
import java.util.List;
public class ProductPricingStepThreeValidationResult {
private boolean valid;
private String message;
private List<ProductPricingStepThreeValueItem> valueItems;
private BigDecimal perceivedValueTotal;
private BigDecimal discountRate;
private BigDecimal valueBasedPrice;
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; }
public List<ProductPricingStepThreeValueItem> getValueItems() { return valueItems; }
public void setValueItems(List<ProductPricingStepThreeValueItem> valueItems) { this.valueItems = valueItems; }
public BigDecimal getPerceivedValueTotal() { return perceivedValueTotal; }
public void setPerceivedValueTotal(BigDecimal perceivedValueTotal) { this.perceivedValueTotal = perceivedValueTotal; }
public BigDecimal getDiscountRate() { return discountRate; }
public void setDiscountRate(BigDecimal discountRate) { this.discountRate = discountRate; }
public BigDecimal getValueBasedPrice() { return valueBasedPrice; }
public void setValueBasedPrice(BigDecimal valueBasedPrice) { this.valueBasedPrice = valueBasedPrice; }
}

@ -0,0 +1,16 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepThreeValueItem {
private String id;
private String name;
private BigDecimal perceivedValue;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public BigDecimal getPerceivedValue() { return perceivedValue; }
public void setPerceivedValue(BigDecimal perceivedValue) { this.perceivedValue = perceivedValue; }
}

@ -0,0 +1,22 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepTwoCompetitor {
private String id;
private String productType;
private String brand;
private BigDecimal ecommercePrice;
private String differentiator;
public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getProductType() { return productType; }
public void setProductType(String productType) { this.productType = productType; }
public String getBrand() { return brand; }
public void setBrand(String brand) { this.brand = brand; }
public BigDecimal getEcommercePrice() { return ecommercePrice; }
public void setEcommercePrice(BigDecimal ecommercePrice) { this.ecommercePrice = ecommercePrice; }
public String getDifferentiator() { return differentiator; }
public void setDifferentiator(String differentiator) { this.differentiator = differentiator; }
}

@ -0,0 +1,22 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
public class ProductPricingStepTwoDecision {
private String benchmarkCompetitor;
private BigDecimal targetPrice;
private BigDecimal grossMarginRate;
private String differentiator;
private String pricingReason;
public String getBenchmarkCompetitor() { return benchmarkCompetitor; }
public void setBenchmarkCompetitor(String benchmarkCompetitor) { this.benchmarkCompetitor = benchmarkCompetitor; }
public BigDecimal getTargetPrice() { return targetPrice; }
public void setTargetPrice(BigDecimal targetPrice) { this.targetPrice = targetPrice; }
public BigDecimal getGrossMarginRate() { return grossMarginRate; }
public void setGrossMarginRate(BigDecimal grossMarginRate) { this.grossMarginRate = grossMarginRate; }
public String getDifferentiator() { return differentiator; }
public void setDifferentiator(String differentiator) { this.differentiator = differentiator; }
public String getPricingReason() { return pricingReason; }
public void setPricingReason(String pricingReason) { this.pricingReason = pricingReason; }
}

@ -0,0 +1,17 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.math.BigDecimal;
import java.util.List;
public class ProductPricingStepTwoValidationRequest {
private BigDecimal unitCost;
private List<ProductPricingStepTwoCompetitor> competitorRows;
private ProductPricingStepTwoDecision decision;
public BigDecimal getUnitCost() { return unitCost; }
public void setUnitCost(BigDecimal unitCost) { this.unitCost = unitCost; }
public List<ProductPricingStepTwoCompetitor> getCompetitorRows() { return competitorRows; }
public void setCompetitorRows(List<ProductPricingStepTwoCompetitor> competitorRows) { this.competitorRows = competitorRows; }
public ProductPricingStepTwoDecision getDecision() { return decision; }
public void setDecision(ProductPricingStepTwoDecision decision) { this.decision = decision; }
}

@ -0,0 +1,19 @@
package com.sztzjy.linkCommerce.entity.dto;
import java.util.List;
public class ProductPricingStepTwoValidationResult {
private boolean valid;
private String message;
private List<ProductPricingStepTwoCompetitor> competitorRows;
private ProductPricingStepTwoDecision decision;
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; }
public List<ProductPricingStepTwoCompetitor> getCompetitorRows() { return competitorRows; }
public void setCompetitorRows(List<ProductPricingStepTwoCompetitor> competitorRows) { this.competitorRows = competitorRows; }
public ProductPricingStepTwoDecision getDecision() { return decision; }
public void setDecision(ProductPricingStepTwoDecision decision) { this.decision = decision; }
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationResult;
public interface ProductPricingStepFiveService {
ProductPricingStepFiveValidationResult validate(ProductPricingStepFiveValidationRequest request, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationResult;
public interface ProductPricingStepFourService {
ProductPricingStepFourValidationResult validate(ProductPricingStepFourValidationRequest request, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationResult;
public interface ProductPricingStepOneService {
ProductPricingStepOneValidationResult validate(ProductPricingStepOneValidationRequest request, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationResult;
public interface ProductPricingStepThreeService {
ProductPricingStepThreeValidationResult validate(ProductPricingStepThreeValidationRequest request, JwtUser user);
}

@ -0,0 +1,9 @@
package com.sztzjy.linkCommerce.service;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationResult;
public interface ProductPricingStepTwoService {
ProductPricingStepTwoValidationResult validate(ProductPricingStepTwoValidationRequest request, JwtUser user);
}

@ -33,7 +33,7 @@ public class MarketOpportunitySelectionStepOneServiceImpl implements MarketOppor
@PostConstruct
public void initializeStandardAnswers() {
jdbcTemplate.execute("CREATE TABLE IF NOT EXISTS training_step_standard_answer (id varchar(64) NOT NULL COMMENT 'primary id',task_key varchar(128) NOT NULL COMMENT 'training task key',step_no int NOT NULL COMMENT 'step number',answer_json longtext NOT NULL COMMENT 'standard answer json',create_time datetime NULL,update_time datetime NULL,PRIMARY KEY (id),UNIQUE KEY uk_training_step_standard_answer (task_key, step_no)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='training step standard answer'");
jdbcTemplate.update("INSERT INTO training_step_standard_answer (id, task_key, step_no, answer_json, create_time, update_time) VALUES (?, ?, ?, ?, NOW(), NOW()) ON DUPLICATE KEY UPDATE answer_json = VALUES(answer_json), update_time = NOW()", "market-opportunity-selection-step-1", TASK_KEY, STEP_NO, JSON.toJSONString(DEFAULT_ANSWERS));
jdbcTemplate.update("INSERT IGNORE INTO training_step_standard_answer (id, task_key, step_no, answer_json, create_time, update_time) VALUES (?, ?, ?, ?, NOW(), NOW())", "market-opportunity-selection-step-1", TASK_KEY, STEP_NO, JSON.toJSONString(DEFAULT_ANSWERS));
}
@Override

@ -0,0 +1,84 @@
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.ProductPricingStepFiveMethodResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationResult;
import com.sztzjy.linkCommerce.service.ProductPricingStepFiveService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class ProductPricingStepFiveServiceImpl implements ProductPricingStepFiveService {
private static final List<String> REQUIRED_METHOD_KEYS = Arrays.asList("cost-markup", "competitive", "value-based", "psm");
private static final BigDecimal MIN_MARGIN_RATE = new BigDecimal("-100");
private static final BigDecimal MAX_MARGIN_RATE = new BigDecimal("100");
@Override
public ProductPricingStepFiveValidationResult validate(ProductPricingStepFiveValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || StringUtils.isBlank(request.getSelectedCostMarkupChannel())) {
throw badRequest("请选择用于汇总的成本加成定价渠道");
}
if (request.getMethodResults() == null || request.getMethodResults().size() != REQUIRED_METHOD_KEYS.size()) {
throw badRequest("请完整汇总四种定价方法的测算结果");
}
List<ProductPricingStepFiveMethodResult> results = new ArrayList<>();
Set<String> keys = new HashSet<>();
for (ProductPricingStepFiveMethodResult source : request.getMethodResults()) {
if (source == null || StringUtils.isAnyBlank(source.getKey(), source.getMethodName())
|| source.getMeasuredPrice() == null || source.getMeasuredPrice().compareTo(BigDecimal.ZERO) <= 0
|| source.getGrossMarginRate() == null) {
throw badRequest("请完整填写四种定价方法的测算结果和毛利润率");
}
String key = source.getKey().trim();
if (!REQUIRED_METHOD_KEYS.contains(key) || !keys.add(key)) {
throw badRequest("定价方法汇总项不正确或重复");
}
if (source.getGrossMarginRate().compareTo(MIN_MARGIN_RATE) < 0 || source.getGrossMarginRate().compareTo(MAX_MARGIN_RATE) > 0) {
throw badRequest("毛利润率应在 -100% 至 100% 之间");
}
ProductPricingStepFiveMethodResult result = new ProductPricingStepFiveMethodResult();
result.setKey(key);
result.setMethodName(source.getMethodName().trim());
result.setMeasuredPrice(source.getMeasuredPrice().setScale(2, RoundingMode.HALF_UP));
result.setGrossMarginRate(source.getGrossMarginRate().setScale(2, RoundingMode.HALF_UP));
results.add(result);
}
if (!keys.containsAll(REQUIRED_METHOD_KEYS) || StringUtils.isBlank(request.getFinalAnalysis())) {
throw badRequest("请填写最终量产定价分析");
}
ProductPricingStepFiveValidationResult result = new ProductPricingStepFiveValidationResult();
result.setValid(true);
result.setMessage("最终量产定价汇总已校验");
result.setSelectedCostMarkupChannel(request.getSelectedCostMarkupChannel().trim());
result.setMethodResults(results);
result.setFinalAnalysis(request.getFinalAnalysis().trim());
return result;
}
private void requireStudent(JwtUser user) {
if (user == null || StringUtils.isBlank(user.getUserId())) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录");
}
if (user.getRoleId() != 4) {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可提交实训答案");
}
}
private ServiceException badRequest(String message) {
return new ServiceException(HttpStatus.BAD_REQUEST, message);
}
}

@ -0,0 +1,93 @@
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.ProductPricingStepFourPricePoints;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourPsmRow;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationResult;
import com.sztzjy.linkCommerce.service.ProductPricingStepFourService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class ProductPricingStepFourServiceImpl implements ProductPricingStepFourService {
private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
private static final List<String> REQUIRED_PRICE_RANGES = Arrays.asList(
"≤100元", "100~150元", "150~200元", "200~250元", "250~300元", "≥300元");
@Override
public ProductPricingStepFourValidationResult validate(ProductPricingStepFourValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getPsmRows() == null || request.getPsmRows().size() < REQUIRED_PRICE_RANGES.size()) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写 6 个固定价格区间的 PSM 累计占比");
}
List<ProductPricingStepFourPsmRow> rows = new ArrayList<>();
Set<String> ids = new HashSet<>();
Set<String> priceRanges = new HashSet<>();
for (int index = 0; index < request.getPsmRows().size(); index++) {
ProductPricingStepFourPsmRow source = request.getPsmRows().get(index);
if (source == null || StringUtils.isAnyBlank(source.getId(), source.getPriceRange())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写第 " + (index + 1) + " 行 PSM 数据");
}
if (!ids.add(source.getId().trim()) || !priceRanges.add(source.getPriceRange().trim())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "PSM 价格区间不能重复");
}
ProductPricingStepFourPsmRow row = new ProductPricingStepFourPsmRow();
row.setId(source.getId().trim());
row.setPriceRange(source.getPriceRange().trim());
row.setTooExpensive(normalizePercentage(source.getTooExpensive(), index, "太贵不买"));
row.setTooCheap(normalizePercentage(source.getTooCheap(), index, "太便宜不买"));
row.setExpensiveAcceptable(normalizePercentage(source.getExpensiveAcceptable(), index, "偏高可接受"));
row.setCheapValue(normalizePercentage(source.getCheapValue(), index, "便宜划算"));
rows.add(row);
}
if (!priceRanges.containsAll(REQUIRED_PRICE_RANGES)) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请保留并填写 6 个固定价格区间");
}
ProductPricingStepFourPricePoints sourcePoints = request.getPricePoints();
if (sourcePoints == null || sourcePoints.getOptimalPrice() == null || sourcePoints.getOptimalPrice().compareTo(BigDecimal.ZERO) <= 0
|| sourcePoints.getAcceptablePrice() == null || sourcePoints.getAcceptablePrice().compareTo(BigDecimal.ZERO) <= 0
|| StringUtils.isBlank(sourcePoints.getAcceptableRange())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写最优价格点、可接受价格点和可接受价格范围");
}
ProductPricingStepFourPricePoints pricePoints = new ProductPricingStepFourPricePoints();
pricePoints.setOptimalPrice(sourcePoints.getOptimalPrice().setScale(2, RoundingMode.HALF_UP));
pricePoints.setAcceptablePrice(sourcePoints.getAcceptablePrice().setScale(2, RoundingMode.HALF_UP));
pricePoints.setAcceptableRange(sourcePoints.getAcceptableRange().trim());
ProductPricingStepFourValidationResult result = new ProductPricingStepFourValidationResult();
result.setValid(true);
result.setMessage("PSM 价格敏感度测试已校验");
result.setPsmRows(rows);
result.setPricePoints(pricePoints);
return result;
}
private BigDecimal normalizePercentage(BigDecimal value, int rowIndex, String name) {
if (value == null || value.compareTo(BigDecimal.ZERO) < 0 || value.compareTo(ONE_HUNDRED) > 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "第 " + (rowIndex + 1) + " 行“" + name + "”累计占比应在 0%100% 之间");
}
return value.setScale(2, RoundingMode.HALF_UP);
}
private void requireStudent(JwtUser user) {
if (user == null || StringUtils.isBlank(user.getUserId())) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
}
if (user.getRoleId() != 4) {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
}
}
}

@ -0,0 +1,88 @@
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.ProductPricingStepOneChannel;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationResult;
import com.sztzjy.linkCommerce.service.ProductPricingStepOneService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@Service
public class ProductPricingStepOneServiceImpl implements ProductPricingStepOneService {
private static final Map<String, List<BigDecimal>> CHANNEL_RANGES = new LinkedHashMap<>();
static {
CHANNEL_RANGES.put("dealer", Arrays.asList(new BigDecimal("15"), new BigDecimal("25")));
CHANNEL_RANGES.put("direct-retail", Arrays.asList(new BigDecimal("40"), new BigDecimal("60")));
CHANNEL_RANGES.put("premium-brand", Arrays.asList(new BigDecimal("70"), new BigDecimal("80")));
}
@Override
public ProductPricingStepOneValidationResult validate(ProductPricingStepOneValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getUnitCost() == null || request.getUnitCost().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "单台综合成本必须大于 0");
}
List<ProductPricingStepOneChannel> rows = request.getPricingRows();
if (rows == null || rows.size() != CHANNEL_RANGES.size()) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请填写三个渠道的加成率");
}
Map<String, ProductPricingStepOneChannel> source = new LinkedHashMap<>();
for (ProductPricingStepOneChannel row : rows) {
String id = row == null ? null : StringUtils.trimToNull(row.getId());
if (id == null || source.put(id, row) != null) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "渠道信息不正确,请重新填写");
}
}
BigDecimal normalizedCost = request.getUnitCost().setScale(2, RoundingMode.HALF_UP);
List<ProductPricingStepOneChannel> normalized = new ArrayList<>();
for (Map.Entry<String, List<BigDecimal>> entry : CHANNEL_RANGES.entrySet()) {
ProductPricingStepOneChannel row = source.get(entry.getKey());
if (row == null || row.getMarkupRate() == null) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请填写完整的渠道加成率");
}
BigDecimal markupRate = row.getMarkupRate();
BigDecimal min = entry.getValue().get(0);
BigDecimal max = entry.getValue().get(1);
if (markupRate.compareTo(min) < 0 || markupRate.compareTo(max) > 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST,
"渠道加成率需在 " + min.toPlainString() + "%~" + max.toPlainString() + "% 范围内");
}
ProductPricingStepOneChannel item = new ProductPricingStepOneChannel();
item.setId(entry.getKey());
item.setMarkupRate(markupRate.setScale(2, RoundingMode.HALF_UP));
item.setSalePrice(normalizedCost.multiply(BigDecimal.ONE.add(markupRate.movePointLeft(2)))
.setScale(2, RoundingMode.HALF_UP));
normalized.add(item);
}
ProductPricingStepOneValidationResult result = new ProductPricingStepOneValidationResult();
result.setValid(true);
result.setMessage("渠道加成定价已校验");
result.setUnitCost(normalizedCost);
result.setPricingRows(normalized);
return result;
}
private void requireStudent(JwtUser user) {
if (user == null || StringUtils.isBlank(user.getUserId())) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
}
if (user.getRoleId() != 4) {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
}
}
}

@ -0,0 +1,80 @@
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.ProductPricingStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValueItem;
import com.sztzjy.linkCommerce.service.ProductPricingStepThreeService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class ProductPricingStepThreeServiceImpl implements ProductPricingStepThreeService {
private static final BigDecimal ONE_HUNDRED = new BigDecimal("100");
@Override
public ProductPricingStepThreeValidationResult validate(ProductPricingStepThreeValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getValueItems() == null || request.getValueItems().isEmpty()) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请至少填写一项可替代产品或功能的价值感知");
}
if (request.getDiscountRate() == null || request.getDiscountRate().compareTo(BigDecimal.ZERO) <= 0
|| request.getDiscountRate().compareTo(ONE_HUNDRED) > 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "利润让渡折扣率应大于 0 且不超过 100%");
}
List<ProductPricingStepThreeValueItem> items = new ArrayList<>();
Set<String> ids = new HashSet<>();
BigDecimal total = BigDecimal.ZERO;
for (int index = 0; index < request.getValueItems().size(); index++) {
ProductPricingStepThreeValueItem source = request.getValueItems().get(index);
if (source == null || StringUtils.isAnyBlank(source.getId(), source.getName())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写第 " + (index + 1) + " 项价值感知");
}
if (!ids.add(source.getId().trim())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "价值感知条目不能重复");
}
if (source.getPerceivedValue() == null || source.getPerceivedValue().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "第 " + (index + 1) + " 项价值感知金额必须大于 0");
}
ProductPricingStepThreeValueItem item = new ProductPricingStepThreeValueItem();
item.setId(source.getId().trim());
item.setName(source.getName().trim());
item.setPerceivedValue(source.getPerceivedValue().setScale(2, RoundingMode.HALF_UP));
items.add(item);
total = total.add(item.getPerceivedValue());
}
BigDecimal perceivedValueTotal = total.setScale(2, RoundingMode.HALF_UP);
BigDecimal discountRate = request.getDiscountRate().setScale(2, RoundingMode.HALF_UP);
BigDecimal valueBasedPrice = perceivedValueTotal.multiply(discountRate).movePointLeft(2)
.setScale(2, RoundingMode.HALF_UP);
ProductPricingStepThreeValidationResult result = new ProductPricingStepThreeValidationResult();
result.setValid(true);
result.setMessage("用户感知价值定价已校验");
result.setValueItems(items);
result.setPerceivedValueTotal(perceivedValueTotal);
result.setDiscountRate(discountRate);
result.setValueBasedPrice(valueBasedPrice);
return result;
}
private void requireStudent(JwtUser user) {
if (user == null || StringUtils.isBlank(user.getUserId())) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
}
if (user.getRoleId() != 4) {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
}
}
}

@ -0,0 +1,97 @@
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.ProductPricingStepTwoCompetitor;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoDecision;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationResult;
import com.sztzjy.linkCommerce.service.ProductPricingStepTwoService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
@Service
public class ProductPricingStepTwoServiceImpl implements ProductPricingStepTwoService {
private static final int REQUIRED_COMPETITOR_COUNT = 4;
@Override
public ProductPricingStepTwoValidationResult validate(ProductPricingStepTwoValidationRequest request, JwtUser user) {
requireStudent(user);
if (request == null || request.getUnitCost() == null || request.getUnitCost().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请先完成第 1 步并填写单台综合成本");
}
List<ProductPricingStepTwoCompetitor> sourceRows = request.getCompetitorRows();
if (sourceRows == null || sourceRows.size() != REQUIRED_COMPETITOR_COUNT) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写 4 个竞品信息");
}
List<ProductPricingStepTwoCompetitor> competitors = new ArrayList<>();
Set<String> productTypes = new HashSet<>();
for (int index = 0; index < sourceRows.size(); index++) {
ProductPricingStepTwoCompetitor row = sourceRows.get(index);
if (row == null || StringUtils.isAnyBlank(row.getId(), row.getProductType(), row.getBrand(), row.getDifferentiator())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写竞品 " + (index + 1) + " 的信息");
}
if (row.getEcommercePrice() == null || row.getEcommercePrice().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "竞品 " + (index + 1) + " 的电商售价必须大于 0");
}
String productType = row.getProductType().trim();
if (!productTypes.add(productType)) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "竞品产品类型不能重复,请使用可区分的竞品名称");
}
ProductPricingStepTwoCompetitor item = new ProductPricingStepTwoCompetitor();
item.setId(row.getId().trim());
item.setProductType(productType);
item.setBrand(row.getBrand().trim());
item.setEcommercePrice(row.getEcommercePrice().setScale(2, RoundingMode.HALF_UP));
item.setDifferentiator(row.getDifferentiator().trim());
competitors.add(item);
}
ProductPricingStepTwoDecision sourceDecision = request.getDecision();
if (sourceDecision == null || StringUtils.isAnyBlank(sourceDecision.getBenchmarkCompetitor(),
sourceDecision.getDifferentiator(), sourceDecision.getPricingReason())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "请完整填写对标竞品、差异化优势和定价原因");
}
if (!productTypes.contains(sourceDecision.getBenchmarkCompetitor().trim())) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "对标竞品必须从上方已填写的竞品中选择");
}
if (sourceDecision.getTargetPrice() == null || sourceDecision.getTargetPrice().compareTo(BigDecimal.ZERO) <= 0) {
throw new ServiceException(HttpStatus.BAD_REQUEST, "本产品定价必须大于 0");
}
BigDecimal targetPrice = sourceDecision.getTargetPrice().setScale(2, RoundingMode.HALF_UP);
BigDecimal grossMarginRate = targetPrice.subtract(request.getUnitCost()).divide(targetPrice, 6, RoundingMode.HALF_UP)
.movePointRight(2).setScale(2, RoundingMode.HALF_UP);
ProductPricingStepTwoDecision decision = new ProductPricingStepTwoDecision();
decision.setBenchmarkCompetitor(sourceDecision.getBenchmarkCompetitor().trim());
decision.setTargetPrice(targetPrice);
decision.setGrossMarginRate(grossMarginRate);
decision.setDifferentiator(sourceDecision.getDifferentiator().trim());
decision.setPricingReason(sourceDecision.getPricingReason().trim());
ProductPricingStepTwoValidationResult result = new ProductPricingStepTwoValidationResult();
result.setValid(true);
result.setMessage("竞品价格带与定价已校验");
result.setCompetitorRows(competitors);
result.setDecision(decision);
return result;
}
private void requireStudent(JwtUser user) {
if (user == null || StringUtils.isBlank(user.getUserId())) {
throw new ServiceException(HttpStatus.UNAUTHORIZED, "请先登录后再校验");
}
if (user.getRoleId() != 4) {
throw new ServiceException(HttpStatus.FORBIDDEN, "仅学生可进行实训校验");
}
}
}

@ -36,7 +36,7 @@ class MarketOpportunitySelectionStepFourServiceImplTest {
private MarketOpportunitySelectionStepFourServiceImpl serviceWithStepThree() {
MarketOpportunitySelectionStepFourServiceImpl service = new MarketOpportunitySelectionStepFourServiceImpl();
StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class);
StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep3Answer("{\"scores\":{}}");
StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep3Answer("{\"scores\":{\"smart-fitness-mirror\":86.50,\"smart-bathroom-mirror\":82.00,\"smart-beauty-mirror\":78.25}}");
when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer);
ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers);
return service;

@ -29,7 +29,7 @@ class MarketOpportunitySelectionStepOneServiceImplTest {
void checkReturnsPerIndicatorFeedbackWithoutExposingTheAnswer() {
MarketOpportunitySelectionStepOneServiceImpl service = new MarketOpportunitySelectionStepOneServiceImpl(standardAnswerJdbcTemplate());
MarketOpportunitySelectionStepOneCheckResult result = service.check(requestWithChangedSecondCategory("市场规模"), student());
assertEquals(7, result.getCorrectCount()); assertEquals(8, result.getTotalCount()); assertFalse(result.isAllCorrect()); assertTrue(result.getItems().get(0).isCorrect()); assertFalse(result.getItems().get(1).isCorrect()); assertFalse(JSON.toJSONString(result).contains("correctCategory"));
assertEquals(8, result.getCorrectCount()); assertEquals(9, result.getTotalCount()); assertFalse(result.isAllCorrect()); assertTrue(result.getItems().get(0).isCorrect()); assertFalse(result.getItems().get(1).isCorrect()); assertFalse(JSON.toJSONString(result).contains("correctCategory"));
}
@Test
@ -48,7 +48,7 @@ class MarketOpportunitySelectionStepOneServiceImplTest {
}
private JdbcTemplate standardAnswerJdbcTemplate() { JdbcTemplate jdbcTemplate = mock(JdbcTemplate.class); Map<String, String> answers = new LinkedHashMap<>(); for (MarketOpportunitySelectionStepOneCheckRequest.Item item : defaultAnswers()) answers.put(item.getId(), item.getCategory()); when(jdbcTemplate.queryForObject(any(String.class), any(Class.class), any(), any())).thenReturn(JSON.toJSONString(answers)); return jdbcTemplate; }
private List<MarketOpportunitySelectionStepOneCheckRequest.Item> defaultAnswers() { return Arrays.asList(item("search-count", "市场规模"), item("demand-supply-ratio", "竞争强度"), item("transaction-amount", "市场规模"), item("transaction-growth", "市场潜力"), item("category-transaction-growth", "市场潜力"), item("online-products", "竞争强度"), item("online-merchants", "竞争强度"), item("organic-traffic-ratio", "运营难度")); }
private List<MarketOpportunitySelectionStepOneCheckRequest.Item> defaultAnswers() { return Arrays.asList(item("search-count", "市场规模"), item("demand-supply-ratio", "竞争强度"), item("transaction-amount", "市场规模"), item("transaction-growth", "市场潜力"), item("category-transaction-growth", "市场潜力"), item("online-products", "竞争强度"), item("online-merchants", "竞争强度"), item("organic-traffic-ratio", "运营难度"), item("return-rate", "运营难度")); }
private MarketOpportunitySelectionStepOneCheckRequest requestWithChangedSecondCategory(String category) { List<MarketOpportunitySelectionStepOneCheckRequest.Item> items = defaultAnswers(); items.get(1).setCategory(category); return request(items); }
private MarketOpportunitySelectionStepOneCheckRequest request(List<MarketOpportunitySelectionStepOneCheckRequest.Item> items) { MarketOpportunitySelectionStepOneCheckRequest request = new MarketOpportunitySelectionStepOneCheckRequest(); request.setItems(items); return request; }
private MarketOpportunitySelectionStepOneCheckRequest.Item item(String id, String category) { MarketOpportunitySelectionStepOneCheckRequest.Item item = new MarketOpportunitySelectionStepOneCheckRequest.Item(); item.setId(id); item.setCategory(category); return item; }

@ -33,7 +33,7 @@ class MarketOpportunitySelectionStepThreeServiceImplTest {
MarketOpportunitySelectionStepThreeServiceImpl service = new MarketOpportunitySelectionStepThreeServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(null);
assertThrows(ServiceException.class, () -> service.validate(completedRequest(), student()));
}
private MarketOpportunitySelectionStepThreeServiceImpl serviceWithStepTwo() { MarketOpportunitySelectionStepThreeServiceImpl service = new MarketOpportunitySelectionStepThreeServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep2Answer("{\"weights\":{}}"); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); return service; }
private MarketOpportunitySelectionStepThreeServiceImpl serviceWithStepTwo() { MarketOpportunitySelectionStepThreeServiceImpl service = new MarketOpportunitySelectionStepThreeServiceImpl(); StudentTrainingAnswerService answers = mock(StudentTrainingAnswerService.class); StudentTrainingAnswer answer = new StudentTrainingAnswer(); answer.setStep2Answer("{\"primaryWeights\":{\"市场规模\":25,\"市场潜力\":25,\"竞争强度\":25,\"运营难度\":25},\"dimensionWeights\":{\"search-count\":50,\"transaction-amount\":50,\"transaction-growth\":50,\"category-transaction-growth\":50,\"demand-supply-ratio\":34,\"online-products\":33,\"online-merchants\":33,\"organic-traffic-ratio\":50,\"return-rate\":50}}"); when(answers.get(anyString(), any(), any(JwtUser.class))).thenReturn(answer); ReflectionTestUtils.setField(service, "studentTrainingAnswerService", answers); return service; }
private MarketOpportunitySelectionStepThreeValidationRequest completedRequest() { MarketOpportunitySelectionStepThreeValidationRequest request = new MarketOpportunitySelectionStepThreeValidationRequest(); ArrayList<MarketOpportunitySelectionStepThreeValidationRequest.Score> scores = new ArrayList<>(); Arrays.asList("smart-fitness-mirror", "smart-bathroom-mirror", "smart-beauty-mirror").forEach(id -> { MarketOpportunitySelectionStepThreeValidationRequest.Score score = new MarketOpportunitySelectionStepThreeValidationRequest.Score(); score.setId(id); score.setScore(new BigDecimal("86.50")); scores.add(score); }); request.setScores(scores); return request; }
private JwtUser student() { JwtUser user = new JwtUser(); user.setUserId("stu-1"); user.setRoleId(4); return user; }
}

@ -0,0 +1,55 @@
package com.sztzjy.linkCommerce.service.impl;
import com.sztzjy.linkCommerce.config.security.JwtUser;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveMethodResult;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFiveValidationResult;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductPricingStepFiveServiceImplTest {
@Test
void acceptsAllFourPricingMethodsAndTrimsFinalAnalysis() {
ProductPricingStepFiveValidationResult result = new ProductPricingStepFiveServiceImpl()
.validate(validRequest(), student());
assertTrue(result.isValid());
assertEquals(4, result.getMethodResults().size());
assertEquals(new BigDecimal("188.00"), result.getMethodResults().get(0).getMeasuredPrice());
assertEquals("综合四种定价结果,选择兼顾市场接受度和合理利润的最终量产定价。", result.getFinalAnalysis());
}
private ProductPricingStepFiveValidationRequest validRequest() {
ProductPricingStepFiveValidationRequest request = new ProductPricingStepFiveValidationRequest();
request.setSelectedCostMarkupChannel("厂家直接电商零售");
request.setMethodResults(Arrays.asList(
method("cost-markup", "成本加成定价", "188", "46.81"),
method("competitive", "竞争定价", "199", "49.75"),
method("value-based", "价值定价", "210", "52.38"),
method("psm", "PSM价格敏感度", "180", "44.44")
));
request.setFinalAnalysis(" 综合四种定价结果,选择兼顾市场接受度和合理利润的最终量产定价。 ");
return request;
}
private ProductPricingStepFiveMethodResult method(String key, String methodName, String price, String grossMarginRate) {
ProductPricingStepFiveMethodResult method = new ProductPricingStepFiveMethodResult();
method.setKey(key);
method.setMethodName(methodName);
method.setMeasuredPrice(new BigDecimal(price));
method.setGrossMarginRate(new BigDecimal(grossMarginRate));
return method;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("student-1");
user.setRoleId(4);
return user;
}
}

@ -0,0 +1,71 @@
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.ProductPricingStepFourPricePoints;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourPsmRow;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepFourValidationResult;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductPricingStepFourServiceImplTest {
@Test
void validatesSixPriceRangesAndNormalizesPsmPercentages() {
ProductPricingStepFourValidationResult result = new ProductPricingStepFourServiceImpl()
.validate(validRequest(), student());
assertTrue(result.isValid());
assertEquals(6, result.getPsmRows().size());
assertEquals(new BigDecimal("20.00"), result.getPsmRows().get(0).getTooExpensive());
assertEquals(new BigDecimal("180.00"), result.getPricePoints().getOptimalPrice());
}
@Test
void rejectsPsmPercentageOutsideZeroToOneHundred() {
ProductPricingStepFourValidationRequest request = validRequest();
request.getPsmRows().get(0).setTooCheap(new BigDecimal("101"));
assertThrows(ServiceException.class,
() -> new ProductPricingStepFourServiceImpl().validate(request, student()));
}
private ProductPricingStepFourValidationRequest validRequest() {
ProductPricingStepFourValidationRequest request = new ProductPricingStepFourValidationRequest();
request.setPsmRows(Arrays.asList(
row("psm-under-100", "≤100元"), row("psm-100-150", "100~150元"),
row("psm-150-200", "150~200元"), row("psm-200-250", "200~250元"),
row("psm-250-300", "250~300元"), row("psm-over-300", "≥300元")
));
ProductPricingStepFourPricePoints pricePoints = new ProductPricingStepFourPricePoints();
pricePoints.setOptimalPrice(new BigDecimal("180"));
pricePoints.setAcceptablePrice(new BigDecimal("220"));
pricePoints.setAcceptableRange("150~260元");
request.setPricePoints(pricePoints);
return request;
}
private ProductPricingStepFourPsmRow row(String id, String priceRange) {
ProductPricingStepFourPsmRow row = new ProductPricingStepFourPsmRow();
row.setId(id);
row.setPriceRange(priceRange);
row.setTooExpensive(new BigDecimal("20"));
row.setTooCheap(new BigDecimal("30"));
row.setExpensiveAcceptable(new BigDecimal("40"));
row.setCheapValue(new BigDecimal("50"));
return row;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("student-1");
user.setRoleId(4);
return user;
}
}

@ -0,0 +1,70 @@
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.ProductPricingStepOneChannel;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepOneValidationResult;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductPricingStepOneServiceImplTest {
@Test
void validatesConfiguredChannelsAndRecalculatesSalePrices() {
ProductPricingStepOneValidationResult result = new ProductPricingStepOneServiceImpl()
.validate(validRequest(), student());
assertTrue(result.isValid());
assertEquals(new BigDecimal("100.00"), result.getUnitCost());
assertEquals(new BigDecimal("120.00"), result.getPricingRows().get(0).getSalePrice());
assertEquals(new BigDecimal("150.00"), result.getPricingRows().get(1).getSalePrice());
assertEquals(new BigDecimal("175.00"), result.getPricingRows().get(2).getSalePrice());
}
@Test
void rejectsOutOfRangeMarkupAndNonStudentUser() {
ProductPricingStepOneServiceImpl service = new ProductPricingStepOneServiceImpl();
ProductPricingStepOneValidationRequest invalid = validRequest();
invalid.getPricingRows().get(0).setMarkupRate(new BigDecimal("26"));
assertThrows(ServiceException.class, () -> service.validate(invalid, student()));
assertThrows(ServiceException.class, () -> service.validate(validRequest(), teacher()));
}
private ProductPricingStepOneValidationRequest validRequest() {
ProductPricingStepOneValidationRequest request = new ProductPricingStepOneValidationRequest();
request.setUnitCost(new BigDecimal("100"));
request.setPricingRows(Arrays.asList(
row("dealer", "20"),
row("direct-retail", "50"),
row("premium-brand", "75")
));
return request;
}
private ProductPricingStepOneChannel row(String id, String markupRate) {
ProductPricingStepOneChannel channel = new ProductPricingStepOneChannel();
channel.setId(id);
channel.setMarkupRate(new BigDecimal(markupRate));
return channel;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("student-1");
user.setRoleId(4);
return user;
}
private JwtUser teacher() {
JwtUser user = student();
user.setRoleId(3);
return user;
}
}

@ -0,0 +1,59 @@
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.ProductPricingStepThreeValueItem;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepThreeValidationResult;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductPricingStepThreeServiceImplTest {
@Test
void totalsPerceivedValueAndCalculatesPriceFromDiscountRate() {
ProductPricingStepThreeValidationResult result = new ProductPricingStepThreeServiceImpl()
.validate(validRequest(), student());
assertTrue(result.isValid());
assertEquals(new BigDecimal("130.00"), result.getPerceivedValueTotal());
assertEquals(new BigDecimal("80.00"), result.getDiscountRate());
assertEquals(new BigDecimal("104.00"), result.getValueBasedPrice());
}
@Test
void rejectsDiscountRateOutsidePercentageRange() {
ProductPricingStepThreeValidationRequest request = validRequest();
request.setDiscountRate(new BigDecimal("101"));
assertThrows(ServiceException.class,
() -> new ProductPricingStepThreeServiceImpl().validate(request, student()));
}
private ProductPricingStepThreeValidationRequest validRequest() {
ProductPricingStepThreeValidationRequest request = new ProductPricingStepThreeValidationRequest();
request.setValueItems(Arrays.asList(item("value-1", "闹钟", "20"), item("value-2", "长续航", "110")));
request.setDiscountRate(new BigDecimal("80"));
return request;
}
private ProductPricingStepThreeValueItem item(String id, String name, String perceivedValue) {
ProductPricingStepThreeValueItem item = new ProductPricingStepThreeValueItem();
item.setId(id);
item.setName(name);
item.setPerceivedValue(new BigDecimal(perceivedValue));
return item;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("student-1");
user.setRoleId(4);
return user;
}
}

@ -0,0 +1,69 @@
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.ProductPricingStepTwoCompetitor;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoDecision;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationRequest;
import com.sztzjy.linkCommerce.entity.dto.ProductPricingStepTwoValidationResult;
import org.junit.jupiter.api.Test;
import java.math.BigDecimal;
import java.util.Arrays;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
class ProductPricingStepTwoServiceImplTest {
@Test
void validatesFourCompetitorsAndCalculatesGrossMargin() {
ProductPricingStepTwoValidationResult result = new ProductPricingStepTwoServiceImpl()
.validate(validRequest(), student());
assertTrue(result.isValid());
assertEquals(new BigDecimal("33.33"), result.getDecision().getGrossMarginRate());
assertEquals("竞品2", result.getDecision().getBenchmarkCompetitor());
}
@Test
void rejectsBenchmarkThatIsNotAmongEnteredCompetitors() {
ProductPricingStepTwoValidationRequest request = validRequest();
request.getDecision().setBenchmarkCompetitor("未填写竞品");
assertThrows(ServiceException.class, () -> new ProductPricingStepTwoServiceImpl().validate(request, student()));
}
private ProductPricingStepTwoValidationRequest validRequest() {
ProductPricingStepTwoValidationRequest request = new ProductPricingStepTwoValidationRequest();
request.setUnitCost(new BigDecimal("100"));
request.setCompetitorRows(Arrays.asList(
competitor("competitor-1", "竞品1"), competitor("competitor-2", "竞品2"),
competitor("competitor-3", "竞品3"), competitor("competitor-4", "竞品4")
));
ProductPricingStepTwoDecision decision = new ProductPricingStepTwoDecision();
decision.setBenchmarkCompetitor("竞品2");
decision.setTargetPrice(new BigDecimal("150"));
decision.setDifferentiator("更长续航");
decision.setPricingReason("处于可切入价格带");
request.setDecision(decision);
return request;
}
private ProductPricingStepTwoCompetitor competitor(String id, String productType) {
ProductPricingStepTwoCompetitor row = new ProductPricingStepTwoCompetitor();
row.setId(id);
row.setProductType(productType);
row.setBrand("品牌" + id.substring(id.length() - 1));
row.setEcommercePrice(new BigDecimal("199"));
row.setDifferentiator("差异化优势");
return row;
}
private JwtUser student() {
JwtUser user = new JwtUser();
user.setUserId("student-1");
user.setRoleId(4);
return user;
}
}
Loading…
Cancel
Save