Merge pull request #167 from LQYBill/feat/Reconstruct_Sku_price

Feat/reconstruct SKU price
pull/8523/head
Qiuyi LI 2025-06-02 15:44:13 +02:00 committed by GitHub
commit 39cdc592e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
16 changed files with 498 additions and 97 deletions

View File

@ -4,9 +4,10 @@ SELECT id AS price_id,
price AS price, price AS price,
threshold AS threshold, threshold AS threshold,
discounted_price AS discounted_price, discounted_price AS discounted_price,
price_rmb AS price_rmb, currency_id AS currency_id,
discounted_price_rmb AS discounted_price_rmb,
date AS date date AS date
FROM sku_price sp FROM sku_price sp
INNER JOIN (SELECT sku_id, MAX(date) max_date FROM sku_price GROUP BY sku_id) sp2 INNER JOIN (SELECT sku_id, MAX(date) max_date FROM sku_price
where DATE(date) <= CURRENT_DATE
GROUP BY sku_id) sp2
ON sp.sku_id = sp2.sku_id AND sp.date = sp2.max_date ON sp.sku_id = sp2.sku_id AND sp.date = sp2.max_date

View File

@ -12,8 +12,7 @@ SELECT s.id AS sku_id,
scp.price AS price, scp.price AS price,
scp.threshold AS threshold, scp.threshold AS threshold,
scp.discounted_price AS discounted_price, scp.discounted_price AS discounted_price,
scp.price_rmb AS price_rmb, scp.currency_id AS currency_id
scp.discounted_price_rmb AS discounted_price_rmb
FROM sku s FROM sku s
LEFT JOIN sku_promotion_relation spr ON s.id = spr.sku_id LEFT JOIN sku_promotion_relation spr ON s.id = spr.sku_id
LEFT JOIN sku_current_price scp ON s.id = scp.sku_id LEFT JOIN sku_current_price scp ON s.id = scp.sku_id

View File

