Commit 1b477aaa0d46985efff9f8891c762e54d7bf9c00

Authored by YunaiV
1 parent c766f7da

trade:优化下单逻辑的实现

Showing 22 changed files with 268 additions and 110 deletions
yudao-module-mall/yudao-module-product-api/src/main/java/cn/iocoder/yudao/module/product/api/sku/ProductSkuApi.java
@@ -39,7 +39,7 @@ public interface ProductSkuApi { @@ -39,7 +39,7 @@ public interface ProductSkuApi {
39 List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds); 39 List<ProductSkuRespDTO> getSkuListBySpuId(Collection<Long> spuIds);
40 40
41 /** 41 /**
42 - * 更新 SKU 库存 42 + * 更新 SKU 库存(增加 or 减少)
43 * 43 *
44 * @param updateStockReqDTO 更新请求 44 * @param updateStockReqDTO 更新请求
45 */ 45 */
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/ErrorCodeConstants.java
@@ -58,8 +58,9 @@ public interface ErrorCodeConstants { @@ -58,8 +58,9 @@ public interface ErrorCodeConstants {
58 58
59 // ========== Price 相关 1011003000 ============ 59 // ========== Price 相关 1011003000 ============
60 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0"); 60 ErrorCode PRICE_CALCULATE_PAY_PRICE_ILLEGAL = new ErrorCode(1011003000, "支付价格计算异常,原因:价格小于等于 0");
61 - ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空"); 61 + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY = new ErrorCode(1011003001, "计算快递运费异常,收件人地址编号为空");
62 ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板"); 62 ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND = new ErrorCode(1011003002, "计算快递运费异常,找不到对应的运费模板");
  63 + ErrorCode PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY = new ErrorCode(1011003003, "计算快递运费异常,自提点为空");
63 64
64 // ========== 物流 Express 模块 1011004000 ========== 65 // ========== 物流 Express 模块 1011004000 ==========
65 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在"); 66 ErrorCode EXPRESS_NOT_EXISTS = new ErrorCode(1011004000, "快递公司不存在");
yudao-module-mall/yudao-module-trade-api/src/main/java/cn/iocoder/yudao/module/trade/enums/delivery/DeliveryTypeEnum.java
@@ -15,7 +15,6 @@ import java.util.Arrays; @@ -15,7 +15,6 @@ import java.util.Arrays;
15 @AllArgsConstructor 15 @AllArgsConstructor
16 public enum DeliveryTypeEnum implements IntArrayValuable { 16 public enum DeliveryTypeEnum implements IntArrayValuable {
17 17
18 - NULL(0, "无需物流"),  
19 EXPRESS(1, "快递发货"), 18 EXPRESS(1, "快递发货"),
20 PICK_UP(2, "用户自提"),; 19 PICK_UP(2, "用户自提"),;
21 20
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
@@ -15,7 +15,8 @@ Authorization: Bearer {{appToken}} @@ -15,7 +15,8 @@ Authorization: Bearer {{appToken}}
15 tenant-id: {{appTenentId}} 15 tenant-id: {{appTenentId}}
16 16
17 { 17 {
18 - "type": 0, 18 + "pointStatus": true,
  19 + "deliveryType": 1,
19 "addressId": 21, 20 "addressId": 21,
20 "items": [ 21 "items": [
21 { 22 {
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
1 package cn.iocoder.yudao.module.trade.controller.app.order; 1 package cn.iocoder.yudao.module.trade.controller.app.order;
2 2
3 -import cn.hutool.core.map.MapUtil;  
4 import cn.iocoder.yudao.framework.common.pojo.CommonResult; 3 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
5 import cn.iocoder.yudao.framework.common.pojo.PageResult; 4 import cn.iocoder.yudao.framework.common.pojo.PageResult;
6 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated; 5 import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
@@ -12,11 +11,8 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert; @@ -12,11 +11,8 @@ import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
12 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; 11 import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO;
13 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; 12 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO;
14 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; 13 import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO;
15 -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum;  
16 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; 14 import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum;
17 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 15 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
18 -import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;  
19 -import cn.iocoder.yudao.module.trade.framework.order.core.utils.TradeOrderLogUtils;  
20 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; 16 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService;
21 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; 17 import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService;
22 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; 18 import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService;
@@ -65,10 +61,7 @@ public class AppTradeOrderController { @@ -65,10 +61,7 @@ public class AppTradeOrderController {
65 @PostMapping("/create") 61 @PostMapping("/create")
66 @Operation(summary = "创建订单") 62 @Operation(summary = "创建订单")
67 @PreAuthenticated 63 @PreAuthenticated
68 - @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.TEST)  
69 - public CommonResult<AppTradeOrderCreateRespVO> createOrder(@RequestBody AppTradeOrderCreateReqVO createReqVO) {  
70 - TradeOrderLogUtils.setOrderInfo(10L, 1, 2,  
71 - MapUtil.<String, Object>builder().put("nickname", "小明").put("thing", "种土豆").build()); 64 + public CommonResult<AppTradeOrderCreateRespVO> createOrder(@Valid @RequestBody AppTradeOrderCreateReqVO createReqVO) {
72 TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO); 65 TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO);
73 return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId())); 66 return success(new AppTradeOrderCreateRespVO().setId(order.getId()).setPayOrderId(order.getPayOrderId()));
74 } 67 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderCreateReqVO.java
1 package cn.iocoder.yudao.module.trade.controller.app.order.vo; 1 package cn.iocoder.yudao.module.trade.controller.app.order.vo;
2 2
  3 +import com.fasterxml.jackson.annotation.JsonIgnore;
3 import io.swagger.v3.oas.annotations.media.Schema; 4 import io.swagger.v3.oas.annotations.media.Schema;
4 import lombok.Data; 5 import lombok.Data;
5 6
  7 +import javax.validation.constraints.AssertTrue;
  8 +
6 @Schema(description = "用户 App - 交易订单创建 Request VO") 9 @Schema(description = "用户 App - 交易订单创建 Request VO")
7 @Data 10 @Data
8 public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { 11 public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
@@ -10,4 +13,10 @@ public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { @@ -10,4 +13,10 @@ public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO {
10 @Schema(description = "备注", example = "这个是我的订单哟") 13 @Schema(description = "备注", example = "这个是我的订单哟")
11 private String remark; 14 private String remark;
12 15
  16 + @AssertTrue(message = "配送方式不能为空")
  17 + @JsonIgnore
  18 + public boolean isDeliveryTypeNotNull() {
  19 + return getDeliveryType() != null;
  20 + }
  21 +
13 } 22 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/vo/AppTradeOrderSettlementReqVO.java
@@ -30,7 +30,7 @@ public class AppTradeOrderSettlementReqVO { @@ -30,7 +30,7 @@ public class AppTradeOrderSettlementReqVO {
30 private Boolean pointStatus; 30 private Boolean pointStatus;
31 31
32 // ========== 配送相关相关字段 ========== 32 // ========== 配送相关相关字段 ==========
33 - @Schema(description = "配送方式", required = true, example = "1") 33 + @Schema(description = "配送方式", example = "1")
34 @InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确") 34 @InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确")
35 private Integer deliveryType; 35 private Integer deliveryType;
36 36
@@ -62,6 +62,8 @@ public class AppTradeOrderSettlementReqVO { @@ -62,6 +62,8 @@ public class AppTradeOrderSettlementReqVO {
62 @Schema(description = "砍价活动编号", example = "123") 62 @Schema(description = "砍价活动编号", example = "123")
63 private Long bargainActivityId; 63 private Long bargainActivityId;
64 64
  65 + // TODO @puhui999:可以写个参数校验,如果 seckillActivityId 或 combinationActivityId 或 combinationHeadId 的情况,items 应该只有一个
  66 +
65 @Data 67 @Data
66 @Schema(description = "用户 App - 商品项") 68 @Schema(description = "用户 App - 商品项")
67 @Valid 69 @Valid
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/convert/order/TradeOrderConvert.java
@@ -56,6 +56,7 @@ public interface TradeOrderConvert { @@ -56,6 +56,7 @@ public interface TradeOrderConvert {
56 56
57 @Mappings({ 57 @Mappings({
58 @Mapping(target = "id", ignore = true), 58 @Mapping(target = "id", ignore = true),
  59 + @Mapping(source = "userId", target = "userId"),
59 @Mapping(source = "createReqVO.couponId", target = "couponId"), 60 @Mapping(source = "createReqVO.couponId", target = "couponId"),
60 @Mapping(target = "remark", ignore = true), 61 @Mapping(target = "remark", ignore = true),
61 @Mapping(source = "createReqVO.remark", target = "userRemark"), 62 @Mapping(source = "createReqVO.remark", target = "userRemark"),
@@ -89,23 +90,16 @@ public interface TradeOrderConvert { @@ -89,23 +90,16 @@ public interface TradeOrderConvert {
89 TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item); 90 TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item);
90 91
91 default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) { 92 default ProductSkuUpdateStockReqDTO convert(List<TradeOrderItemDO> list) {
92 - return new ProductSkuUpdateStockReqDTO(TradeOrderConvert.INSTANCE.convertList(list)); 93 + List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
  94 + new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
  95 + return new ProductSkuUpdateStockReqDTO(items);
93 } 96 }
94 -  
95 - default ProductSkuUpdateStockReqDTO convertNegative(List<TradeOrderItemDO> list) {  
96 - List<ProductSkuUpdateStockReqDTO.Item> items = TradeOrderConvert.INSTANCE.convertList(list);  
97 - items.forEach(item -> item.setIncrCount(-item.getIncrCount())); 97 + default ProductSkuUpdateStockReqDTO convertNegative(List<AppTradeOrderSettlementReqVO.Item> list) {
  98 + List<ProductSkuUpdateStockReqDTO.Item> items = CollectionUtils.convertList(list, item ->
  99 + new ProductSkuUpdateStockReqDTO.Item().setId(item.getSkuId()).setIncrCount(-item.getCount()));
98 return new ProductSkuUpdateStockReqDTO(items); 100 return new ProductSkuUpdateStockReqDTO(items);
99 } 101 }
100 102
101 - List<ProductSkuUpdateStockReqDTO.Item> convertList(List<TradeOrderItemDO> list);  
102 -  
103 - @Mappings({  
104 - @Mapping(source = "skuId", target = "id"),  
105 - @Mapping(source = "count", target = "incrCount"),  
106 - })  
107 - ProductSkuUpdateStockReqDTO.Item convert(TradeOrderItemDO bean);  
108 -  
109 default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems, 103 default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
110 TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) { 104 TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) {
111 PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO() 105 PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO()
@@ -218,8 +212,9 @@ public interface TradeOrderConvert { @@ -218,8 +212,9 @@ public interface TradeOrderConvert {
218 212
219 default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, 213 default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
220 List<CartDO> cartList) { 214 List<CartDO> cartList) {
221 - TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();  
222 - reqBO.setUserId(userId).setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId()) 215 + TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO().setUserId(userId)
  216 + .setCouponId(settlementReqVO.getCouponId()).setPointStatus(settlementReqVO.getPointStatus())
  217 + .setDeliveryType(settlementReqVO.getDeliveryType()).setAddressId(settlementReqVO.getAddressId())
223 .setItems(new ArrayList<>(settlementReqVO.getItems().size())); 218 .setItems(new ArrayList<>(settlementReqVO.getItems().size()));
224 // 商品项的构建 219 // 商品项的构建
225 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId); 220 Map<Long, CartDO> cartMap = convertMap(cartList, CartDO::getId);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/dal/dataobject/order/TradeOrderDO.java
@@ -251,12 +251,19 @@ public class TradeOrderDO extends BaseDO { @@ -251,12 +251,19 @@ public class TradeOrderDO extends BaseDO {
251 * 对应 taobao 的 trade.coupon_fee 字段 251 * 对应 taobao 的 trade.coupon_fee 字段
252 */ 252 */
253 private Integer couponPrice; 253 private Integer couponPrice;
254 - // TODO 芋艿:需要记录使用的积分; 254 + /**
  255 + * 使用的积分
  256 + */
  257 + private Integer usePoint;
255 /** 258 /**
256 * 积分抵扣的金额,单位:分 259 * 积分抵扣的金额,单位:分
257 * 260 *
258 * 对应 taobao 的 trade.point_fee 字段 261 * 对应 taobao 的 trade.point_fee 字段
259 */ 262 */
260 private Integer pointPrice; 263 private Integer pointPrice;
  264 +// /**
  265 +// * 奖励的积分 TODO 疯狂:可以使用这个字段哈;
  266 +// */
  267 +// private Integer rewardPoint;
261 268
262 } 269 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/TradeOrderUpdateServiceImpl.java
@@ -47,6 +47,7 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum; @@ -47,6 +47,7 @@ import cn.iocoder.yudao.module.trade.enums.brokerage.BrokerageRecordBizTypeEnum;
47 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 47 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
48 import cn.iocoder.yudao.module.trade.enums.order.*; 48 import cn.iocoder.yudao.module.trade.enums.order.*;
49 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; 49 import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
  50 +import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog;
50 import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; 51 import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO;
51 import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService; 52 import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService;
52 import cn.iocoder.yudao.module.trade.service.cart.CartService; 53 import cn.iocoder.yudao.module.trade.service.cart.CartService;
@@ -182,32 +183,24 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -182,32 +183,24 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
182 183
183 @Override 184 @Override
184 @Transactional(rollbackFor = Exception.class) 185 @Transactional(rollbackFor = Exception.class)
  186 + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE)
185 public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) { 187 public TradeOrderDO createOrder(Long userId, String userIp, AppTradeOrderCreateReqVO createReqVO) {
186 - // 1、执行订单创建前置处理器  
187 - // TODO @puhui999:最好也抽个 beforeOrderCreate 方法;不要 BO 各自处理参数岂不美哉?  
188 - TradeBeforeOrderCreateReqBO beforeOrderCreateReqBO = TradeOrderConvert.INSTANCE.convert(createReqVO);  
189 - beforeOrderCreateReqBO.setOrderType(validateActivity(createReqVO));  
190 - beforeOrderCreateReqBO.setUserId(userId);  
191 - beforeOrderCreateReqBO.setCount(getSumValue(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCount, Integer::sum));  
192 - // TODO @puhui999:这里有个纠结点;handler 的定义是只处理指定类型的订单的拓展逻辑;还是通用的 handler,类似可以处理优惠劵等等  
193 - tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(beforeOrderCreateReqBO));  
194 -  
195 - // 2. 价格计算 188 + // 0. 价格计算
196 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO); 189 TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO);
197 190
  191 + // 1. 订单创建前的逻辑
  192 + beforeCreateTradeOrder(userId, createReqVO, calculateRespBO);
  193 +
198 // 2.1 插入 TradeOrderDO 订单 194 // 2.1 插入 TradeOrderDO 订单
199 TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO); 195 TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO);
200 // 2.2 插入 TradeOrderItemDO 订单项 196 // 2.2 插入 TradeOrderItemDO 订单项
201 List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO); 197 List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO);
202 198
203 - // 3. 订单创建完后的逻辑 199 + // 3. 订单创建后的逻辑
204 afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO); 200 afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO);
205 -  
206 - // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!  
207 return order; 201 return order;
208 } 202 }
209 203
210 -  
211 // TODO @puhui999:订单超时,自动取消; 204 // TODO @puhui999:订单超时,自动取消;
212 205
213 /** 206 /**
@@ -234,9 +227,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -234,9 +227,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
234 address = validateAddress(userId, createReqVO.getAddressId()); 227 address = validateAddress(userId, createReqVO.getAddressId());
235 } 228 }
236 TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address); 229 TradeOrderDO order = TradeOrderConvert.INSTANCE.convert(userId, clientIp, createReqVO, calculateRespBO, address);
237 - String no = orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX);  
238 - order.setType(validateActivity(createReqVO));  
239 - order.setNo(no); 230 + order.setType(calculateRespBO.getType());
  231 + order.setNo(orderNoRedisDAO.generate(TradeOrderNoRedisDAO.TRADE_ORDER_NO_PREFIX));
240 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); 232 order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus());
241 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); 233 order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus());
242 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); 234 order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum));
@@ -252,71 +244,80 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -252,71 +244,80 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
252 return order; 244 return order;
253 } 245 }
254 246
  247 + private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO,
  248 + TradePriceCalculateRespBO calculateRespBO) {
  249 + List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);
  250 + tradeOrderItemMapper.insertBatch(orderItems);
  251 + return orderItems;
  252 + }
  253 +
255 /** 254 /**
256 - * 校验活动,并返回订单类型 255 + * 订单创建前,执行前置逻辑
257 * 256 *
258 - * @param createReqVO 请求参数  
259 - * @return 订单类型 257 + * @param userId 用户编号
  258 + * @param createReqVO 创建订单请求
  259 + * @param calculateRespBO 订单价格计算结果
260 */ 260 */
261 - private Integer validateActivity(AppTradeOrderCreateReqVO createReqVO) {  
262 - if (createReqVO.getSeckillActivityId() != null) {  
263 - return TradeOrderTypeEnum.SECKILL.getType();  
264 - }  
265 - if (createReqVO.getCombinationActivityId() != null) {  
266 - return TradeOrderTypeEnum.COMBINATION.getType();  
267 - }  
268 - // TODO 砍价敬请期待  
269 - return TradeOrderTypeEnum.NORMAL.getType();  
270 - } 261 + private void beforeCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
  262 + TradePriceCalculateRespBO calculateRespBO) {
  263 + // 1. 执行订单创建前置处理器
  264 + TradeBeforeOrderCreateReqBO beforeOrderCreateReqBO = TradeOrderConvert.INSTANCE.convert(createReqVO);
  265 + beforeOrderCreateReqBO.setOrderType(calculateRespBO.getType());
  266 + beforeOrderCreateReqBO.setUserId(userId);
  267 + beforeOrderCreateReqBO.setCount(getSumValue(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCount, Integer::sum));
  268 + // TODO @puhui999:这里有个纠结点;handler 的定义是只处理指定类型的订单的拓展逻辑;还是通用的 handler,类似可以处理优惠劵等等
  269 + tradeOrderHandlers.forEach(handler -> handler.beforeOrderCreate(beforeOrderCreateReqBO));
271 270
272 - private List<TradeOrderItemDO> createTradeOrderItems(TradeOrderDO tradeOrderDO, TradePriceCalculateRespBO calculateRespBO) {  
273 - List<TradeOrderItemDO> orderItems = TradeOrderConvert.INSTANCE.convertList(tradeOrderDO, calculateRespBO);  
274 - tradeOrderItemMapper.insertBatch(orderItems);  
275 - return orderItems; 271 + // 2. 下单时扣减商品库存
  272 + productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(createReqVO.getItems()));
276 } 273 }
277 274
278 /** 275 /**
279 - * 执行创建完创建完订单后的逻辑 276 + * 订单创建后,执行后置逻辑
280 * 277 *
281 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等 278 * 例如说:优惠劵的扣减、积分的扣减、支付单的创建等等
282 * 279 *
283 * @param userId 用户编号 280 * @param userId 用户编号
284 * @param createReqVO 创建订单请求 281 * @param createReqVO 创建订单请求
285 - * @param tradeOrderDO 交易订单 282 + * @param order 交易订单
286 * @param calculateRespBO 订单价格计算结果 283 * @param calculateRespBO 订单价格计算结果
287 */ 284 */
288 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, 285 private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO,
289 - TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems, 286 + TradeOrderDO order, List<TradeOrderItemDO> orderItems,
290 TradePriceCalculateRespBO calculateRespBO) { 287 TradePriceCalculateRespBO calculateRespBO) {
291 - // 执行订单创建后置处理器  
292 - tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(TradeOrderConvert.INSTANCE.convert(userId, createReqVO, tradeOrderDO, orderItems.get(0))));  
293 -  
294 - // 扣减积分 TODO 芋艿:待实现,需要前置;  
295 - // 这个是不是应该放到支付成功之后?如果支付后的话,可能积分可以重复使用哈。资源类,都要预扣 288 + // 1. 执行订单创建后置处理器
  289 + // TODO @puhui999:从通用性来说,应该不用 orderItems.get(0)
  290 + tradeOrderHandlers.forEach(handler -> handler.afterOrderCreate(
  291 + TradeOrderConvert.INSTANCE.convert(userId, createReqVO, order, orderItems.get(0))));
296 292
297 - // 有使用优惠券时更新 TODO 芋艿:需要前置; 293 + // 2. 有使用优惠券时更新
  294 + // 不在前置扣减的原因,是因为优惠劵要记录使用的订单号
298 if (createReqVO.getCouponId() != null) { 295 if (createReqVO.getCouponId() != null) {
299 couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId) 296 couponApi.useCoupon(new CouponUseReqDTO().setId(createReqVO.getCouponId()).setUserId(userId)
300 - .setOrderId(tradeOrderDO.getId())); 297 + .setOrderId(order.getId()));
301 } 298 }
302 299
303 - // 下单时扣减商品库存  
304 - productSkuApi.updateSkuStock(TradeOrderConvert.INSTANCE.convertNegative(orderItems)); 300 + // 3. 扣减积分
  301 + // 不在前置扣减的原因,是因为积分扣减时,需要记录关联业务
  302 + if (order.getUsePoint() != null && order.getUsePoint() > 0) {
  303 + memberPointApi.reducePoint(userId, calculateRespBO.getUsePoint(),
  304 + MemberPointBizTypeEnum.ORDER_USE.getType(), String.valueOf(order.getId()));
  305 + }
305 306
306 - // 删除购物车商品 307 + // 4. 删除购物车商品
307 Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); 308 Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId);
308 if (CollUtil.isNotEmpty(cartIds)) { 309 if (CollUtil.isNotEmpty(cartIds)) {
309 cartService.deleteCart(userId, cartIds); 310 cartService.deleteCart(userId, cartIds);
310 } 311 }
311 312
312 - // 生成预支付  
313 - createPayOrder(tradeOrderDO, orderItems, calculateRespBO); 313 + // 5. 生成预支付
  314 + createPayOrder(order, orderItems, calculateRespBO);
314 315
315 - // 增加订单日志 TODO 芋艿:待实现 316 + // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来!
316 } 317 }
317 318
318 -  
319 - private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems, TradePriceCalculateRespBO calculateRespBO) { 319 + private void createPayOrder(TradeOrderDO order, List<TradeOrderItemDO> orderItems,
  320 + TradePriceCalculateRespBO calculateRespBO) {
320 // 创建支付单,用于后续的支付 321 // 创建支付单,用于后续的支付
321 PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( 322 PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert(
322 order, orderItems, calculateRespBO, tradeOrderProperties); 323 order, orderItems, calculateRespBO, tradeOrderProperties);
@@ -344,6 +345,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -344,6 +345,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
344 } 345 }
345 // 校验活动 346 // 校验活动
346 // 1、拼团活动 347 // 1、拼团活动
  348 + // TODO @puhui999:这块也抽象到 handler 里
