Commit 1b477aaa0d46985efff9f8891c762e54d7bf9c00
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
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 | 58 | |
| 59 | 59 | // ========== Price 相关 1011003000 ============ |
| 60 | 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 | 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 | 65 | // ========== 物流 Express 模块 1011004000 ========== |
| 65 | 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
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.http
yudao-module-mall/yudao-module-trade-biz/src/main/java/cn/iocoder/yudao/module/trade/controller/app/order/AppTradeOrderController.java
| 1 | 1 | package cn.iocoder.yudao.module.trade.controller.app.order; |
| 2 | 2 | |
| 3 | -import cn.hutool.core.map.MapUtil; | |
| 4 | 3 | import cn.iocoder.yudao.framework.common.pojo.CommonResult; |
| 5 | 4 | import cn.iocoder.yudao.framework.common.pojo.PageResult; |
| 6 | 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 | import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryExpressDO; |
| 13 | 12 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderDO; |
| 14 | 13 | import cn.iocoder.yudao.module.trade.dal.dataobject.order.TradeOrderItemDO; |
| 15 | -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderOperateTypeEnum; | |
| 16 | 14 | import cn.iocoder.yudao.module.trade.enums.order.TradeOrderStatusEnum; |
| 17 | 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 | 16 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressService; |
| 21 | 17 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderQueryService; |
| 22 | 18 | import cn.iocoder.yudao.module.trade.service.order.TradeOrderUpdateService; |
| ... | ... | @@ -65,10 +61,7 @@ public class AppTradeOrderController { |
| 65 | 61 | @PostMapping("/create") |
| 66 | 62 | @Operation(summary = "创建订单") |
| 67 | 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 | 65 | TradeOrderDO order = tradeOrderUpdateService.createOrder(getLoginUserId(), getClientIP(), createReqVO); |
| 73 | 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 | 1 | package cn.iocoder.yudao.module.trade.controller.app.order.vo; |
| 2 | 2 | |
| 3 | +import com.fasterxml.jackson.annotation.JsonIgnore; | |
| 3 | 4 | import io.swagger.v3.oas.annotations.media.Schema; |
| 4 | 5 | import lombok.Data; |
| 5 | 6 | |
| 7 | +import javax.validation.constraints.AssertTrue; | |
| 8 | + | |
| 6 | 9 | @Schema(description = "用户 App - 交易订单创建 Request VO") |
| 7 | 10 | @Data |
| 8 | 11 | public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { |
| ... | ... | @@ -10,4 +13,10 @@ public class AppTradeOrderCreateReqVO extends AppTradeOrderSettlementReqVO { |
| 10 | 13 | @Schema(description = "备注", example = "这个是我的订单哟") |
| 11 | 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 | 30 | private Boolean pointStatus; |
| 31 | 31 | |
| 32 | 32 | // ========== 配送相关相关字段 ========== |
| 33 | - @Schema(description = "配送方式", required = true, example = "1") | |
| 33 | + @Schema(description = "配送方式", example = "1") | |
| 34 | 34 | @InEnum(value = DeliveryTypeEnum.class, message = "配送方式不正确") |
| 35 | 35 | private Integer deliveryType; |
| 36 | 36 | |
| ... | ... | @@ -62,6 +62,8 @@ public class AppTradeOrderSettlementReqVO { |
| 62 | 62 | @Schema(description = "砍价活动编号", example = "123") |
| 63 | 63 | private Long bargainActivityId; |
| 64 | 64 | |
| 65 | + // TODO @puhui999:可以写个参数校验,如果 seckillActivityId 或 combinationActivityId 或 combinationHeadId 的情况,items 应该只有一个 | |
| 66 | + | |
| 65 | 67 | @Data |
| 66 | 68 | @Schema(description = "用户 App - 商品项") |
| 67 | 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 | 56 | |
| 57 | 57 | @Mappings({ |
| 58 | 58 | @Mapping(target = "id", ignore = true), |
| 59 | + @Mapping(source = "userId", target = "userId"), | |
| 59 | 60 | @Mapping(source = "createReqVO.couponId", target = "couponId"), |
| 60 | 61 | @Mapping(target = "remark", ignore = true), |
| 61 | 62 | @Mapping(source = "createReqVO.remark", target = "userRemark"), |
| ... | ... | @@ -89,23 +90,16 @@ public interface TradeOrderConvert { |
| 89 | 90 | TradeOrderItemDO convert(TradePriceCalculateRespBO.OrderItem item); |
| 90 | 91 | |
| 91 | 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 | 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 | 103 | default PayOrderCreateReqDTO convert(TradeOrderDO order, List<TradeOrderItemDO> orderItems, |
| 110 | 104 | TradePriceCalculateRespBO calculateRespBO, TradeOrderProperties orderProperties) { |
| 111 | 105 | PayOrderCreateReqDTO createReqDTO = new PayOrderCreateReqDTO() |
| ... | ... | @@ -218,8 +212,9 @@ public interface TradeOrderConvert { |
| 218 | 212 | |
| 219 | 213 | default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO, |
| 220 | 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 | 218 | .setItems(new ArrayList<>(settlementReqVO.getItems().size())); |
| 224 | 219 | // 商品项的构建 |
| 225 | 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 | 251 | * 对应 taobao 的 trade.coupon_fee 字段 |
| 252 | 252 | */ |
| 253 | 253 | private Integer couponPrice; |
| 254 | - // TODO 芋艿:需要记录使用的积分; | |
| 254 | + /** | |
| 255 | + * 使用的积分 | |
| 256 | + */ | |
| 257 | + private Integer usePoint; | |
| 255 | 258 | /** |
| 256 | 259 | * 积分抵扣的金额,单位:分 |
| 257 | 260 | * |
| 258 | 261 | * 对应 taobao 的 trade.point_fee 字段 |
| 259 | 262 | */ |
| 260 | 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 | 47 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 48 | 48 | import cn.iocoder.yudao.module.trade.enums.order.*; |
| 49 | 49 | import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties; |
| 50 | +import cn.iocoder.yudao.module.trade.framework.order.core.annotations.TradeOrderLog; | |
| 50 | 51 | import cn.iocoder.yudao.module.trade.service.brokerage.bo.BrokerageAddReqBO; |
| 51 | 52 | import cn.iocoder.yudao.module.trade.service.brokerage.record.BrokerageRecordService; |
| 52 | 53 | import cn.iocoder.yudao.module.trade.service.cart.CartService; |
| ... | ... | @@ -182,32 +183,24 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 182 | 183 | |
| 183 | 184 | @Override |
| 184 | 185 | @Transactional(rollbackFor = Exception.class) |
| 186 | + @TradeOrderLog(operateType = TradeOrderOperateTypeEnum.MEMBER_CREATE) | |
| 185 | 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 | 189 | TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, createReqVO); |
| 197 | 190 | |
| 191 | + // 1. 订单创建前的逻辑 | |
| 192 | + beforeCreateTradeOrder(userId, createReqVO, calculateRespBO); | |
| 193 | + | |
| 198 | 194 | // 2.1 插入 TradeOrderDO 订单 |
| 199 | 195 | TradeOrderDO order = createTradeOrder(userId, userIp, createReqVO, calculateRespBO); |
| 200 | 196 | // 2.2 插入 TradeOrderItemDO 订单项 |
| 201 | 197 | List<TradeOrderItemDO> orderItems = createTradeOrderItems(order, calculateRespBO); |
| 202 | 198 | |
| 203 | - // 3. 订单创建完后的逻辑 | |
| 199 | + // 3. 订单创建后的逻辑 | |
| 204 | 200 | afterCreateTradeOrder(userId, createReqVO, order, orderItems, calculateRespBO); |
| 205 | - | |
| 206 | - // TODO @LeeYan9: 是可以思考下, 订单的营销优惠记录, 应该记录在哪里, 微信讨论起来! | |
| 207 | 201 | return order; |
| 208 | 202 | } |
| 209 | 203 | |
| 210 | - | |
| 211 | 204 | // TODO @puhui999:订单超时,自动取消; |
| 212 | 205 | |
| 213 | 206 | /** |
| ... | ... | @@ -234,9 +227,8 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 234 | 227 | address = validateAddress(userId, createReqVO.getAddressId()); |
| 235 | 228 | } |
| 236 | 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 | 232 | order.setStatus(TradeOrderStatusEnum.UNPAID.getStatus()); |
| 241 | 233 | order.setRefundStatus(TradeOrderRefundStatusEnum.NONE.getStatus()); |
| 242 | 234 | order.setProductCount(getSumValue(calculateRespBO.getItems(), TradePriceCalculateRespBO.OrderItem::getCount, Integer::sum)); |
| ... | ... | @@ -252,71 +244,80 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 252 | 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 | 280 | * @param userId 用户编号 |
| 284 | 281 | * @param createReqVO 创建订单请求 |
| 285 | - * @param tradeOrderDO 交易订单 | |
| 282 | + * @param order 交易订单 | |
| 286 | 283 | * @param calculateRespBO 订单价格计算结果 |
| 287 | 284 | */ |
| 288 | 285 | private void afterCreateTradeOrder(Long userId, AppTradeOrderCreateReqVO createReqVO, |
| 289 | - TradeOrderDO tradeOrderDO, List<TradeOrderItemDO> orderItems, | |
| 286 | + TradeOrderDO order, List<TradeOrderItemDO> orderItems, | |
| 290 | 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 | 295 | if (createReqVO.getCouponId() != null) { |
| 299 | 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 | 308 | Set<Long> cartIds = convertSet(createReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId); |
| 308 | 309 | if (CollUtil.isNotEmpty(cartIds)) { |
| 309 | 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 | 322 | PayOrderCreateReqDTO payOrderCreateReqDTO = TradeOrderConvert.INSTANCE.convert( |
| 322 | 323 | order, orderItems, calculateRespBO, tradeOrderProperties); |
| ... | ... | @@ -344,6 +345,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 344 | 345 | } |
| 345 | 346 | // 校验活动 |
| 346 | 347 | // 1、拼团活动 |
| 348 | + // TODO @puhui999:这块也抽象到 handler 里 | |
| 347 | 349 | if (Objects.equals(TradeOrderTypeEnum.COMBINATION.getType(), order.getType())) { |
| 348 | 350 | // 更新拼团状态 TODO puhui999:订单支付失败或订单支付过期删除这条拼团记录 |
| 349 | 351 | combinationRecordApi.updateRecordStatusToInProgress(order.getUserId(), order.getId(), LocalDateTime.now()); |
| ... | ... | @@ -741,6 +743,7 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 741 | 743 | couponApi.returnUsedCoupon(order.getCouponId()); |
| 742 | 744 | |
| 743 | 745 | // 4.回滚积分:积分是支付成功后才增加的吧? 回复:每个项目不同,目前看下来,确认收货貌似更合适,我再看看其它项目的业务选择; |
| 746 | + // TODO @疯狂:有赞是可配置(支付 or 确认收货),我们按照支付好列;然后这里的退积分,指的是下单时的积分抵扣。 | |
| 744 | 747 | |
| 745 | 748 | // TODO 芋艿:OrderLog |
| 746 | 749 | |
| ... | ... | @@ -773,17 +776,18 @@ public class TradeOrderUpdateServiceImpl implements TradeOrderUpdateService { |
| 773 | 776 | |
| 774 | 777 | @Async |
| 775 | 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 | 781 | memberPointApi.addPoint(userId, payPrice, bizType, String.valueOf(orderId)); |
| 778 | 782 | } |
| 779 | 783 | |
| 780 | 784 | @Async |
| 781 | 785 | protected void reduceUserPointAsync(Long userId, Integer refundPrice, Long afterSaleId) { |
| 786 | + // TODO @疯狂:退款时,按照金额比例,退还积分;https://help.youzan.com/displaylist/detail_4_4-1-49185 | |
| 782 | 787 | int bizType = MemberPointBizTypeEnum.ORDER_CANCEL.getType(); |
| 783 | 788 | memberPointApi.addPoint(userId, -refundPrice, bizType, String.valueOf(afterSaleId)); |
| 784 | 789 | } |
| 785 | 790 | |
| 786 | - | |
| 787 | 791 | @Async |
| 788 | 792 | protected void addBrokerageAsync(Long userId, Long orderId) { |
| 789 | 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 | 37 | |
| 38 | 38 | @Override |
| 39 | 39 | public void afterOrderCreate(TradeAfterOrderCreateReqBO reqBO) { |
| 40 | + // TODO @puhui999:需要判断下; | |
| 41 | + if (true) { | |
| 42 | + return; | |
| 43 | + } | |
| 44 | + | |
| 40 | 45 | // 创建砍价记录 |
| 41 | 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 | 1 | package cn.iocoder.yudao.module.trade.service.price.bo; |
| 2 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 4 | -import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 5 | 4 | import lombok.Data; |
| 6 | 5 | |
| 7 | 6 | import javax.validation.Valid; |
| ... | ... | @@ -18,13 +17,6 @@ import java.util.List; |
| 18 | 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 | 22 | * 对应 MemberUserDO 的 id 编号 |
| ... | ... | @@ -37,20 +29,31 @@ public class TradePriceCalculateReqBO { |
| 37 | 29 | * 对应 CouponDO 的 id 编号 |
| 38 | 30 | */ |
| 39 | 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 | 48 | * 对应 MemberAddressDO 的 id 编号 |
| 45 | 49 | */ |
| 46 | 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 | 59 | * 商品 SKU 数组 |
| ... | ... | @@ -58,6 +61,30 @@ public class TradePriceCalculateReqBO { |
| 58 | 61 | @NotNull(message = "商品数组不能为空") |
| 59 | 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 | 89 | * 商品 SKU |
| 63 | 90 | */ | ... | ... |
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/TradeDeliveryPriceCalculator.java
| ... | ... | @@ -2,11 +2,14 @@ package cn.iocoder.yudao.module.trade.service.price.calculator; |
| 2 | 2 | |
| 3 | 3 | import cn.hutool.core.collection.CollUtil; |
| 4 | 4 | import cn.hutool.core.lang.Assert; |
| 5 | +import cn.iocoder.yudao.framework.common.enums.CommonStatusEnum; | |
| 5 | 6 | import cn.iocoder.yudao.module.member.api.address.AddressApi; |
| 6 | 7 | import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO; |
| 8 | +import cn.iocoder.yudao.module.trade.dal.dataobject.delivery.DeliveryPickUpStoreDO; | |
| 7 | 9 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryExpressChargeModeEnum; |
| 8 | 10 | import cn.iocoder.yudao.module.trade.enums.delivery.DeliveryTypeEnum; |
| 9 | 11 | import cn.iocoder.yudao.module.trade.service.delivery.DeliveryExpressTemplateService; |
| 12 | +import cn.iocoder.yudao.module.trade.service.delivery.DeliveryPickUpStoreService; | |
| 10 | 13 | import cn.iocoder.yudao.module.trade.service.delivery.bo.DeliveryExpressTemplateRespBO; |
| 11 | 14 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 12 | 15 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| ... | ... | @@ -22,8 +25,7 @@ import java.util.Set; |
| 22 | 25 | |
| 23 | 26 | import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; |
| 24 | 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 | 31 | * 运费的 {@link TradePriceCalculator} 实现类 |
| ... | ... | @@ -37,20 +39,41 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 37 | 39 | |
| 38 | 40 | @Resource |
| 39 | 41 | private AddressApi addressApi; |
| 42 | + | |
| 43 | + @Resource | |
| 44 | + private DeliveryPickUpStoreService deliveryPickUpStoreService; | |
| 40 | 45 | @Resource |
| 41 | 46 | private DeliveryExpressTemplateService deliveryExpressTemplateService; |
| 42 | 47 | |
| 43 | 48 | @Override |
| 44 | 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 | 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 | 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 | 77 | AddressRespDTO address = addressApi.getAddress(param.getAddressId(), param.getUserId()); |
| 55 | 78 | Assert.notNull(address, "收件人({})的地址,不能为空", param.getUserId()); |
| 56 | 79 | |
| ... | ... | @@ -89,8 +112,12 @@ public class TradeDeliveryPriceCalculator implements TradePriceCalculator { |
| 89 | 112 | for (OrderItem orderItem : orderItems) { |
| 90 | 113 | totalCount += orderItem.getCount(); |
| 91 | 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 | 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 | /** |
| 7 | 7 | * 价格计算的计算器接口 |
| 8 | 8 | * |
| 9 | + * 优惠计算顺序: | |
| 10 | + * 1. <a href="https://help.youzan.com/displaylist/detail_4_4-1-53316">积分抵现、会员价、优惠券、粉丝专享价、满减送哪个优先计算?</> | |
| 11 | + * | |
| 9 | 12 | * @author 芋道源码 |
| 10 | 13 | */ |
| 11 | 14 | public interface TradePriceCalculator { |
| ... | ... | @@ -13,12 +16,13 @@ public interface TradePriceCalculator { |
| 13 | 16 | int ORDER_DISCOUNT_ACTIVITY = 10; |
| 14 | 17 | int ORDER_REWARD_ACTIVITY = 20; |
| 15 | 18 | int ORDER_COUPON = 30; |
| 19 | + int ORDER_POINT_USE = 40; | |
| 16 | 20 | /** |
| 17 | 21 | * 快递运费的计算 |
| 18 | 22 | * |
| 19 | 23 | * 放在各种营销活动、优惠劵后面 TODO |
| 20 | 24 | */ |
| 21 | - int ORDER_DELIVERY = 40; | |
| 25 | + int ORDER_DELIVERY = 50; | |
| 22 | 26 | |
| 23 | 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 | 4 | import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils; |
| 5 | 5 | import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO; |
| 6 | 6 | import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO; |
| 7 | +import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum; | |
| 7 | 8 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO; |
| 8 | 9 | import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO; |
| 9 | 10 | |
| ... | ... | @@ -28,7 +29,7 @@ public class TradePriceCalculatorHelper { |
| 28 | 29 | List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) { |
| 29 | 30 | // 创建 PriceCalculateRespDTO 对象 |
| 30 | 31 | TradePriceCalculateRespBO result = new TradePriceCalculateRespBO(); |
| 31 | - result.setType(param.getType()); | |
| 32 | + result.setType(getOrderType(param)); | |
| 32 | 33 | result.setPromotions(new ArrayList<>()); |
| 33 | 34 | |
| 34 | 35 | // 创建它的 OrderItem 属性 |
| ... | ... | @@ -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 | 91 | * 基于订单项,重新计算 price 总价 |
| 74 | 92 | * |
| 75 | 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 | 45 | public void testCalculatePrice() { |
| 46 | 46 | // 准备参数 |
| 47 | 47 | TradePriceCalculateReqBO calculateReqBO = new TradePriceCalculateReqBO() |
| 48 | - .setType(TradeOrderTypeEnum.NORMAL.getType()).setUserId(10L) | |
| 48 | + .setUserId(10L) | |
| 49 | 49 | .setCouponId(20L).setAddressId(30L) |
| 50 | 50 | .setItems(Arrays.asList( |
| 51 | 51 | new TradePriceCalculateReqBO.Item().setSkuId(100L).setCount(1).setSelected(true), | ... | ... |
yudao-module-member/yudao-module-member-api/pom.xml
| ... | ... | @@ -21,6 +21,13 @@ |
| 21 | 21 | <groupId>cn.iocoder.boot</groupId> |
| 22 | 22 | <artifactId>yudao-common</artifactId> |
| 23 | 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 | 31 | </dependencies> |
| 25 | 32 | |
| 26 | 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 | 2 | |
| 3 | 3 | import cn.iocoder.yudao.module.member.enums.point.MemberPointBizTypeEnum; |
| 4 | 4 | |
| 5 | +import javax.validation.constraints.Min; | |
| 6 | + | |
| 5 | 7 | /** |
| 6 | 8 | * 用户积分的 API 接口 |
| 7 | 9 | * |
| ... | ... | @@ -17,6 +19,18 @@ public interface MemberPointApi { |
| 17 | 19 | * @param bizType 业务类型 {@link MemberPointBizTypeEnum} |
| 18 | 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 | 17 | public enum MemberPointBizTypeEnum implements IntArrayValuable { |
| 18 | 18 | |
| 19 | 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 | 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 | } | ... | ... |