Merge remote-tracking branch 'origin/feature/mall_product' into brokerate

# Conflicts:
#	yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/common/PromotionTypeEnum.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderItemDO.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
#	yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
#	yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/user/dto/MemberUserRespDTO.java
This commit is contained in:
owen
2023-09-24 10:10:53 +08:00
73 changed files with 910 additions and 422 deletions

View File

@@ -8,11 +8,11 @@ import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.pay.api.notify.dto.PayRefundNotifyReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.*;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService;
import cn.iocoder.yudao.module.trade.service.aftersale.TradeAfterSaleService;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
@@ -87,13 +87,13 @@ public class TradeAfterSaleController {
// 拼接数据
MemberUserRespDTO user = memberUserApi.getUser(afterSale.getUserId());
// 获取售后日志
List<TradeAfterSaleLogRespDTO> logs = afterSaleLogService.getLog(afterSale.getId());
List<TradeAfterSaleLogRespVO> logs = afterSaleLogService.getLog(afterSale.getId());
// TODO 方便测试看效果review 后移除
if (logs == null) {
logs = new ArrayList<>();
}
for (int i = 1; i <= 6; i++) {
TradeAfterSaleLogRespDTO respVO = new TradeAfterSaleLogRespDTO();
TradeAfterSaleLogRespVO respVO = new TradeAfterSaleLogRespVO();
respVO.setId((long) i);
respVO.setUserId((long) i);
respVO.setUserType(i % 2 == 0 ? 2 : 1);

View File

@@ -156,7 +156,7 @@ public class AppTradeOrderController {
@Operation(summary = "删除交易订单")
@Parameter(name = "id", description = "交易订单编号")
public CommonResult<Boolean> deleteOrder(@RequestParam("id") Long id) {
// TODO @芋艿未实现mock 用
tradeOrderUpdateService.deleteOrder(getLoginUserId(), id);
return success(true);
}

View File

@@ -82,6 +82,9 @@ public class AppTradeOrderSettlementRespVO {
@Schema(description = "积分抵扣的金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "50")
private Integer pointPrice;
@Schema(description = "VIP 减免金额,单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "30")
private Integer vipPrice;
@Schema(description = "实际支付金额(总),单位:分", requiredMode = Schema.RequiredMode.REQUIRED, example = "450")
private Integer payPrice;

View File

@@ -16,7 +16,6 @@ import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.aftersale.TradeAfterSaleLogDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
@@ -68,10 +67,10 @@ public interface TradeAfterSaleConvert {
PageResult<AppTradeAfterSaleRespVO> convertPage02(PageResult<TradeAfterSaleDO> page);
List<TradeAfterSaleLogRespDTO> convertList(List<TradeAfterSaleLogDO> list);
List<TradeAfterSaleLogRespVO> convertList(List<TradeAfterSaleLogDO> list);
default TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, TradeOrderDO order, List<TradeOrderItemDO> orderItems,
MemberUserRespDTO user, List<TradeAfterSaleLogRespDTO> logs) {
MemberUserRespDTO user, List<TradeAfterSaleLogRespVO> logs) {
TradeAfterSaleDetailRespVO respVO = convert(afterSale, orderItems);
// 处理用户信息
respVO.setUser(convert(user));
@@ -81,7 +80,8 @@ public interface TradeAfterSaleConvert {
respVO.setLogs(convertList1(logs));
return respVO;
}
List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespDTO> list);
List<TradeAfterSaleLogRespVO> convertList1(List<TradeAfterSaleLogRespVO> list);
@Mapping(target = "id", source = "afterSale.id")
TradeAfterSaleDetailRespVO convert(TradeAfterSaleDO afterSale, List<TradeOrderItemDO> orderItems);
TradeOrderBaseVO convert(TradeOrderDO order);

View File

@@ -65,6 +65,7 @@ public interface TradeOrderConvert {
@Mapping(source = "calculateRespBO.price.deliveryPrice", target = "deliveryPrice"),
@Mapping(source = "calculateRespBO.price.couponPrice", target = "couponPrice"),
@Mapping(source = "calculateRespBO.price.pointPrice", target = "pointPrice"),
@Mapping(source = "calculateRespBO.price.vipPrice", target = "vipPrice"),
@Mapping(source = "calculateRespBO.price.payPrice", target = "payPrice")
})
TradeOrderDO convert(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO,

View File

@@ -168,6 +168,7 @@ public class TradeOrderDO extends BaseDO {
* - {@link #discountPrice}
* + {@link #deliveryPrice}
* + {@link #adjustPrice}
* - {@link #vipPrice}
*/
private Integer payPrice;
@@ -273,5 +274,9 @@ public class TradeOrderDO extends BaseDO {
* 退还的使用的积分
*/
private Integer refundPoint;
/**
* VIP 减免金额,单位:分
*/
private Integer vipPrice;
}

View File

@@ -126,6 +126,7 @@ public class TradeOrderItemDO extends BaseDO {
* - {@link #discountPrice}
* + {@link #deliveryPrice}
* + {@link #adjustPrice}
* - {@link #vipPrice}
*/
private Integer payPrice;
@@ -151,6 +152,10 @@ public class TradeOrderItemDO extends BaseDO {
* 赠送的积分
*/
private Integer givePoint;
/**
* VIP 减免金额,单位:分
*/
private Integer vipPrice;
// TODO @芋艿:如果商品 vip 折扣时,到底是新增一个 vipPrice 记录优惠记录,还是 vipDiscountPrice记录 vip 的优惠;还是直接使用 vipPrice
// 目前 crmeb 的选择,单独一个 vipPrice 记录优惠价格;感觉不一定合理,可以在看看有赞的;

View File

@@ -1,59 +0,0 @@
package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.validation.constraints.NotNull;
import java.time.LocalDateTime;
// TODO @puhui999这个是不是应该搞成 vo 啊?
/**
* 贸易售后日志详情 DTO
*
* @author HUIHUI
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TradeAfterSaleLogRespDTO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "20669")
private Long id;
@Schema(description = "用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "22634")
@NotNull(message = "用户编号不能为空")
private Long userId;
@Schema(description = "用户类型", requiredMode = Schema.RequiredMode.REQUIRED, example = "2")
@NotNull(message = "用户类型不能为空")
private Integer userType;
@Schema(description = "售后编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3023")
@NotNull(message = "售后编号不能为空")
private Long afterSaleId;
@Schema(description = "订单编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "25870")
@NotNull(message = "订单编号不能为空")
private Long orderId;
@Schema(description = "订单项编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "23154")
@NotNull(message = "订单项编号不能为空")
private Long orderItemId;
@Schema(description = "售后状态(之前)", example = "2")
private Integer beforeStatus;
@Schema(description = "售后状态(之后)", requiredMode = Schema.RequiredMode.REQUIRED, example = "1")
@NotNull(message = "售后状态(之后)不能为空")
private Integer afterStatus;
@Schema(description = "操作明细", requiredMode = Schema.RequiredMode.REQUIRED, example = "维权完成退款金额¥37776.00")
@NotNull(message = "操作明细不能为空")
private String content;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
private LocalDateTime createTime;
}

View File

@@ -1,8 +1,8 @@
package cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO;
import java.util.List;
@@ -29,6 +29,6 @@ public interface AfterSaleLogService {
* @param afterSaleId 售后编号
* @return 售后日志
*/
List<TradeAfterSaleLogRespDTO> getLog(Long afterSaleId);
List<TradeAfterSaleLogRespVO> getLog(Long afterSaleId);
}

View File

@@ -63,6 +63,9 @@ public class TradeOrderLogAspect {
Long userId = getUserId();
// 1.2 订单信息
Long orderId = ORDER_ID.get();
if (orderId == null) { // 如果未设置,只有注解,说明不需要记录订单日志
return;
}
Integer beforeStatus = BEFORE_STATUS.get();
Integer afterStatus = AFTER_STATUS.get();
Map<String, Object> exts = ObjectUtil.defaultIfNull(EXTS.get(), emptyMap());

View File

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.pay.api.refund.dto.PayRefundCreateReqDTO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleDisagreeReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSalePageReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.TradeAfterSaleRefuseReqVO;
import cn.iocoder.yudao.module.trade.controller.admin.aftersale.vo.log.TradeAfterSaleLogRespVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleCreateReqVO;
import cn.iocoder.yudao.module.trade.controller.app.aftersale.vo.AppTradeAfterSaleDeliveryReqVO;
import cn.iocoder.yudao.module.trade.convert.aftersale.TradeAfterSaleConvert;
@@ -26,7 +27,6 @@ import cn.iocoder.yudao.module.trade.enums.aftersale.TradeAfterSaleWayEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderItemAfterSaleStatusEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogCreateReqDTO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.dto.TradeAfterSaleLogRespDTO;
import cn.iocoder.yudao.module.trade.framework.aftersalelog.core.service.AfterSaleLogService;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
@@ -449,8 +449,9 @@ public class TradeAfterSaleServiceImpl implements TradeAfterSaleService, AfterSa
}
}
// TODO @puhui999应该返回 do 哈。
@Override
public List<TradeAfterSaleLogRespDTO> getLog(Long afterSaleId) {
public List<TradeAfterSaleLogRespVO> getLog(Long afterSaleId) {
// TODO 不熟悉流程先这么滴
List<TradeAfterSaleLogDO> saleLogDOs = tradeAfterSaleLogMapper.selectList(TradeAfterSaleLogDO::getAfterSaleId, afterSaleId);
return TradeAfterSaleConvert.INSTANCE.convertList(saleLogDOs);

View File

@@ -106,7 +106,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 查询物流
return getExpressTrackList(order);
}
@@ -117,7 +117,7 @@ public class TradeOrderQueryServiceImpl implements TradeOrderQueryService {
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 查询物流
return getExpressTrackList(order);
}

View File

@@ -62,6 +62,22 @@ public interface TradeOrderUpdateService {
*/
void receiveOrder(Long userId, Long id);
/**
* 【会员】取消订单
*
* @param userId 用户编号
* @param id 订单编号
*/
void cancelOrder(Long userId, Long id);
/**
* 【会员】删除订单
*
* @param userId 用户编号
* @param id 订单编号
*/
void deleteOrder(Long userId, Long id);
/**
* 【管理员】交易订单备注
*
@@ -117,11 +133,4 @@ public interface TradeOrderUpdateService {
*/
Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO);
/**
* 【会员】取消订单
*
* @param userId 用户ID
* @param id 订单编号
*/
void cancelOrder(Long userId, Long id);
}

View File

@@ -49,6 +49,7 @@ import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.*;
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;
import cn.iocoder.yudao.module.trade.service.brokerage.BrokerageRecordService;
import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
import cn.iocoder.yudao.module.trade.service.cart.CartService;
@@ -299,6 +300,9 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// 5. 生成预支付
createPayOrder(order, orderItems, calculateRespBO);
// 6. 插入订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), null, order.getStatus());
// TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
}
@@ -482,6 +486,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_RECEIVE)
public void receiveOrder(Long userId, Long id) {
// 校验并获得交易订单(可收货)
TradeOrderDO order = validateOrderReceivable(userId, id);
@@ -492,7 +497,6 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
if (updateCount == 0) {
throw exception(ORDER_RECEIVE_FAIL_STATUS_NOT_DELIVERED);
}
// TODO 芋艿OrderLog
// TODO 芋艿lili 发送订单变化的消息
@@ -500,7 +504,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
// TODO 芋艿:销售佣金的记录;
// TODO 芋艿:获得积分;
// 插入订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.COMPLETED.getStatus());
}
@Override
@@ -541,41 +546,49 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
tradeOrderMapper.updateById(update);
// TODO @芋艿:改价时,赠送的积分,要不要做改动???
// TODO @puhui999应该是按照 payPrice 分配并且要考虑取余问题payPrice 也要考虑item 里的
// TODO先按 adjustPrice 实现,没明白 payPrice 怎么搞哈哈哈
// TODO @puhui999就是对比新老 adjustPrice 的差值,然后计算补充的 adjustPrice 最终值;另外,可以不用区分 items.size 是不是 > 1 哈;应该是一致的逻辑;分摊的逻辑,有点类似 dividePrice 方法噢;
// 5、更新 TradeOrderItem
if (items.size() > 1) {
// TradeOrderItemDO 需要做 adjustPrice 的分摊
int price = reqVO.getAdjustPrice() / items.size();
int remainderPrice = reqVO.getAdjustPrice() % items.size();
List<TradeOrderItemDO> orders = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
// 把平摊后剩余的金额加到第一个订单项
if (remainderPrice != 0 && i == 0) {
orders.add(convertOrderItemPrice(items.get(i), price + remainderPrice));
}
orders.add(convertOrderItemPrice(items.get(i), price));
}
tradeOrderItemMapper.updateBatch(orders);
} else {
TradeOrderItemDO orderItem = items.get(0);
TradeOrderItemDO updateItem = convertOrderItemPrice(orderItem, reqVO.getAdjustPrice());
tradeOrderItemMapper.updateById(updateItem);
// TradeOrderItemDO 需要做 adjustPrice 的分摊
List<Integer> dividePrices = dividePrice(items, orderPayPrice);
List<TradeOrderItemDO> updateItems = new ArrayList<>();
for (int i = 0; i < items.size(); i++) {
TradeOrderItemDO item = items.get(i);
Integer adjustPrice = item.getPrice() - dividePrices.get(i); // 计算调整的金额
updateItems.add(new TradeOrderItemDO().setId(item.getId()).setAdjustPrice(adjustPrice)
.setPayPrice(item.getPayPrice() - adjustPrice));
}
tradeOrderItemMapper.updateBatch(updateItems);
// 6、更新支付订单
payOrderApi.updatePayOrderPrice(order.getPayOrderId(), update.getPayPrice());
}
private TradeOrderItemDO convertOrderItemPrice(TradeOrderItemDO orderItem, Integer price) {
TradeOrderItemDO newOrderItem = new TradeOrderItemDO();
newOrderItem.setId(orderItem.getId());
newOrderItem.setAdjustPrice(price);
int payPrice = orderItem.getAdjustPrice() != null ? (orderItem.getPayPrice() - orderItem.getAdjustPrice())
+ price : orderItem.getPayPrice() + price;
newOrderItem.setPayPrice(payPrice);
return newOrderItem;
/**
* 计算订单调价价格分摊
*
* @param items 订单项
* @param orderPayPrice 订单支付金额
* @return 分摊金额数组,和传入的 orderItems 一一对应
*/
private List<Integer> dividePrice(List<TradeOrderItemDO> items, Integer orderPayPrice) {
Integer total = getSumValue(items, TradeOrderItemDO::getPrice, Integer::sum);
assert total != null;
// 遍历每一个,进行分摊
List<Integer> prices = new ArrayList<>(items.size());
int remainPrice = orderPayPrice;
for (int i = 0; i < items.size(); i++) {
TradeOrderItemDO orderItem = items.get(i);
int partPrice;
if (i < items.size() - 1) { // 减一的原因,是因为拆分时,如果按照比例,可能会出现.所以最后一个,使用反减
partPrice = (int) (orderPayPrice * (1.0D * orderItem.getPayPrice() / total));
remainPrice -= partPrice;
} else {
partPrice = remainPrice;
}
Assert.isTrue(partPrice >= 0, "分摊金额必须大于等于 0");
prices.add(partPrice);
}
return prices;
}
@Override
@@ -671,6 +684,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
@Override
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_COMMENT)
public Long createOrderItemComment(Long userId, AppTradeOrderItemCommentCreateReqVO createReqVO) {
// 先通过订单项 ID查询订单项是否存在
TradeOrderItemDO orderItem = tradeOrderItemMapper.selectByIdAndUserId(createReqVO.getOrderItemId(), userId);
@@ -698,24 +712,27 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(order.getId());
if (!anyMatch(orderItems, item -> Objects.equals(item.getCommentStatus(), Boolean.FALSE))) {
tradeOrderMapper.updateById(new TradeOrderDO().setId(order.getId()).setCommentStatus(Boolean.TRUE));
// TODO 待实现:已完成评价,要不要写一条订单日志?目前 crmeb 会写,有赞可以研究下
// 增加订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
}
return comment;
}
@Override
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CANCEL)
public void cancelOrder(Long userId, Long id) {
// 校验存在
// 1.1 校验存在
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 校验状态
// 1.2 校验状态
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.UNPAID.getStatus())) {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
// 1.更新 TradeOrderDO 状态为已取消
// 2. 更新 TradeOrderDO 状态为已取消
int updateCount = tradeOrderMapper.updateByIdAndStatus(id, order.getStatus(),
new TradeOrderDO().setStatus(TradeOrderStatusEnum.CANCELED.getStatus())
.setCancelTime(LocalDateTime.now())
@@ -724,22 +741,43 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
throw exception(ORDER_CANCEL_FAIL_STATUS_NOT_UNPAID);
}
// TODO 活动相关库存回滚需要活动 id活动 id 怎么获取app 端能否传过来
// 3. TODO 活动相关库存回滚需要活动 id活动 id 怎么获取app 端能否传过来;回复:从订单里拿呀
tradeOrderHandlers.forEach(handler -> handler.rollback());
// 2.回滚库存
// 4. 回滚库存
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(id);
productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convert(orderItems));
// 3.回滚优惠券
couponApi.returnUsedCoupon(order.getCouponId());
// 5. 回滚优惠券
if (order.getCouponId() > 0) {
couponApi.returnUsedCoupon(order.getCouponId());
}
// 4.回滚积分(抵扣的)
// 6. 回滚积分(抵扣的)
addUserPoint(order.getUserId(), order.getUsePoint(), MemberPointBizTypeEnum.ORDER_CANCEL, order.getId());
// TODO 芋艿OrderLog
// 7. 增加订单日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus());
}
// TODO 芋艿lili 发送订单变化的消息
@Override
@Transactional(rollbackFor = Exception.class)
@TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_DELETE)
public void deleteOrder(Long userId, Long id) {
// 1.1 校验存在
TradeOrderDO order = tradeOrderMapper.selectOrderByIdAndUserId(id, userId);
if (order == null) {
throw exception(ORDER_NOT_FOUND);
}
// 1.2 校验状态
if (ObjectUtil.notEqual(order.getStatus(), TradeOrderStatusEnum.CANCELED.getStatus())) {
throw exception(ORDER_DELETE_FAIL_STATUS_NOT_CANCEL);
}
// 2. 删除订单
tradeOrderMapper.deleteById(id);
// 3. 记录日志
TradeOrderLogUtils.setOrderInfo(order.getId(), order.getStatus(), order.getStatus());
}
/**
@@ -766,6 +804,20 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
memberLevelApi.addExperience(userId, -refundPrice, bizType, String.valueOf(afterSaleId));
}
/**
* 添加用户积分
* <p>
* 目前是支付成功后,就会创建积分记录。
* <p>
* 业内还有两种做法,可以根据自己的业务调整:
* 1. 确认收货后,才创建积分记录
* 2. 支付 or 下单成功时,创建积分记录(冻结),确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param point 增加积分数量
* @param bizType 业务编号
* @param bizId 业务编号
*/
protected void addUserPoint(Long userId, Integer point, MemberPointBizTypeEnum bizType, Long bizId) {
if (point != null && point > 0) {
memberPointApi.addPoint(userId, point, bizType.getType(), String.valueOf(bizId));
@@ -778,15 +830,27 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
}
}
/**
* 创建分销记录
*
* 目前是支付成功后,就会创建分销记录。
*
* 业内还有两种做法,可以根据自己的业务调整:
* 1. 确认收货后,才创建分销记录
* 2. 支付 or 下单成功时,创建分销记录(冻结),确认收货解冻或者 n 天后解冻
*
* @param userId 用户编号
* @param orderId 订单编号
*/
@Async
protected void addBrokerageAsync(Long userId, Long orderId) {
MemberUserRespDTO user = memberUserApi.getUser(userId);
Assert.notNull(user);
// 每一个订单项,都会去生成分销记录
List<TradeOrderItemDO> orderItems = tradeOrderItemMapper.selectListByOrderId(orderId);
List<BrokerageAddReqBO> list = convertList(orderItems,
List<BrokerageAddReqBO> addList = convertList(orderItems,
item -> TradeOrderConvert.INSTANCE.convert(user, item, productSkuApi.getSku(item.getSkuId())));
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, list);
brokerageRecordService.addBrokerage(userId, BrokerageRecordBizTypeEnum.ORDER, addList);
}
@Async

View File

@@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.trade.service.order.bo;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
@@ -14,36 +15,73 @@ import javax.validation.constraints.NotNull;
@Data
public class TradeBeforeOrderCreateReqBO {
// TODO @puhui999注释也写下哈bo 还是写注释噢
/**
* 订单类型
*
* 枚举 {@link TradeOrderTypeEnum}
*/
@NotNull(message = "订单类型不能为空")
private Integer orderType;
/**
* 用户编号
*
* 关联 MemberUserDO 的 id 编号
*/
@NotNull(message = "用户编号不能为空")
private Long userId;
// ========== 秒杀活动相关字段 ==========
/**
*
*/
@Schema(description = "秒杀活动编号", example = "1024")
private Long seckillActivityId;
// ========== 拼团活动相关字段 ==========
/**
* 拼团活动编号
*/
@Schema(description = "拼团活动编号", example = "1024")
private Long combinationActivityId;
/**
* 拼团团长编号
*/
@Schema(description = "拼团团长编号", example = "2048")
private Long combinationHeadId;
// ========== 砍价活动相关字段 ==========
/**
* 砍价活动编号
*/
@Schema(description = "砍价活动编号", example = "123")
private Long bargainActivityId;
// ========== 活动购买商品相关字段 ==========
/**
* 商品 SPU 编号
*
* 关联 ProductSkuDO 的 spuId 编号
*/
@NotNull(message = "SPU 编号不能为空")
private Long spuId;
/**
* 商品 SKU 编号
*
* 关联 ProductSkuDO 的 id 编号
*/
@NotNull(message = "SKU 编号活动商品不能为空")
private Long skuId;
@NotNull(message = "用户编号不能为空")
private Long userId;
/**
* 购买的商品数量
*/
@NotNull(message = "购买数量不能为空")
private Integer count;

View File

@@ -20,14 +20,17 @@ public class TradeBargainHandler implements TradeOrderHandler {
@Resource
private BargainActivityApi bargainActivityApi;
// TODO @puhui999先临时写在这里在价格计算时如果是秒杀商品需要校验如下条件
// 1. 商品存在、库存充足、单次限购;
// 2. 活动进行中、时间段符合
@Override
public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {
// 如果是砍价订单
if (ObjectUtil.notEqual(TradeOrderTypeEnum.BARGAIN.getType(), reqBO.getOrderType())) {
return;
}
// 额外扣减砍价的库存
// 扣减砍价活动的库存
bargainActivityApi.updateBargainActivityStock(reqBO.getBargainActivityId(), reqBO.getCount());
}

View File

@@ -24,9 +24,12 @@ public interface TradeOrderHandler {
*/
void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO);
// TODO @puhui999这个搞成订单取消
/**
* 回滚
*/
void rollback();
// TODO @puhui999再搞个订单项取消哈
}

View File

@@ -20,13 +20,17 @@ public class TradeSeckillHandler implements TradeOrderHandler {
@Resource
private SeckillActivityApi seckillActivityApi;
// TODO @puhui999先临时写在这里在价格计算时如果是秒杀商品需要校验如下条件
// 1. 商品存在、库存充足、单次限购;
// 2. 活动进行中、时间段符合
@Override
public void beforeOrderCreate(TradeBeforeOrderCreateReqBO reqBO) {
// 如果是秒杀订单:额外扣减秒杀的库存;
if (ObjectUtil.notEqual(TradeOrderTypeEnum.SECKILL.getType(), reqBO.getOrderType())) {
return;
}
// 扣减秒杀活动的库存
seckillActivityApi.updateSeckillStock(reqBO.getSeckillActivityId(), reqBO.getSkuId(), reqBO.getCount());
}

View File

@@ -94,6 +94,10 @@ public class TradePriceCalculateRespBO {
* 对应 taobao 的 trade.point_fee 字段
*/
private Integer pointPrice;
/**
* VIP 减免金额,单位:分
*/
private Integer vipPrice;
/**
* 最终购买金额(总),单位:分
*
@@ -102,6 +106,7 @@ public class TradePriceCalculateRespBO {
* - {@link #pointPrice}
* - {@link #discountPrice}
* + {@link #deliveryPrice}
* - {@link #vipPrice}
*/
private Integer payPrice;
@@ -167,6 +172,10 @@ public class TradePriceCalculateRespBO {
* 使用的积分
*/
private Integer usePoint;
/**
* VIP 减免金额,单位:分
*/
private Integer vipPrice;
/**
* 应付金额(总),单位:分
*
@@ -175,6 +184,7 @@ public class TradePriceCalculateRespBO {
* - {@link #pointPrice}
* - {@link #discountPrice}
* + {@link #deliveryPrice}
* - {@link #vipPrice}
*/
private Integer payPrice;

View File

@@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.promotion.api.coupon.CouponApi;
import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponRespDTO;
@@ -9,6 +10,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
@@ -22,6 +24,7 @@ import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionU
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.filterList;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_MIN_PRICE;
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.COUPON_NO_MATCH_SPU;
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER;
/**
* 优惠劵的 {@link TradePriceCalculator} 实现类
@@ -44,6 +47,10 @@ public class TradeCouponPriceCalculator implements TradePriceCalculator {
CouponRespDTO coupon = couponApi.validateCoupon(new CouponValidReqDTO()
.setId(param.getCouponId()).setUserId(param.getUserId()));
Assert.notNull(coupon, "校验通过的优惠劵({}),不能为空", param.getCouponId());
// 1.2 只有【普通】订单,才允许使用优惠劵
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
throw exception(PRICE_CALCULATE_COUPON_NOT_MATCH_NORMAL_ORDER);
}
// 2.1 获得匹配的商品 SKU 数组
List<TradePriceCalculateRespBO.OrderItem> orderItems = filterMatchCouponOrderItems(result, coupon);

View File

@@ -64,7 +64,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
private void calculateByPickUp(TradePriceCalculateReqBO param) {
if (param.getPickUpStoreId() == null) {
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY);
// 价格计算时,如果为空就不算~最终下单,会校验该字段不允许空
return;
}
DeliveryPickUpStoreDO pickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(param.getPickUpStoreId());
if (pickUpStore == null || CommonStatusEnum.DISABLE.getStatus().equals(pickUpStore.getStatus())) {
@@ -77,7 +78,8 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 0. 得到收件地址区域
if (param.getAddressId() == null) {
throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY);
// 价格计算时,如果为空就不算~最终下单,会校验该字段不允许空
return;
}
AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());

View File

@@ -1,11 +1,13 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
@@ -33,6 +35,10 @@ public class TradeDiscountActivityPriceCalculator implements TradePriceCalculato
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 0. 只有【普通】订单,才计算该优惠
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
return;
}
// 获得 SKU 对应的限时折扣活动
List<DiscountProductRespDTO> discountProducts = discountActivityApi.getMatchDiscountProductList(
convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSkuId));

View File

@@ -0,0 +1,88 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.util.ObjectUtil;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import static cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper.formatPrice;
/**
* 会员 VIP 折扣的 {@link TradePriceCalculator} 实现类
*
* @author 芋道源码
*/
@Component
@Order(TradePriceCalculator.ORDER_MEMBER_LEVEL)
public class TradeMemberLevelPriceCalculator implements TradePriceCalculator {
@Resource
private MemberLevelApi memberLevelApi;
@Resource
private MemberUserApi memberUserApi;
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 0. 只有【普通】订单,才计算该优惠
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
return;
}
// 1. 获得用户的会员等级
MemberUserRespDTO user = memberUserApi.getUser(param.getUserId());
if (user.getLevelId() == null || user.getLevelId() <= 0) {
return;
}
MemberLevelRespDTO level = memberLevelApi.getMemberLevel(user.getLevelId());
if (level == null || level.getDiscountPercent() == null) {
return;
}
// 2. 计算每个 SKU 的优惠金额
result.getItems().forEach(orderItem -> {
// 2.1 计算优惠金额
Integer vipPrice = calculateVipPrice(orderItem.getPayPrice(), level.getDiscountPercent());
if (vipPrice <= 0) {
return;
}
// 2.2 记录优惠明细
if (orderItem.getSelected()) {
// 注意,只有在选中的情况下,才会记录到优惠明细。否则仅仅是更新 SKU 优惠金额,用于展示
TradePriceCalculatorHelper.addPromotion(result, orderItem,
level.getId(), level.getName(), PromotionTypeEnum.MEMBER_LEVEL.getType(),
String.format("会员等级折扣:省 %s 元", formatPrice(vipPrice)),
vipPrice);
}
// 2.3 更新 SKU 的优惠金额
orderItem.setVipPrice(vipPrice);
TradePriceCalculatorHelper.recountPayPrice(orderItem);
});
TradePriceCalculatorHelper.recountAllPrice(result);
}
/**
* 计算会员 VIP 优惠价格
*
* @param price 原价
* @param discountPercent 折扣
* @return 优惠价格
*/
public Integer calculateVipPrice(Integer price, Integer discountPercent) {
if (discountPercent == null) {
return 0;
}
Integer newPrice = price * discountPercent / 100;
return price - newPrice;
}
}

View File

@@ -13,6 +13,7 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
*/
public interface TradePriceCalculator {
int ORDER_MEMBER_LEVEL = 5;
int ORDER_DISCOUNT_ACTIVITY = 10;
int ORDER_REWARD_ACTIVITY = 20;
int ORDER_COUPON = 30;

View File

@@ -52,7 +52,7 @@ public class TradePriceCalculatorHelper {
.setCount(item.getCount()).setCartId(item.getCartId()).setSelected(item.getSelected());
// sku 价格
orderItem.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * item.getCount())
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0).setVipPrice(0);
// sku 信息
orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties())
.setWeight(sku.getWeight()).setVolume(sku.getVolume());
@@ -98,7 +98,7 @@ public class TradePriceCalculatorHelper {
// 先重置
TradePriceCalculateRespBO.Price price = result.getPrice();
price.setTotalPrice(0).setDiscountPrice(0).setDeliveryPrice(0)
.setCouponPrice(0).setPointPrice(0).setPayPrice(0);
.setCouponPrice(0).setPointPrice(0).setVipPrice(0).setPayPrice(0);
// 再合计 item
result.getItems().forEach(item -> {
if (!item.getSelected()) {
@@ -109,6 +109,7 @@ public class TradePriceCalculatorHelper {
price.setDeliveryPrice(price.getDeliveryPrice() + item.getDeliveryPrice());
price.setCouponPrice(price.getCouponPrice() + item.getCouponPrice());
price.setPointPrice(price.getPointPrice() + item.getPointPrice());
price.setVipPrice(price.getVipPrice() + item.getVipPrice());
price.setPayPrice(price.getPayPrice() + item.getPayPrice());
});
}
@@ -132,7 +133,9 @@ public class TradePriceCalculatorHelper {
- orderItem.getDiscountPrice()
+ orderItem.getDeliveryPrice()
- orderItem.getCouponPrice()
- orderItem.getPointPrice());
- orderItem.getPointPrice()
- orderItem.getVipPrice()
);
}
/**
@@ -162,6 +165,9 @@ public class TradePriceCalculatorHelper {
if (orderItem.getGivePoint() == null) {
orderItem.setGivePoint(0);
}
if (orderItem.getVipPrice() == null) {
orderItem.setVipPrice(0);
}
recountPayPrice(orderItem);
});
}

View File

@@ -1,11 +1,13 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.springframework.core.annotation.Order;
@@ -32,6 +34,10 @@ public class TradeRewardActivityPriceCalculator implements TradePriceCalculator
@Override
public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
// 0. 只有【普通】订单,才计算该优惠
if (ObjectUtil.notEqual(result.getType(), TradeOrderTypeEnum.NORMAL.getType())) {
return;
}
// 获得 SKU 对应的满减送活动
List<RewardActivityMatchRespDTO> rewardActivities = rewardActivityApi.getMatchRewardActivityList(
convertSet(result.getItems(), TradePriceCalculateRespBO.OrderItem::getSpuId));

View File

@@ -7,6 +7,7 @@ import cn.iocoder.yudao.module.promotion.api.coupon.dto.CouponValidReqDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionProductScopeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.junit.jupiter.api.Test;
@@ -47,6 +48,7 @@ public class TradeCouponPriceCalculatorTest extends BaseMockitoUnitTest {
new TradePriceCalculateReqBO.Item().setSkuId(40L).setCount(5).setSelected(false) // 匹配优惠劵,但是未选中
));
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setItems(asList(

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.discount.DiscountActivityApi;
import cn.iocoder.yudao.module.promotion.api.discount.dto.DiscountProductRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.junit.jupiter.api.Test;
@@ -42,6 +43,7 @@ public class TradeDiscountActivityPriceCalculatorTest extends BaseMockitoUnitTes
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中
));
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setItems(asList(

View File

@@ -0,0 +1,118 @@
package cn.iocoder.yudao.module.trade.service.price.calculator;
import cn.iocoder.yudao.framework.test.core.ut.BaseMockitoUnitTest;
import cn.iocoder.yudao.module.member.api.level.MemberLevelApi;
import cn.iocoder.yudao.module.member.api.level.dto.MemberLevelRespDTO;
import cn.iocoder.yudao.module.member.api.user.MemberUserApi;
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.junit.jupiter.api.Test;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import java.util.ArrayList;
import static java.util.Arrays.asList;
import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;
/**
* {@link TradeMemberLevelPriceCalculator} 的单元测试类
*
* @author 芋道源码
*/
public class TradeMemberLevelPriceCalculatorTest extends BaseMockitoUnitTest {
@InjectMocks
private TradeMemberLevelPriceCalculator memberLevelPriceCalculator;
@Mock
private MemberLevelApi memberLevelApi;
@Mock
private MemberUserApi memberUserApi;
@Test
public void testCalculate() {
// 准备参数
TradePriceCalculateReqBO param = new TradePriceCalculateReqBO()
.setUserId(1024L)
.setItems(asList(
new TradePriceCalculateReqBO.Item().setSkuId(10L).setCount(2).setSelected(true), // 匹配活动,且已选中
new TradePriceCalculateReqBO.Item().setSkuId(20L).setCount(3).setSelected(false) // 匹配活动,但未选中
));
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setItems(asList(
new TradePriceCalculateRespBO.OrderItem().setSkuId(10L).setCount(2).setSelected(true)
.setPrice(100),
new TradePriceCalculateRespBO.OrderItem().setSkuId(20L).setCount(3).setSelected(false)
.setPrice(50)
));
// 保证价格被初始化上
TradePriceCalculatorHelper.recountPayPrice(result.getItems());
TradePriceCalculatorHelper.recountAllPrice(result);
// mock 方法(会员等级)
when(memberUserApi.getUser(eq(1024L))).thenReturn(new MemberUserRespDTO().setLevelId(2048L));
when(memberLevelApi.getMemberLevel(eq(2048L))).thenReturn(
new MemberLevelRespDTO().setId(2048L).setName("VIP 会员").setDiscountPercent(60));
// 调用
memberLevelPriceCalculator.calculate(param, result);
// 断言Price 部分
TradePriceCalculateRespBO.Price price = result.getPrice();
assertEquals(price.getTotalPrice(), 200);
assertEquals(price.getDiscountPrice(), 0);
assertEquals(price.getPointPrice(), 0);
assertEquals(price.getDeliveryPrice(), 0);
assertEquals(price.getCouponPrice(), 0);
assertEquals(price.getVipPrice(), 80);
assertEquals(price.getPayPrice(), 120);
assertNull(result.getCouponId());
// 断言SKU 1
assertEquals(result.getItems().size(), 2);
TradePriceCalculateRespBO.OrderItem orderItem01 = result.getItems().get(0);
assertEquals(orderItem01.getSkuId(), 10L);
assertEquals(orderItem01.getCount(), 2);
assertEquals(orderItem01.getPrice(), 100);
assertEquals(orderItem01.getDiscountPrice(), 0);
assertEquals(orderItem01.getDeliveryPrice(), 0);
assertEquals(orderItem01.getCouponPrice(), 0);
assertEquals(orderItem01.getPointPrice(), 0);
assertEquals(orderItem01.getVipPrice(), 80);
assertEquals(orderItem01.getPayPrice(), 120);
// 断言SKU 2
TradePriceCalculateRespBO.OrderItem orderItem02 = result.getItems().get(1);
assertEquals(orderItem02.getSkuId(), 20L);
assertEquals(orderItem02.getCount(), 3);
assertEquals(orderItem02.getPrice(), 50);
assertEquals(orderItem02.getDiscountPrice(), 0);
assertEquals(orderItem02.getDeliveryPrice(), 0);
assertEquals(orderItem02.getCouponPrice(), 0);
assertEquals(orderItem02.getPointPrice(), 0);
assertEquals(orderItem02.getVipPrice(), 60);
assertEquals(orderItem02.getPayPrice(), 90);
// 断言Promotion 部分
assertEquals(result.getPromotions().size(), 1);
TradePriceCalculateRespBO.Promotion promotion01 = result.getPromotions().get(0);
assertEquals(promotion01.getId(), 2048L);
assertEquals(promotion01.getName(), "VIP 会员");
assertEquals(promotion01.getType(), PromotionTypeEnum.MEMBER_LEVEL.getType());
assertEquals(promotion01.getTotalPrice(), 200);
assertEquals(promotion01.getDiscountPrice(), 80);
assertTrue(promotion01.getMatch());
assertEquals(promotion01.getDescription(), "会员等级折扣:省 0.80 元");
TradePriceCalculateRespBO.PromotionItem promotionItem01 = promotion01.getItems().get(0);
assertEquals(promotion01.getItems().size(), 1);
assertEquals(promotionItem01.getSkuId(), 10L);
assertEquals(promotionItem01.getTotalPrice(), 200);
assertEquals(promotionItem01.getDiscountPrice(), 80);
}
}

View File

@@ -5,6 +5,7 @@ import cn.iocoder.yudao.module.promotion.api.reward.RewardActivityApi;
import cn.iocoder.yudao.module.promotion.api.reward.dto.RewardActivityMatchRespDTO;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionConditionTypeEnum;
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
import org.junit.jupiter.api.Test;
@@ -44,6 +45,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true) // 匹配活动 2
));
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setItems(asList(
@@ -157,6 +159,7 @@ public class TradeRewardActivityPriceCalculatorTest extends BaseMockitoUnitTest
new TradePriceCalculateReqBO.Item().setSkuId(30L).setCount(4).setSelected(true)
));
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO()
.setType(TradeOrderTypeEnum.NORMAL.getType())
.setPrice(new TradePriceCalculateRespBO.Price())
.setPromotions(new ArrayList<>())
.setItems(asList(