347 if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { 349 if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) {
348 // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录 350 // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录
349 combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now()); 351 combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now());
@@ -741,6 +743,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -741,6 +743,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
741 couponApi.returnUsedCoupon(order.getCouponId()); 743 couponApi.returnUsedCoupon(order.getCouponId());
742 744
743 // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择; 745 // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择;
  746 + // TODO @疯狂:有赞是可配置(支付 or 确认收货),我们按照支付好列;然后这里的退积分,指的是下单时的积分抵扣。
744 747
745 // TODO 芋艿:OrderLog 748 // TODO 芋艿:OrderLog
746 749
@@ -773,17 +776,18 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { @@ -773,17 +776,18 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService {
773 776
774 @Async 777 @Async
775 protected void addUserPointAsync(Long userId, Integer payPrice, Long orderId) { 778 protected void addUserPointAsync(Long userId, Integer payPrice, Long orderId) {
776 - int bizType = MemberPointBizTypeEnum.ORDER_BUY.getType(); 779 + // TODO @疯狂:具体多少积分,需要分成 2 不分:1. 支付金额;2. 商品金额
  780 + int bizType = MemberPointBizTypeEnum.ORDER_REWARD.getType();
777 memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId)); 781 memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId));
778 } 782 }
779 783
780 @Async 784 @Async
781 protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) { 785 protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) {
  786 + // TODO @疯狂:退款时,按照金额比例,退还积分;https://help.youzan.com/displaylist/detail_4_4-1-49185
782 int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType(); 787 int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType();
783 memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); 788 memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId));
784 } 789 }
785 790
786 -  
787 @Async 791 @Async
788 protected void addBrokerageAsync(Long userId, Long orderId) { 792 protected void addBrokerageAsync(Long userId, Long orderId) {
789 MemberUserRespDTO user = memberUserApi.getUser(userId); 793 MemberUserRespDTO user = memberUserApi.getUser(userId);
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/order/handler/TradeCombinationHandler.java
@@ -37,6 +37,11 @@ public class TradeCombinationHandler implements TradeOrderHandler { @@ -37,6 +37,11 @@ public class TradeCombinationHandler implements TradeOrderHandler {
37 37
38 @Override 38 @Override
39 public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) { 39 public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) {
  40 + // TODO @puhui999:需要判断下;
  41 + if (true) {
  42 + return;
  43 + }
  44 +
40 // 创建砍价记录 45 // 创建砍价记录
41 combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(reqBO)); 46 combinationRecordApi.createCombinationRecord(TradeOrderConvert.INSTANCE.convert(reqBO));
42 } 47 }
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateReqBO.java
1 package cn.iocoder.yudao.module.trade.service.price.bo; 1 package cn.iocoder.yudao.module.trade.service.price.bo;
2 2
3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 3 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
4 -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;  
5 import lombok.Data; 4 import lombok.Data;
6 5
7 import javax.validation.Valid; 6 import javax.validation.Valid;
@@ -18,13 +17,6 @@ import java.util.List; @@ -18,13 +17,6 @@ import java.util.List;
18 public class TradePriceCalculateReqBO { 17 public class TradePriceCalculateReqBO {
19 18
20 /** 19 /**
21 - * 订单类型  
22 - *  
23 - * 枚举 {@link TradeOrderTypeEnum}  
24 - */  
25 - private Integer type;  
26 -  
27 - /**  
28 * 用户编号 20 * 用户编号
29 * 21 *
30 * 对应 MemberUserDO 的 id 编号 22 * 对应 MemberUserDO 的 id 编号
@@ -37,20 +29,31 @@ public class TradePriceCalculateReqBO { @@ -37,20 +29,31 @@ public class TradePriceCalculateReqBO {
37 * 对应 CouponDO 的 id 编号 29 * 对应 CouponDO 的 id 编号
38 */ 30 */
39 private Long couponId; 31 private Long couponId;
  32 + // TODO @疯狂:需要增加一个 PriceCalculator 实现积分扣减的计算;写回到 TradePriceCalculateRespBO 的 usePoint
  33 + /**
  34 + * 是否使用积分
  35 + */
  36 + @NotNull(message = "是否使用积分不能为空")
  37 + private Boolean pointStatus;
40 38
41 /** 39 /**
  40 + * 配送方式
  41 + *
  42 + * 枚举 {@link DeliveryTypeEnum}
  43 + */
  44 + private Integer deliveryType;
  45 + /**
42 * 收货地址编号 46 * 收货地址编号
43 * 47 *
44 * 对应 MemberAddressDO 的 id 编号 48 * 对应 MemberAddressDO 的 id 编号
45 */ 49 */
46 private Long addressId; 50 private Long addressId;
47 -  
48 /** 51 /**
49 - * 配送方式 52 + * 自提门店编号
50 * 53 *
51 - * 枚举 {@link DeliveryTypeEnum} 54 + * 对应 PickUpStoreDO 的 id 编号
52 */ 55 */
53 - private Integer deliveryType; 56 + private Long pickUpStoreId;
54 57
55 /** 58 /**
56 * 商品 SKU 数组 59 * 商品 SKU 数组
@@ -58,6 +61,30 @@ public class TradePriceCalculateReqBO { @@ -58,6 +61,30 @@ public class TradePriceCalculateReqBO {
58 @NotNull(message = "商品数组不能为空") 61 @NotNull(message = "商品数组不能为空")
59 private List<Item> items; 62 private List<Item> items;
60 63
  64 + // ========== 秒杀活动相关字段 ==========
  65 + /**
  66 + * 秒杀活动编号
  67 + */
  68 + private Long seckillActivityId;
  69 +
  70 + // ========== 拼团活动相关字段 ==========
  71 + // TODO @puhui999:是不是拼团记录的编号哈?
  72 + /**
  73 + * 拼团活动编号
  74 + */
  75 + private Long combinationActivityId;
  76 +
  77 + /**
  78 + * 拼团团长编号
  79 + */
  80 + private Long combinationHeadId;
  81 +
  82 + // ========== 砍价活动相关字段 ==========
  83 + /**
  84 + * 砍价活动编号
  85 + */
  86 + private Long bargainActivityId;
  87 +
61 /** 88 /**
62 * 商品 SKU 89 * 商品 SKU
63 */ 90 */
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/bo/TradePriceCalculateRespBO.java
@@ -49,6 +49,11 @@ public class TradePriceCalculateRespBO { @@ -49,6 +49,11 @@ public class TradePriceCalculateRespBO {
49 private Long couponId; 49 private Long couponId;
50 50
51 /** 51 /**
  52 + * 使用的积分
  53 + */
  54 + private Integer usePoint;
  55 +
  56 + /**
52 * 订单价格 57 * 订单价格
53 */ 58 */
54 @Data 59 @Data
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradeDeliveryPriceCalculator.java
@@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; @@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
2 2
3 import cn.hutool.core.collection.CollUtil; 3 import cn.hutool.core.collection.CollUtil;
4 import cn.hutool.core.lang.Assert; 4 import cn.hutool.core.lang.Assert;
  5 +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum;
5 import cn.iocoder.yudao.module.member.api.address.AddressApi; 6 import cn.iocoder.yudao.module.member.api.address.AddressApi;
6 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; 7 import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
  8 +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO;
7 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; 9 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum;
8 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; 10 import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum;
9 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; 11 import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService;
  12 +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService;
10 import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; 13 import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO;
11 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 14 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
12 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 15 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
@@ -22,8 +25,7 @@ import java.util.Set; @@ -22,8 +25,7 @@ import java.util.Set;
22 25
23 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; 26 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
24 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*; 27 import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
25 -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY;  
26 -import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.PRICE_CALCULATE_DELIVERY_PRICE_TEMPLATE_NOT_FOUND; 28 +import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
27 29
28 /** 30 /**
29 * 运费的 {@link TradePriceCalculator} 实现类 31 * 运费的 {@link TradePriceCalculator} 实现类
@@ -37,20 +39,41 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -37,20 +39,41 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
37 39
38 @Resource 40 @Resource
39 private AddressApi addressApi; 41 private AddressApi addressApi;
  42 +
  43 + @Resource
  44 + private DeliveryPickUpStoreService deliveryPickUpStoreService;
40 @Resource 45 @Resource
41 private DeliveryExpressTemplateService deliveryExpressTemplateService; 46 private DeliveryExpressTemplateService deliveryExpressTemplateService;
42 47
43 @Override 48 @Override
44 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) { 49 public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
45 - // TODO @芋艿:如果门店自提,需要校验是否开启;  
46 - // 1.1 判断配送方式  
47 - if (param.getDeliveryType() == null || DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) { 50 + if (param.getDeliveryType() == null) {
48 return; 51 return;
49 } 52 }
  53 + if (DeliveryTypeEnum.PICK_UP.getMode().equals(param.getDeliveryType())) {
  54 + calculateByPickUp(param, result);
  55 + } else if (DeliveryTypeEnum.EXPRESS.getMode().equals(param.getDeliveryType())) {
  56 + calculateExpress(param, result);
  57 + }
  58 + }
  59 +
  60 + private void calculateByPickUp(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  61 + if (param.getPickUpStoreId() == null) {
  62 + throw exception(PRICE_CALCULATE_DELIVERY_PRICE_PICK_UP_STORE_IS_EMPTY);
  63 + }
  64 + DeliveryPickUpStoreDO pickUpStore = deliveryPickUpStoreService.getDeliveryPickUpStore(param.getPickUpStoreId());
  65 + if (pickUpStore == null || CommonStatusEnum.DISABLE.getStatus().equals(pickUpStore.getStatus())) {
  66 + throw exception(PICK_UP_STORE_NOT_EXISTS);
  67 + }
  68 + }
  69 +
  70 + // ========= 快递发货 ==========
  71 +
  72 + private void calculateExpress(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  73 + // 1. 得到收件地址区域
50 if (param.getAddressId() == null) { 74 if (param.getAddressId() == null) {
51 - throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDR_IS_EMPTY); 75 + throw exception(PRICE_CALCULATE_DELIVERY_PRICE_USER_ADDRESS_IS_EMPTY);
52 } 76 }
53 - // 1.2 得到收件地址区域  
54 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); 77 AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId());
55 Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); 78 Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId());
56 79
@@ -89,8 +112,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { @@ -89,8 +112,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator {
89 for (OrderItem orderItem : orderItems) { 112 for (OrderItem orderItem : orderItems) {
90 totalCount += orderItem.getCount(); 113 totalCount += orderItem.getCount();
91 totalPrice += orderItem.getPayPrice(); 114 totalPrice += orderItem.getPayPrice();
92 - totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();  
93 - totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount(); 115 + if (orderItem.getWeight() != null) {
  116 + totalWeight += totalWeight + orderItem.getWeight() * orderItem.getCount();
  117 + }
  118 + if (orderItem.getVolume() != null) {
  119 + totalVolume += totalVolume + orderItem.getVolume() * orderItem.getCount();
  120 + }
94 } 121 }
95 // 优先判断是否包邮. 如果包邮不计算快递运费 122 // 优先判断是否包邮. 如果包邮不计算快递运费
96 if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight, 123 if (isExpressFree(templateBO.getChargeMode(), totalCount, totalWeight,
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePointUsePriceCalculator.java 0 → 100644
  1 +package cn.iocoder.yudao.module.trade.service.price.calculator;
  2 +
  3 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
  4 +import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
  5 +import lombok.extern.slf4j.Slf4j;
  6 +import org.springframework.core.annotation.Order;
  7 +import org.springframework.stereotype.Component;
  8 +
  9 +/**
  10 + * 使用积分的 {@link TradePriceCalculator} 实现类
  11 + *
  12 + * @author owen
  13 + */
  14 +@Component
  15 +@Order(TradePriceCalculator.ORDER_POINT_USE)
  16 +@Slf4j
  17 +public class TradePointUsePriceCalculator implements TradePriceCalculator {
  18 +
  19 + @Override
  20 + public void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result) {
  21 + // TODO 疯狂:待实现,嘿嘿;
  22 + if (param.getPointStatus()) {
  23 + result.setUsePoint(10);
  24 + } else {
  25 + result.setUsePoint(0);
  26 + }
  27 + }
  28 +
  29 +}
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculator.java
@@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; @@ -6,6 +6,9 @@ import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
6 /** 6 /**
7 * 价格计算的计算器接口 7 * 价格计算的计算器接口
8 * 8 *
  9 + * 优惠计算顺序:
  10 + * 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-53316">积分抵现、会员价、优惠券、粉丝专享价、满减送哪个优先计算?</>
  11 + *
9 * @author 芋道源码 12 * @author 芋道源码
10 */ 13 */
11 public interface TradePriceCalculator { 14 public interface TradePriceCalculator {
@@ -13,12 +16,13 @@ public interface TradePriceCalculator { @@ -13,12 +16,13 @@ public interface TradePriceCalculator {
13 int ORDER_DISCOUNT_ACTIVITY = 10; 16 int ORDER_DISCOUNT_ACTIVITY = 10;
14 int ORDER_REWARD_ACTIVITY = 20; 17 int ORDER_REWARD_ACTIVITY = 20;
15 int ORDER_COUPON = 30; 18 int ORDER_COUPON = 30;
  19 + int ORDER_POINT_USE = 40;
16 /** 20 /**
17 * 快递运费的计算 21 * 快递运费的计算
18 * 22 *
19 * 放在各种营销活动、优惠劵后面 TODO 23 * 放在各种营销活动、优惠劵后面 TODO
20 */ 24 */
21 - int ORDER_DELIVERY = 40; 25 + int ORDER_DELIVERY = 50;
22 26
23 void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result); 27 void calculate(TradePriceCalculateReqBO param, TradePriceCalculateRespBO result);
24 28
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/service/price/calculator/TradePriceCalculatorHelper.java
@@ -4,6 +4,7 @@ import cn.hutool.core.lang.Assert; @@ -4,6 +4,7 @@ import cn.hutool.core.lang.Assert;
4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; 4 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; 5 import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
6 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; 6 import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
  7 +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
7 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; 8 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
8 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; 9 import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
9 10
@@ -28,7 +29,7 @@ public class TradePriceCalculatorHelper { @@ -28,7 +29,7 @@ public class TradePriceCalculatorHelper {
28 List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) { 29 List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
29 // 创建 PriceCalculateRespDTO 对象 30 // 创建 PriceCalculateRespDTO 对象
30 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); 31 TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
31 - result.setType(param.getType()); 32 + result.setType(getOrderType(param));
32 result.setPromotions(new ArrayList<>()); 33 result.setPromotions(new ArrayList<>());
33 34
34 // 创建它的 OrderItem 属性 35 // 创建它的 OrderItem 属性
@@ -70,6 +71,23 @@ public class TradePriceCalculatorHelper { @@ -70,6 +71,23 @@ public class TradePriceCalculatorHelper {
70 } 71 }
71 72
72 /** 73 /**
  74 + * 计算订单类型
  75 + *
  76 + * @param param 计算参数
  77 + * @return 订单类型
  78 + */
  79 + private static Integer getOrderType(TradePriceCalculateReqBO param) {
  80 + if (param.getSeckillActivityId() != null) {
  81 + return TradeOrderTypeEnum.SECKILL.getType();
  82 + }
  83 + if (param.getCombinationActivityId() != null) {
  84 + return TradeOrderTypeEnum.COMBINATION.getType();
  85 + }
  86 + // TODO 砍价敬请期待
  87 + return TradeOrderTypeEnum.NORMAL.getType();
  88 + }
  89 +
  90 + /**
73 * 基于订单项,重新计算 price 总价 91 * 基于订单项,重新计算 price 总价
74 * 92 *
75 * @param result 计算结果 93 * @param result 计算结果
yudao-module-mall/yudao-module-trade-biz/src/test/java/cn/iocoder/yudao/module/trade/service/price/TradePriceServiceImplTest.java
@@ -45,7 +45,7 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest { @@ -45,7 +45,7 @@ public class TradePriceServiceImplTest extends BaseMockitoUnitTest {
45 public void testCalculatePrice() { 45 public void testCalculatePrice() {
46 // 准备参数 46 // 准备参数
47 TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO() 47 TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO()
48 - .setType(TradeOrderTypeEnum.NORMAL.getType()).setUserId(10L) 48 + .setUserId(10L)
49 .setCouponId(20L).setAddressId(30L) 49 .setCouponId(20L).setAddressId(30L)
50 .setItems(Arrays.asList( 50 .setItems(Arrays.asList(
51 new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true), 51 new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true),
yudao-module-member/yudao-module-member-api/pom.xml
@@ -21,6 +21,13 @@ @@ -21,6 +21,13 @@
21 <groupId>cn.iocoder.boot</groupId> 21 <groupId>cn.iocoder.boot</groupId>
22 <artifactId>yudao-common</artifactId> 22 <artifactId>yudao-common</artifactId>
23 </dependency> 23 </dependency>
  24 +
  25 + <!-- 参数校验 -->
  26 + <dependency>
  27 + <groupId>org.springframework.boot</groupId>
  28 + <artifactId>spring-boot-starter-validation</artifactId>
  29 + <optional>true</optional>
  30 + </dependency>
24 </dependencies> 31 </dependencies>
25 32
26 </project> 33 </project>
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApi.java
@@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.api.point; @@ -2,6 +2,8 @@ package cn.iocoder.yudao.module.member.api.point;
2 2
3 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; 3 import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum;
4 4
  5 +import javax.validation.constraints.Min;
  6 +
5 /** 7 /**
6 * 用户积分的 API 接口 8 * 用户积分的 API 接口
7 * 9 *
@@ -17,6 +19,18 @@ public interface MemberPointApi { @@ -17,6 +19,18 @@ public interface MemberPointApi {
17 * @param bizType 业务类型 {@link MemberPointBizTypeEnum} 19 * @param bizType 业务类型 {@link MemberPointBizTypeEnum}
18 * @param bizId 业务编号 20 * @param bizId 业务编号
19 */ 21 */
20 - void addPoint(Long userId, Integer point, Integer bizType, String bizId); 22 + void addPoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point,
  23 + Integer bizType, String bizId);
  24 +
  25 + /**
  26 + * 减少用户积分
  27 + *
  28 + * @param userId 用户编号
  29 + * @param point 积分
  30 + * @param bizType 业务类型 {@link MemberPointBizTypeEnum}
  31 + * @param bizId 业务编号
  32 + */
  33 + void reducePoint(Long userId, @Min(value = 1L, message = "积分必须是正数") Integer point,
  34 + Integer bizType, String bizId);
21 35
22 } 36 }
yudao-module-member/yudao-module-member-api/src/main/java/cn/iocoder/yudao/module/member/enums/point/MemberPointBizTypeEnum.java
@@ -17,8 +17,10 @@ import java.util.Objects; @@ -17,8 +17,10 @@ import java.util.Objects;
17 public enum MemberPointBizTypeEnum implements IntArrayValuable { 17 public enum MemberPointBizTypeEnum implements IntArrayValuable {
18 18
19 SIGN(1, "签到", "签到获得 {} 积分", true), 19 SIGN(1, "签到", "签到获得 {} 积分", true),
20 - ORDER_BUY(10, "订单消费", "下单获得 {} 积分", true),  
21 - ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false); // 退回积分 20 + ORDER_REWARD(10, "订单奖励", "下单获得 {} 积分", true),
  21 + ORDER_CANCEL(11, "订单取消", "退单获得 {} 积分", false), // 退回积分
  22 + ORDER_USE(12, "订单使用", "下单使用 {} 积分", false), // 扣减积分
  23 + ;
22 24
23 /** 25 /**
24 * 类型 26 * 类型
yudao-module-member/yudao-module-member-biz/src/main/java/cn/iocoder/yudao/module/member/api/point/MemberPointApiImpl.java
@@ -31,4 +31,13 @@ public class MemberPointApiImpl implements MemberPointApi { @@ -31,4 +31,13 @@ public class MemberPointApiImpl implements MemberPointApi {
31 memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId); 31 memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId);
32 } 32 }
33 33
  34 + @Override
  35 + public void reducePoint(Long userId, Integer point, Integer bizType, String bizId) {
  36 + MemberPointBizTypeEnum bizTypeEnum = MemberPointBizTypeEnum.getByType(bizType);
  37 + if (bizTypeEnum == null) {
  38 + throw exception(POINT_RECORD_BIZ_NOT_SUPPORT);
  39 + }
  40 + memberPointRecordService.createPointRecord(userId, point, bizTypeEnum, bizId);
  41 + }
  42 +
34 } 43 }