@ -195,16 +195,6 @@ public class MybatisInterceptor implements Interceptor {
eventPublisher.publishEvent(new SkuModifiedEvent(this, id, "UPDATE")); eventPublisher.publishEvent(new SkuModifiedEvent(this, id, "UPDATE"));
} }
} }
if(table.equals("sku_price")) {
if(operationStatus.equals(INSERT_SUCCESS_MSG)) {
String id = extractIdFromRequestParam(requestParam);
eventPublisher.publishEvent(new SkuPriceModifiedEvent(this, id, "INSERT"));
}
if(operationStatus.equals(UPDATE_SUCCESS_MSG)) {
String id = extractIdFromRequestParam(requestParam);
eventPublisher.publishEvent(new SkuPriceModifiedEvent(this, id, "UPDATE"));
}
}
if(table.equals("sku_declared_value")) { if(table.equals("sku_declared_value")) {
if(operationStatus.equals(INSERT_SUCCESS_MSG)) { if(operationStatus.equals(INSERT_SUCCESS_MSG)) {
String id = extractIdFromRequestParam(requestParam); String id = extractIdFromRequestParam(requestParam);

View File

@ -0,0 +1,380 @@
package org.jeecg.modules.business.controller.admin;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.shiro.SecurityUtils;
import org.jeecg.common.api.vo.Result;
import org.jeecg.common.aspect.annotation.AutoLog;
import org.jeecg.common.system.base.controller.JeecgController;
import org.jeecg.common.system.query.QueryGenerator;
import org.jeecg.common.system.vo.LoginUser;
import org.jeecg.modules.business.entity.Sku;
import org.jeecg.modules.business.entity.SkuPrice;
import org.jeecg.modules.business.model.SkuDocument;
import org.jeecg.modules.business.mongoService.SkuMongoService;
import org.jeecg.modules.business.service.ICurrencyService;
import org.jeecg.modules.business.service.ISecurityService;
import org.jeecg.modules.business.service.ISkuPriceService;
import org.jeecg.modules.business.service.ISkuService;
import org.jeecg.modules.business.util.DateUtils;
import org.jeecg.modules.business.vo.ResponsesWithMsg;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.multipart.MultipartHttpServletRequest;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal;
import java.text.SimpleDateFormat;
import java.util.*;
/**
* @Description: sku_price
* @Author: jeecg-boot
* @Date: 2025-05-12
* @Version: V1.0
*/
@Api(tags="sku_price")
@RestController
@RequestMapping("/skuprice")
@Slf4j
public class SkuPriceController extends JeecgController<SkuPrice, ISkuPriceService> {
@Autowired
private ISkuPriceService skuPriceService;
@Autowired
private ISecurityService securityService;
@Autowired
private ISkuService skuService;
@Autowired
private ICurrencyService currencyService;
@Autowired
private SkuMongoService skuMongoService;
@Autowired
private MongoTemplate mongoTemplate;
/**
*
*
* @param skuPrice
* @param pageNo
* @param pageSize
* @param req
* @return
*/
//@AutoLog(value = "SKU售价-分页列表查询")
@ApiOperation(value="SKU售价-分页列表查询", notes="SKU售价-分页列表查询")
@GetMapping(value = "/list")
public Result<IPage<SkuPrice>> queryPageList(SkuPrice skuPrice,
@RequestParam(name="pageNo", defaultValue="1") Integer pageNo,
@RequestParam(name="pageSize", defaultValue="10") Integer pageSize,
HttpServletRequest req) {
QueryWrapper<SkuPrice> queryWrapper = QueryGenerator.initQueryWrapper(skuPrice, req.getParameterMap());
Page<SkuPrice> page = new Page<>(pageNo, pageSize);
IPage<SkuPrice> pageList = skuPriceService.page(page, queryWrapper);
return Result.OK(pageList);
}
/**
*
*
* @param skuPrice
* @return
*/
// @AutoLog(value = "SKU售价-添加")
@ApiOperation(value="SKU售价-添加", notes="SKU售价-添加")
// @RequiresPermissions("skuprice:sku_price:add")
@PostMapping(value = "/add")
public Result<String> add(@RequestBody SkuPrice skuPrice) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
boolean isEmployee = securityService.checkIsEmployee();
if (!isEmployee) {
log.warn("Unauthorized add attempt by user: {}", sysUser.getUsername());
return Result.error(403, "无权限添加 SKU 售价数据");
}
//unique index on sku_id and date
skuPrice.setDate(DateUtils.setToEuropeMorning8(skuPrice.getDate()));
QueryWrapper<SkuPrice> wrapper = new QueryWrapper<>();
wrapper.eq("sku_id", skuPrice.getSkuId());
wrapper.eq("date", skuPrice.getDate());
boolean exists = skuPriceService.count(wrapper) > 0;
if (exists) {
return Result.error("已存在相同 SKU 和日期的记录,不能重复添加。");
}
//duplicate content check(except date)
List<SkuPrice> existingList = skuPriceService.list(new QueryWrapper<SkuPrice>()
.eq("sku_id", skuPrice.getSkuId()));
boolean sameContentExists = existingList.stream().anyMatch(existing ->
existing.getPrice().compareTo(skuPrice.getPrice()) == 0 &&
existing.getDiscountedPrice().compareTo(skuPrice.getDiscountedPrice()) == 0 &&
existing.getThreshold().equals(skuPrice.getThreshold()) &&
existing.getCurrencyId().equals(skuPrice.getCurrencyId())
);
if (sameContentExists) {
return Result.error("相同内容的售价记录已存在,仅日期不同,请勿重复添加。");
}
skuPrice.setCreateBy(sysUser.getUsername());
skuPrice.setCreateTime(new Date());
skuPriceService.save(skuPrice);
skuMongoService.upsertSkuPrice(skuPrice);
return Result.OK("添加成功!");
}
/**
*
*
* @param ids
* @return
*/
@AutoLog(value = "SKU售价-批量删除")
@ApiOperation(value="SKU售价-批量删除", notes="SKU售价-批量删除")
// @RequiresPermissions("skuprice:sku_price:deleteBatch")
@DeleteMapping(value = "/deleteBatch")
public Result<String> deleteBatch(@RequestParam(name="ids",required=true) String ids) {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
boolean isEmployee = securityService.checkIsEmployee();
if (!isEmployee) {
log.warn("Unauthorized import attempt by user: {}", sysUser.getUsername());
return Result.error(403, "无权限执行导入操作");
}
List<String> idList = Arrays.asList(ids.split(","));
List<SkuPrice> pricesToDelete = skuPriceService.listByIds(idList);
// delete from MySQL
skuPriceService.removeByIds(idList);
// check if the deleted prices are the latest ones in MongoDB
for (SkuPrice deleted : pricesToDelete) {
String skuId = deleted.getSkuId();
Date deletedDate = deleted.getDate();
Query query = new Query(Criteria.where("skuId").is(skuId));
SkuDocument doc = mongoTemplate.findOne(query, SkuDocument.class);
boolean isLatestDeleted = doc != null
&& doc.getLatestSkuPrice() != null
&& deletedDate.equals(doc.getLatestSkuPrice().getDate());
if (isLatestDeleted) {
skuMongoService.deleteSkuPriceBySkuId(skuId);
List<SkuPrice> remainingPrices = skuPriceService.list(
new QueryWrapper<SkuPrice>()
.eq("sku_id", skuId)
.orderByDesc("date")
);
// if there are remaining prices, update the latest price in MongoDB
if (!remainingPrices.isEmpty()) {
log.info("Updating latest SKU price for SKU {} to {}", skuId, remainingPrices.get(0));
skuMongoService.upsertSkuPrice(remainingPrices.get(0));
}
}
}
log.info("Deleted SKU prices: {}", pricesToDelete);
return Result.OK("批量删除成功!");
}
/**
* id
*
* @param id
* @return
*/
//@AutoLog(value = "SKU售价-通过id查询")
@ApiOperation(value="SKU售价-通过id查询", notes="SKU售价-通过id查询")
@GetMapping(value = "/queryById")
public Result<SkuPrice> queryById(@RequestParam(name="id",required=true) String id) {
SkuPrice skuPrice = skuPriceService.getById(id);
if(skuPrice==null) {
return Result.error("未找到对应数据");
}
return Result.OK(skuPrice);
}
/**
* excel
*
* @param request
* @param skuPrice
*/
// @RequiresPermissions("skuprice:sku_price:exportXls")
@RequestMapping(value = "/exportXls")
public ModelAndView exportXls(HttpServletRequest request, SkuPrice skuPrice) {
return super.exportXls(request, skuPrice, SkuPrice.class, "SKU售价");
}
/**
* excel
*
* @param request
* @param response
* @return
*/
@RequestMapping(value = "/importExcel", method = RequestMethod.POST)
public Result<?> importExcel(HttpServletRequest request, HttpServletResponse response) throws IOException {
LoginUser sysUser = (LoginUser) SecurityUtils.getSubject().getPrincipal();
boolean isEmployee = securityService.checkIsEmployee();
if (!isEmployee) {
log.warn("Unauthorized import attempt by user: {}", sysUser.getUsername());
return Result.error(403, "无权限执行导入操作");
}
log.info("Importing Sku Prices from Excel...");
MultipartHttpServletRequest multipartRequest = (MultipartHttpServletRequest) request;
Map<String, MultipartFile> fileMap = multipartRequest.getFileMap();
ResponsesWithMsg<String> responses = new ResponsesWithMsg<>();
List<SkuPrice> skuPrices = new ArrayList<>();
//support multiple files
for (Map.Entry<String, MultipartFile> entry : fileMap.entrySet()) {
MultipartFile file = entry.getValue();
try (InputStream inputStream = file.getInputStream()) {
Workbook workbook = file.getOriginalFilename().endsWith(".xlsx")
? new XSSFWorkbook(inputStream)
: new HSSFWorkbook(inputStream);
Sheet sheet = workbook.getSheetAt(0);
int startRow = sheet.getFirstRowNum();
int lastRow = sheet.getLastRowNum();
// find the first row with valid SKU
for (; startRow <= lastRow; startRow++) {
Row testRow = sheet.getRow(startRow);
if (testRow == null) continue;
Cell skuCell = testRow.getCell(0, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
String skuCode = skuCell.getStringCellValue().trim();
if (!skuCode.isEmpty() && skuService.getByErpCode(skuCode) != null) {
break;
}
}
for (int rowIndex = startRow; rowIndex <= lastRow; rowIndex++) {
Row row = sheet.getRow(rowIndex);
if (row == null) continue;
SkuPrice skuPrice = new SkuPrice();
boolean hasError = false;
String erpCode = null;
for (int cellIndex = 0; cellIndex <= 5; cellIndex++) {
Cell cell = row.getCell(cellIndex, Row.MissingCellPolicy.CREATE_NULL_AS_BLANK);
try {
switch (cellIndex) {
case 0: // SKU
erpCode = cell.getStringCellValue().trim();
Sku sku = skuService.getByErpCode(erpCode);
if (sku == null) {
responses.addFailure("Row " + rowIndex + ": Invalid SKU: " + erpCode);
hasError = true;
} else {
skuPrice.setSkuId(sku.getId());
}
break;
case 1: // Price
BigDecimal price;
if (cell.getCellType() == CellType.NUMERIC) {
price = BigDecimal.valueOf(cell.getNumericCellValue());
} else {
price = new BigDecimal(cell.getStringCellValue().trim());
}
skuPrice.setPrice(price);
break;
case 2: // Threshold
int threshold;
if (cell.getCellType() == CellType.NUMERIC) {
threshold = (int) cell.getNumericCellValue();
} else {
threshold = Integer.parseInt(cell.getStringCellValue().trim());
}
skuPrice.setThreshold(threshold);
break;
case 3: // Discounted Price
BigDecimal discountedPrice;
if (cell.getCellType() == CellType.NUMERIC) {
discountedPrice = BigDecimal.valueOf(cell.getNumericCellValue());
} else {
discountedPrice = new BigDecimal(cell.getStringCellValue().trim());
}
skuPrice.setDiscountedPrice(discountedPrice);
break;
case 4: // Date
Date effectiveDate;
if (cell.getCellType() == CellType.NUMERIC) {
effectiveDate = cell.getDateCellValue();
effectiveDate = DateUtils.setToEuropeMorning8(effectiveDate);
log.info("Effective date: {}", effectiveDate);
} else {
effectiveDate = new SimpleDateFormat("yyyy-MM-dd").parse(cell.getStringCellValue().trim());
}
skuPrice.setDate(effectiveDate);
break;
case 5: // Currency
String currencyCode = cell.getStringCellValue().trim();
String currencyId = currencyService.getIdByCode(currencyCode);
if (currencyId == null) {
responses.addFailure("Row " + (rowIndex+1), ": 无效币种代码: " + currencyCode);
hasError = true;
} else {
skuPrice.setCurrencyId(currencyId);
}
break;
}
} catch (Exception ex) {
responses.addFailure("Row " + (rowIndex+1), " Failure at column " + cellIndex + ": " + ex.getMessage());
hasError = true;
break;
}
}
if (!hasError) {
QueryWrapper<SkuPrice> wrapper = new QueryWrapper<>();
wrapper.eq("sku_id", skuPrice.getSkuId());
List<SkuPrice> existingPrices = skuPriceService.list(wrapper);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String newDateStr = sdf.format(skuPrice.getDate());
boolean sameSkuAndDateExists = existingPrices.stream()
.anyMatch(existing -> sdf.format(existing.getDate()).equals(newDateStr));
if (sameSkuAndDateExists) {
responses.addSuccess("Row " + (rowIndex+1), ": 已存在相同 SKU + 日期,跳过导入");
continue;
}
boolean sameContentExists = existingPrices.stream().anyMatch(existing ->
existing.getPrice().compareTo(skuPrice.getPrice()) == 0 &&
existing.getDiscountedPrice().compareTo(skuPrice.getDiscountedPrice()) == 0 &&
existing.getThreshold().equals(skuPrice.getThreshold()) &&
existing.getCurrencyId().equals(skuPrice.getCurrencyId())
);
if (sameContentExists) {
responses.addSuccess("Row " + (rowIndex+1), ": 与已有记录内容相同,仅日期不同,跳过导入");
continue;
}
skuPrices.add(skuPrice);
skuMongoService.upsertSkuPrice(skuPrice);
responses.addSuccess("Row " + (rowIndex+1), ": 导入成功。ERP: " + erpCode);
}
}
if (skuPrices.isEmpty()) {
Result<Object> result = Result.error("导入失败:未找到任何有效数据行。");
result.setResult(responses);
return result;
}
skuPriceService.saveBatch(skuPrices);
} catch (Exception e) {
log.error("导入失败", e);
return Result.error("导入失败:" + e.getMessage());
}
}
return Result.OK(responses);
}
}

View File

@ -48,6 +48,7 @@ import java.net.URISyntaxException;
import java.text.ParseException; import java.text.ParseException;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.time.LocalDate; import java.time.LocalDate;
import java.time.ZoneId;
import java.util.*; import java.util.*;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
@ -103,6 +104,8 @@ public class InvoiceController {
@Autowired @Autowired
private ISkuService skuService; private ISkuService skuService;
@Autowired @Autowired
private ISkuPriceService skuPriceService;
@Autowired
private IExchangeRatesService iExchangeRatesService; private IExchangeRatesService iExchangeRatesService;
@Autowired @Autowired
private IQuartzJobService quartzJobService; private IQuartzJobService quartzJobService;
@ -152,7 +155,7 @@ public class InvoiceController {
Set<String> skuIds = orderContents.stream().map(PlatformOrderContent::getSkuId).collect(Collectors.toSet()); Set<String> skuIds = orderContents.stream().map(PlatformOrderContent::getSkuId).collect(Collectors.toSet());
List<String> skusWithoutPrice = platformOrderContentMap.searchSkuDetail(new ArrayList<>(skuIds)) List<String> skusWithoutPrice = platformOrderContentMap.searchSkuDetail(new ArrayList<>(skuIds))
.stream() .stream()
.filter(skuDetail -> skuDetail.getPrice().getPrice() == null && skuDetail.getPrice().getPriceRmb() == null) .filter(skuDetail -> skuDetail.getPrice() == null || skuDetail.getPrice().getPrice()== null)
.map(SkuDetail::getErpCode) .map(SkuDetail::getErpCode)
.collect(Collectors.toList()); .collect(Collectors.toList());
if (skusWithoutPrice.isEmpty()) { if (skusWithoutPrice.isEmpty()) {
@ -948,14 +951,23 @@ public class InvoiceController {
break; break;
} }
} }
Date today = Date.from(LocalDate.now().atStartOfDay(ZoneId.systemDefault()).toInstant());
for(PlatformOrderContent content : orderContents){ for(PlatformOrderContent content : orderContents){
for (SkuPrice skuPrice : skuPrices) { Optional<SkuPrice> matched = skuPrices.stream()
if(content.getSkuId().equals(skuPrice.getSkuId())) { .filter(p -> content.getSkuId().equals(p.getSkuId()))
BigDecimal price = skuPrice.getPrice(content.getQuantity(), exchangeRateEurToRmb); .filter(p -> p.getDate() != null && !p.getDate().after(today))
price = price.multiply(new BigDecimal(content.getQuantity())); .max(Comparator.comparing(SkuPrice::getDate));
log.info("Can't be after today: {} - {}", content.getSkuId(), matched.isPresent() ? matched.get().getDate() : "null");
if (matched.isPresent()) {
SkuPrice skuPrice = matched.get();
BigDecimal price = skuPriceService.getPrice(skuPrice, content.getQuantity(), exchangeRateEurToRmb);
price = price.multiply(BigDecimal.valueOf(content.getQuantity()));
purchaseEstimation = purchaseEstimation.add(price); purchaseEstimation = purchaseEstimation.add(price);
break; log.info("Matched SKU: {}, Qty: {}, Final Price: {}", content.getSkuId(), content.getQuantity(), price);
} } else {
isCompleteInvoiceReady = false;
errorMessages.add("No valid price for SKU: " + content.getSkuId());
log.warn("No valid price for SKU: {}", content.getSkuId());
} }
} }
} }

View File

@ -38,6 +38,8 @@ public class TransactionController {
@Autowired @Autowired
private IShopService shopService; private IShopService shopService;
@Autowired @Autowired
private ISkuPriceService skuPriceService;
@Autowired
PlatformOrderContentMapper platformOrderContentMapper; PlatformOrderContentMapper platformOrderContentMapper;
@Autowired @Autowired
ExchangeRatesMapper exchangeRatesMapper; ExchangeRatesMapper exchangeRatesMapper;
@ -118,7 +120,7 @@ public class TransactionController {
for (PlatformOrderContent content : orderContents) { for (PlatformOrderContent content : orderContents) {
for (SkuPrice skuPrice : skuPrices) { for (SkuPrice skuPrice : skuPrices) {
if (content.getSkuId().equals(skuPrice.getSkuId())) { if (content.getSkuId().equals(skuPrice.getSkuId())) {
purchaseEstimation = purchaseEstimation.add(skuPrice.getPrice(content.getQuantity(), exchangeRateEurToRmb)); purchaseEstimation = purchaseEstimation.add(skuPriceService.getPrice(skuPrice, content.getQuantity(), exchangeRateEurToRmb));
} }
} }
} }

View File

@ -2,6 +2,7 @@ package org.jeecg.modules.business.entity;
import lombok.Data; import lombok.Data;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.jeecg.modules.business.service.ISkuPriceService;
import org.jeecg.modules.business.vo.SkuDetail; import org.jeecg.modules.business.vo.SkuDetail;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -21,6 +22,8 @@ public class OrderContentDetail {
private final BigDecimal exchangeRate; private final BigDecimal exchangeRate;
private final ISkuPriceService skuPriceService;
/** /**
* Calculate the reduced amount by applying the promotion to the sku. * Calculate the reduced amount by applying the promotion to the sku.
* *
@ -37,7 +40,7 @@ public class OrderContentDetail {
* @return the total price (price * quantity) * @return the total price (price * quantity)
*/ */
public BigDecimal totalPrice() { public BigDecimal totalPrice() {
BigDecimal unit = skuDetail.getPrice().getPrice(quantity, exchangeRate); BigDecimal unit = skuPriceService.getPrice(skuDetail.getPrice(), quantity, exchangeRate);
BigDecimal total = unit.multiply(new BigDecimal(quantity)); BigDecimal total = unit.multiply(new BigDecimal(quantity));
log.info("unit: {}", unit); log.info("unit: {}", unit);
log.info("total: {}", total); log.info("total: {}", total);
@ -45,7 +48,7 @@ public class OrderContentDetail {
} }
public BigDecimal unitPrice(){ public BigDecimal unitPrice(){
return skuDetail.getPrice().getPrice(quantity, exchangeRate); return skuPriceService.getPrice(skuDetail.getPrice(), quantity, exchangeRate);
} }
public int promotionCount() { public int promotionCount() {

View File

@ -63,9 +63,10 @@ public class SkuPrice implements Serializable {
/** /**
* SKU ID * SKU ID
*/ */
@Excel(name = "SKU ID", width = 15, dictTable = "sku", dicText = "erp_code", dicCode = "id")
@Dict(dictTable = "sku", dicText = "erp_code", dicCode = "id") @Dict(dictTable = "sku", dicText = "erp_code", dicCode = "id")
@ApiModelProperty(value = "SKU ID") @ApiModelProperty(value = "SKU ID")
private String skuId; private java.lang.String skuId;
/** /**
* *
*/ */
@ -93,40 +94,13 @@ public class SkuPrice implements Serializable {
@DateTimeFormat(pattern = "yyyy-MM-dd") @DateTimeFormat(pattern = "yyyy-MM-dd")
@ApiModelProperty(value = "生效日期") @ApiModelProperty(value = "生效日期")
private Date date; private Date date;
/** /**币种ID*/
* @Excel(name = "币种ID", width = 15, dictTable = "currency", dicText = "code", dicCode = "id")
*/ @Dict(dictTable = "currency", dicText = "code", dicCode = "id")
@Excel(name = "人民币价格", width = 15) @ApiModelProperty(value = "币种ID")
@ApiModelProperty(value = "人民币价格") private java.lang.String currencyId;
private java.math.BigDecimal priceRmb;
/**
*
*/
@Excel(name = "人民币优惠价", width = 15)
@ApiModelProperty(value = "人民币优惠价")
private java.math.BigDecimal discountedPriceRmb;
/**
* The price of a sku depends on its quantity, Given a quantity here, return the correspondent price.
*
* @param quantity a quantity
* @param eurToRmb Exchange rate from EUR to RMB
* @return the price correspondent to the quantity
*/
public BigDecimal getPrice(int quantity, BigDecimal eurToRmb) {
BigDecimal priceCandidate = price;
BigDecimal discountedPriceCandidate = discountedPrice == null ? price : discountedPrice;
if (priceRmb != null) {
priceCandidate = priceRmb.divide(eurToRmb, RoundingMode.HALF_UP);
discountedPriceCandidate = discountedPriceRmb == null ? priceCandidate : discountedPriceRmb.divide(eurToRmb, RoundingMode.HALF_UP);
}
if (threshold != null && quantity >= threshold) {
return discountedPriceCandidate;
}
return priceCandidate;
}
public String toString() { public String toString() {
return String.format("%s, %s[%d], %s(RMB), %s[%d](RMB)", price, discountedPrice, threshold, priceRmb, discountedPriceRmb, threshold); return String.format("%s, %s[%d]", price, discountedPrice, threshold);
} }
} }

View File

@ -15,7 +15,7 @@
WHERE sku_id = #{mainId} WHERE sku_id = #{mainId}
</select> </select>
<select id="getLatestBySkuId" resultType="org.jeecg.modules.business.entity.SkuPrice"> <select id="getLatestBySkuId" resultType="org.jeecg.modules.business.entity.SkuPrice">
SELECT date, price, threshold, discounted_price, price_rmb, discounted_price_rmb SELECT date, price, threshold, discounted_price, currency_id
FROM sku_price FROM sku_price
WHERE sku_id = #{skuId} WHERE sku_id = #{skuId}
ORDER BY date DESC ORDER BY date DESC

View File

@ -97,8 +97,9 @@ public class SkuDocument {
* effective date * effective date
*/ */
private Date date; private Date date;
private BigDecimal priceRmb;
private BigDecimal discountedPriceRmb; private String currencyCode;
} }
@Data @Data
@NoArgsConstructor @NoArgsConstructor

View File

@ -96,33 +96,6 @@ public class SkuMongoSyncService {
} }
} }
/**
* Listens to an event fired by MyBatisInterceptor in jeecg-boot-base-core
* /!\ the id in event SHOULD be a sku_price ID.
* The condition being if no mapper method uses other criteria to delete a sku_price
* @param event contains the sku_price ID and the operation type (INSERT, UPDATE, DELETE)
*/
@EventListener
public void handleSkuPriceModifiedEvent(SkuPriceModifiedEvent event) {
log.info("Received a SkuPriceModifiedEvent: {}", event);
String id = event.getId();
String operation = event.getOperation();
SkuPrice skuPrice = skuPriceService.getById(id);
switch (operation) {
case "INSERT":
case "UPDATE":
skuMongoService.upsertSkuPrice(skuPrice);
break;
case "DELETE":
skuMongoService.deleteSkuPriceBySkuId(skuPrice.getSkuId());
break;
default:
break;
}
}
@EventListener @EventListener
public void handeSkuWeightModifiedEvent(SkuWeightModifiedEvent event) { public void handeSkuWeightModifiedEvent(SkuWeightModifiedEvent event) {
log.info("Received a SkuWeightModifiedEvent: {}", event); log.info("Received a SkuWeightModifiedEvent: {}", event);

View File

@ -36,6 +36,8 @@ public class SkuMongoServiceImpl implements SkuMongoService {
@Autowired @Autowired
private ISkuPriceService skuPriceService; private ISkuPriceService skuPriceService;
@Autowired @Autowired
private ICurrencyService currencyService;
@Autowired
private ISensitiveAttributeService sensitiveAttributeService; private ISensitiveAttributeService sensitiveAttributeService;
@Override @Override
@ -72,13 +74,14 @@ public class SkuMongoServiceImpl implements SkuMongoService {
log.error("Cannot insert or update SkuPrice of Sku Mongo Document, SkuDocument with skuId was {} not found.", price.getSkuId()); log.error("Cannot insert or update SkuPrice of Sku Mongo Document, SkuDocument with skuId was {} not found.", price.getSkuId());
throw new RuntimeException("SkuDocument with skuId: " + price.getSkuId() + " not found"); throw new RuntimeException("SkuDocument with skuId: " + price.getSkuId() + " not found");
} }
Currency currency = currencyService.getById(price.getCurrencyId());
String currencyCode = currency != null ? currency.getCode() : "UNKNOWN";
SkuDocument.LatestSkuPrice skuPrice = SkuDocument.LatestSkuPrice.builder() SkuDocument.LatestSkuPrice skuPrice = SkuDocument.LatestSkuPrice.builder()
.date(price.getDate()) .date(price.getDate())
.price(price.getPrice()) .price(price.getPrice())
.discountedPrice(price.getDiscountedPrice()) .discountedPrice(price.getDiscountedPrice())
.threshold(price.getThreshold()) .threshold(price.getThreshold())
.priceRmb(price.getPriceRmb()) .currencyCode(currencyCode)
.discountedPriceRmb(price.getDiscountedPriceRmb())
.build(); .build();
mongoTemplate.update(SkuDocument.class) mongoTemplate.update(SkuDocument.class)
@ -376,13 +379,14 @@ public class SkuMongoServiceImpl implements SkuMongoService {
); );
} }
if(latestPrice != null) { if(latestPrice != null) {
Currency currency = currencyService.getById(latestPrice.getCurrencyId());
String currencyCode = currency != null ? currency.getCode() : "UNKNOWN";
skuDocument.setLatestSkuPrice(SkuDocument.LatestSkuPrice.builder() skuDocument.setLatestSkuPrice(SkuDocument.LatestSkuPrice.builder()
.date(latestPrice.getDate()) .date(latestPrice.getDate())
.price(latestPrice.getPrice()) .price(latestPrice.getPrice())
.discountedPrice(latestPrice.getDiscountedPrice()) .discountedPrice(latestPrice.getDiscountedPrice())
.threshold(latestPrice.getThreshold()) .threshold(latestPrice.getThreshold())
.priceRmb(latestPrice.getPriceRmb()) .currencyCode(currencyCode)
.discountedPriceRmb(latestPrice.getDiscountedPriceRmb())
.build() .build()
); );
} }

View File

@ -2,6 +2,7 @@ package org.jeecg.modules.business.service;
import org.jeecg.modules.business.entity.SkuPrice; import org.jeecg.modules.business.entity.SkuPrice;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import java.math.BigDecimal;
import java.util.List; import java.util.List;
/** /**
@ -15,4 +16,6 @@ public interface ISkuPriceService extends IService<SkuPrice> {
List<SkuPrice> selectByMainId(String mainId); List<SkuPrice> selectByMainId(String mainId);
SkuPrice getLatestBySkuId(String sku_id); SkuPrice getLatestBySkuId(String sku_id);
BigDecimal getPrice(SkuPrice skuPrice, int quantity, BigDecimal eurToRmb);
} }

View File

@ -15,6 +15,7 @@ import org.jeecg.modules.business.mapper.PlatformOrderMapper;
import org.jeecg.modules.business.service.IClientService; import org.jeecg.modules.business.service.IClientService;
import org.jeecg.modules.business.service.IPlatformOrderService; import org.jeecg.modules.business.service.IPlatformOrderService;
import org.jeecg.modules.business.service.IShippingFeesWaiverProductService; import org.jeecg.modules.business.service.IShippingFeesWaiverProductService;
import org.jeecg.modules.business.service.ISkuPriceService;
import org.jeecg.modules.business.vo.*; import org.jeecg.modules.business.vo.*;
import org.jeecg.modules.business.vo.clientPlatformOrder.ClientPlatformOrderPage; import org.jeecg.modules.business.vo.clientPlatformOrder.ClientPlatformOrderPage;
import org.jeecg.modules.business.vo.clientPlatformOrder.PurchaseConfirmation; import org.jeecg.modules.business.vo.clientPlatformOrder.PurchaseConfirmation;
@ -58,6 +59,8 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
private IClientService clientService; private IClientService clientService;
@Autowired @Autowired
private ExchangeRatesMapper exchangeRatesMapper; private ExchangeRatesMapper exchangeRatesMapper;
@Autowired
private ISkuPriceService skuPriceService;
@Override @Override
@Transactional @Transactional
@ -246,7 +249,8 @@ public class PlatformOrderServiceImpl extends ServiceImpl<PlatformOrderMapper, P
skuDetail -> new OrderContentDetail( skuDetail -> new OrderContentDetail(
skuDetail, skuDetail,
skuQuantity.get(skuDetail.getSkuId()), skuQuantity.get(skuDetail.getSkuId()),
eurToRmb eurToRmb,
skuPriceService
) )
) )
.collect(toList()); .collect(toList());

View File

@ -2,8 +2,12 @@ package org.jeecg.modules.business.service.impl;
import org.jeecg.modules.business.entity.SkuPrice; import org.jeecg.modules.business.entity.SkuPrice;
import org.jeecg.modules.business.mapper.SkuPriceMapper; import org.jeecg.modules.business.mapper.SkuPriceMapper;
import org.jeecg.modules.business.service.ICurrencyService;
import org.jeecg.modules.business.service.ISkuPriceService; import org.jeecg.modules.business.service.ISkuPriceService;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.List; import java.util.List;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Autowired;
@ -20,6 +24,9 @@ public class SkuPriceServiceImpl extends ServiceImpl<SkuPriceMapper, SkuPrice> i
@Autowired @Autowired
private SkuPriceMapper skuPriceMapper; private SkuPriceMapper skuPriceMapper;
@Autowired
private ICurrencyService currencyService;
@Override @Override
public List<SkuPrice> selectByMainId(String mainId) { public List<SkuPrice> selectByMainId(String mainId) {
return skuPriceMapper.selectByMainId(mainId); return skuPriceMapper.selectByMainId(mainId);
@ -29,4 +36,32 @@ public class SkuPriceServiceImpl extends ServiceImpl<SkuPriceMapper, SkuPrice> i
public SkuPrice getLatestBySkuId(String sku_id) { public SkuPrice getLatestBySkuId(String sku_id) {
return skuPriceMapper.getLatestBySkuId(sku_id); return skuPriceMapper.getLatestBySkuId(sku_id);
} }
/**
* The price of a sku depends on its quantity, Given a quantity here, return the correspondent price.
* @param skuPrice SKU price object
* @param quantity Quantity of the SKU
* @param eurToRmb Exchange rate from EUR to RMB
* @return The price based on the quantity
*/
@Override
public BigDecimal getPrice(SkuPrice skuPrice, int quantity, BigDecimal eurToRmb) {
String RMB_CURRENCY_CODE = "RMB";
String rmbId = currencyService.getIdByCode(RMB_CURRENCY_CODE);
BigDecimal priceCandidate = skuPrice.getPrice();
BigDecimal discountPriceCandidate = skuPrice.getDiscountedPrice() == null ? priceCandidate : skuPrice.getDiscountedPrice();
// Convert the price to EURO if the currency is RMB
boolean isRmb = rmbId.equals(skuPrice.getCurrencyId());
if (isRmb) {
priceCandidate = priceCandidate.divide(eurToRmb, 2, RoundingMode.HALF_UP);
discountPriceCandidate = discountPriceCandidate.divide(eurToRmb, 2, RoundingMode.HALF_UP);
}
// Get the price based on the quantity
if (skuPrice.getThreshold() != null && quantity >= skuPrice.getThreshold()) {
return discountPriceCandidate;
} else {
return priceCandidate;
}
}
} }

View File

@ -0,0 +1,20 @@
package org.jeecg.modules.business.util;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;
public class DateUtils {
public static Date setToEuropeMorning8(Date original) {
if (original == null) return null;
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("Europe/Paris"));
cal.setTime(original);
cal.set(Calendar.HOUR_OF_DAY, 8);
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime();
}
}