mirror of
https://gitee.com/huangge1199_admin/vue-pro.git
synced 2024-11-25 16:51:52 +08:00
mall + trade:获得订单结算信息,接入支付接口
This commit is contained in:
parent
5de8fa2e42
commit
2a9a869e01
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.product.api.sku.dto;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
@ -25,7 +26,7 @@ public class ProductSkuRespDTO {
|
||||
/**
|
||||
* 属性数组
|
||||
*/
|
||||
private List<Property> properties;
|
||||
private List<ProductPropertyValueDetailRespDTO> properties;
|
||||
/**
|
||||
* 销售价格,单位:分
|
||||
*/
|
||||
@ -63,30 +64,4 @@ public class ProductSkuRespDTO {
|
||||
*/
|
||||
private Double volume;
|
||||
|
||||
/**
|
||||
* 商品属性
|
||||
*/
|
||||
@Data
|
||||
public static class Property {
|
||||
|
||||
/**
|
||||
* 属性编号
|
||||
*/
|
||||
private Long propertyId;
|
||||
/**
|
||||
* 属性名字
|
||||
*/
|
||||
private String propertyName;
|
||||
|
||||
/**
|
||||
* 属性值编号
|
||||
*/
|
||||
private Long valueId;
|
||||
/**
|
||||
* 属性值名字
|
||||
*/
|
||||
private String valueName;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -18,6 +18,9 @@ public class AppProductSpuDetailRespVO {
|
||||
@Schema(description = "商品名称", required = true, example = "芋道")
|
||||
private String name;
|
||||
|
||||
@Schema(description = "商品简介", required = true, example = "我是一个快乐简介")
|
||||
private String introduction;
|
||||
|
||||
@Schema(description = "商品详情", required = true, example = "我是商品描述")
|
||||
private String description;
|
||||
|
||||
|
@ -93,7 +93,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
assertNull(promotion.getId());
|
||||
assertEquals(promotion.getName(), "会员折扣");
|
||||
assertEquals(promotion.getType(), PromotionTypeEnum.MEMBER.getType());
|
||||
assertEquals(promotion.getLevel(), PromotionLevelEnum.SKU.getLevel());
|
||||
assertEquals(promotion.getTotalPrice(), 200);
|
||||
assertEquals(promotion.getDiscountPrice(), 20);
|
||||
assertTrue(promotion.getMatch());
|
||||
@ -264,7 +263,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
@ -283,7 +281,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
assertEquals(promotion02.getId(), 2000L);
|
||||
assertEquals(promotion02.getName(), "活动 2000 号");
|
||||
assertEquals(promotion02.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion02.getLevel(), PromotionLevelEnum.ORDER.getLevel());
|
||||
assertEquals(promotion02.getTotalPrice(), 120);
|
||||
assertEquals(promotion02.getDiscountPrice(), 60);
|
||||
assertTrue(promotion02.getMatch());
|
||||
@ -352,7 +349,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
assertEquals(promotion01.getId(), 1000L);
|
||||
assertEquals(promotion01.getName(), "活动 1000 号");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.REWARD_ACTIVITY.getType());
|
||||
assertEquals(promotion01.getLevel(), PromotionLevelEnum.ORDER.getLevel());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 0);
|
||||
assertFalse(promotion01.getMatch());
|
||||
@ -434,7 +430,6 @@ public class PriceServiceTest extends BaseMockitoUnitTest {
|
||||
assertEquals(promotion01.getId(), 1024L);
|
||||
assertEquals(promotion01.getName(), "程序员节");
|
||||
assertEquals(promotion01.getType(), PromotionTypeEnum.COUPON.getType());
|
||||
assertEquals(promotion01.getLevel(), PromotionLevelEnum.COUPON.getLevel());
|
||||
assertEquals(promotion01.getTotalPrice(), 350);
|
||||
assertEquals(promotion01.getDiscountPrice(), 70);
|
||||
assertTrue(promotion01.getMatch());
|
||||
|
@ -1,5 +1,10 @@
|
||||
### /trade-order/settlement 获得订单结算信息
|
||||
GET {{appApi}}/trade/order/settlement?cartIds=1
|
||||
### /trade-order/settlement 获得订单结算信息(基于商品)
|
||||
GET {{appApi}}/trade/order/settlement?type=0&items[0].skuId=1&items[0].count=2&items[1].skuId=2&items[1].count=3&couponId=1
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
### /trade-order/settlement 获得订单结算信息(基于购物车)
|
||||
GET {{appApi}}/trade/order/settlement?type=0&items[0].cartId=50&couponId=1
|
||||
Authorization: Bearer {{appToken}}
|
||||
tenant-id: {{appTenentId}}
|
||||
|
||||
|
@ -6,7 +6,6 @@ import cn.iocoder.yudao.framework.security.core.annotations.PreAuthenticated;
|
||||
import cn.iocoder.yudao.module.pay.api.notify.dto.PayOrderNotifyReqDTO;
|
||||
import cn.iocoder.yudao.module.product.api.property.ProductPropertyValueApi;
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
@ -25,7 +24,6 @@ import org.springframework.web.bind.annotation.*;
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.validation.Valid;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
@ -53,79 +51,9 @@ public class AppTradeOrderController {
|
||||
@GetMapping("/settlement")
|
||||
@Operation(summary = "获得订单结算信息")
|
||||
@PreAuthenticated
|
||||
public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(
|
||||
@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
if (true) {
|
||||
public CommonResult<AppTradeOrderSettlementRespVO> settlementOrder(@Valid AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
return success(tradeOrderService.settlementOrder(getLoginUserId(), settlementReqVO));
|
||||
}
|
||||
// return success(tradeOrderService.getOrderConfirmCreateInfo(UserSecurityContextHolder.getUserId(), skuId, quantity, couponCardId));
|
||||
AppTradeOrderSettlementRespVO settlement = new AppTradeOrderSettlementRespVO();
|
||||
|
||||
AppTradeOrderSettlementRespVO.Price price = new AppTradeOrderSettlementRespVO.Price();
|
||||
price.setTotalPrice(1000);
|
||||
price.setDeliveryPrice(200);
|
||||
price.setCouponPrice(100);
|
||||
price.setPointPrice(50);
|
||||
price.setPayPrice(950);
|
||||
|
||||
List<AppTradeOrderSettlementRespVO.Item> skus = new ArrayList<>();
|
||||
|
||||
AppTradeOrderSettlementRespVO.Item item1 = new AppTradeOrderSettlementRespVO.Item();
|
||||
item1.setCartId(1L);
|
||||
item1.setSpuId(2048L);
|
||||
item1.setSpuName("Apple iPhone 12");
|
||||
item1.setSkuId(1024);
|
||||
item1.setPrice(500);
|
||||
item1.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");
|
||||
item1.setCount(2);
|
||||
List<AppProductPropertyValueDetailRespVO> properties1 = new ArrayList<>();
|
||||
AppProductPropertyValueDetailRespVO property1 = new AppProductPropertyValueDetailRespVO();
|
||||
property1.setPropertyId(1L);
|
||||
property1.setPropertyName("尺寸");
|
||||
property1.setValueId(2L);
|
||||
property1.setValueName("大");
|
||||
properties1.add(property1);
|
||||
item1.setProperties(properties1);
|
||||
|
||||
AppTradeOrderSettlementRespVO.Item item2 = new AppTradeOrderSettlementRespVO.Item();
|
||||
item2.setCartId(2L);
|
||||
item2.setSpuId(3072L);
|
||||
item2.setSpuName("Samsung Galaxy S21");
|
||||
item2.setSkuId(2048);
|
||||
item2.setPrice(800);
|
||||
item2.setPicUrl("https://pro.crmeb.net/uploads/attach/2022/10/12/0c56f9abb80d2775fc1e80dbe4f8826a.jpg");
|
||||
item2.setCount(1);
|
||||
List<AppProductPropertyValueDetailRespVO> properties2 = new ArrayList<>();
|
||||
AppProductPropertyValueDetailRespVO property2 = new AppProductPropertyValueDetailRespVO();
|
||||
property2.setPropertyId(10L);
|
||||
property2.setPropertyName("颜色");
|
||||
property2.setValueId(20L);
|
||||
property2.setValueName("白色");
|
||||
properties2.add(property2);
|
||||
item2.setProperties(properties2);
|
||||
|
||||
skus.add(item1);
|
||||
skus.add(item2);
|
||||
|
||||
settlement.setItems(skus);
|
||||
settlement.setPrice(price);
|
||||
|
||||
AppTradeOrderSettlementRespVO.Address address = new AppTradeOrderSettlementRespVO.Address();
|
||||
address.setId(1L);
|
||||
address.setName("John");
|
||||
address.setMobile("18888888888");
|
||||
address.setProvinceId(1L);
|
||||
address.setProvinceName("Beijing");
|
||||
address.setCityId(1L);
|
||||
address.setCityName("Beijing");
|
||||
address.setDistrictId(1L);
|
||||
address.setDistrictName("Chaoyang Distripct");
|
||||
address.setDetailAddress("No. 10, Xinzhong Street, Chaoyang District");
|
||||
address.setDefaulted(true);
|
||||
settlement.setAddress(address);
|
||||
|
||||
return success(settlement);
|
||||
}
|
||||
|
||||
@PostMapping("/create")
|
||||
@Operation(summary = "创建订单")
|
||||
|
@ -1,23 +1,60 @@
|
||||
package cn.iocoder.yudao.module.trade.controller.app.order.vo;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.validation.InEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import com.fasterxml.jackson.annotation.JsonIgnore;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.Valid;
|
||||
import javax.validation.constraints.AssertTrue;
|
||||
import javax.validation.constraints.Min;
|
||||
import javax.validation.constraints.NotNull;
|
||||
import java.util.List;
|
||||
|
||||
@Schema(description = "用户 App - 交易订单结算 Request VO")
|
||||
@Data
|
||||
public class AppTradeOrderSettlementReqVO {
|
||||
|
||||
@NotNull(message = "交易类型不能为空")
|
||||
@InEnum(value = TradeOrderTypeEnum.class, message = "交易类型必须是 {value}")
|
||||
private Integer type;
|
||||
|
||||
@Schema(description = "商品项数组", required = true)
|
||||
@NotNull(message = "商品不能为空")
|
||||
private List<Item> items;
|
||||
|
||||
@Schema(description = "收件地址编号", example = "1")
|
||||
private Long addressId;
|
||||
|
||||
@Schema(description = "优惠劵编号", example = "1024")
|
||||
private Long couponId;
|
||||
|
||||
@Schema(description = "购物车项的编号数组", required = true, example = "true")
|
||||
@NotEmpty(message = "购物车项不能为空")
|
||||
private List<Long> cartIds;
|
||||
@Data
|
||||
@Schema(description = "用户 App - 商品项")
|
||||
@Valid
|
||||
public static class Item {
|
||||
|
||||
@Schema(description = "商品 SKU 编号", example = "2048")
|
||||
private Long skuId;
|
||||
@Schema(description = "购买数量", example = "1")
|
||||
@Min(value = 1, message = "购买数量最小值为 {value}")
|
||||
private Integer count;
|
||||
|
||||
@Schema(description = "购物车项的编号", example = "1024")
|
||||
private Long cartId;
|
||||
|
||||
@AssertTrue(message = "商品不正确")
|
||||
@JsonIgnore
|
||||
public boolean isValid() {
|
||||
// 组合一:skuId + count 使用商品 SKU
|
||||
if (skuId != null && count != null) {
|
||||
return true;
|
||||
}
|
||||
// 组合二:cartId 使用购物车项
|
||||
return cartId != null;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -91,17 +91,17 @@ public class AppTradeOrderSettlementRespVO {
|
||||
private String mobile;
|
||||
|
||||
@Schema(description = "省份编号", required = true, example = "1")
|
||||
private Long provinceId;
|
||||
private Integer provinceId;
|
||||
@Schema(description = "省份名字", required = true, example = "北京")
|
||||
private String provinceName;
|
||||
|
||||
@Schema(description = "城市编号", required = true, example = "1")
|
||||
private Long cityId;
|
||||
private Integer cityId;
|
||||
@Schema(description = "城市名字", required = true, example = "北京")
|
||||
private String cityName;
|
||||
|
||||
@Schema(description = "地区编号", required = true, example = "1")
|
||||
private Long districtId;
|
||||
private Integer districtId;
|
||||
@Schema(description = "地区名字", required = true, example = "朝阳区")
|
||||
private String districtName;
|
||||
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.convert.order;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.framework.ip.core.Area;
|
||||
import cn.iocoder.yudao.framework.ip.core.utils.AreaUtils;
|
||||
import cn.iocoder.yudao.module.member.api.address.dto.AddressRespDTO;
|
||||
import cn.iocoder.yudao.module.member.api.user.dto.MemberUserRespDTO;
|
||||
@ -18,14 +19,15 @@ import cn.iocoder.yudao.module.trade.controller.admin.base.product.property.Prod
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.admin.order.vo.TradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.base.property.AppProductPropertyValueDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderCreateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderDetailRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.*;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.item.AppTradeOrderItemRespVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
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.enums.order.TradeOrderItemAfterSaleStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import org.mapstruct.Mapper;
|
||||
import org.mapstruct.Mapping;
|
||||
import org.mapstruct.Mappings;
|
||||
@ -241,4 +243,45 @@ public interface TradeOrderConvert {
|
||||
|
||||
AppTradeOrderItemRespVO convert03(TradeOrderItemDO bean);
|
||||
|
||||
default TradePriceCalculateReqBO convert(Long userId, AppTradeOrderSettlementReqVO settlementReqVO,
|
||||
List<TradeCartDO> cartList) {
|
||||
TradePriceCalculateReqBO reqBO = new TradePriceCalculateReqBO();
|
||||
reqBO.setUserId(userId).setType(settlementReqVO.getType())
|
||||
.setCouponId(settlementReqVO.getCouponId()).setAddressId(settlementReqVO.getAddressId())
|
||||
.setItems(new ArrayList<>(settlementReqVO.getItems().size()));
|
||||
// 商品项的构建
|
||||
Map<Long, TradeCartDO> cartMap = convertMap(cartList, TradeCartDO::getId);
|
||||
for (AppTradeOrderSettlementReqVO.Item item : settlementReqVO.getItems()) {
|
||||
// 情况一:skuId + count
|
||||
if (item.getSkuId() != null) {
|
||||
reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(item.getSkuId()).setCount(item.getCount())
|
||||
.setSelected(true)); // true 的原因,下单一定选中
|
||||
continue;
|
||||
}
|
||||
// 情况二:cartId
|
||||
TradeCartDO cart = cartMap.get(item.getCartId());
|
||||
if (cart == null) {
|
||||
continue;
|
||||
}
|
||||
reqBO.getItems().add(new TradePriceCalculateReqBO.Item().setSkuId(cart.getSkuId()).setCount(cart.getCount())
|
||||
.setCartId(item.getCartId()).setSelected(true)); // true 的原因,下单一定选中
|
||||
}
|
||||
return reqBO;
|
||||
}
|
||||
|
||||
default AppTradeOrderSettlementRespVO convert(TradePriceCalculateRespBO calculate, AddressRespDTO address) {
|
||||
AppTradeOrderSettlementRespVO respVO = convert0(calculate, address);
|
||||
if (address != null) {
|
||||
Area area = AreaUtils.getArea(address.getAreaId());
|
||||
respVO.getAddress().setDistrictId(area.getId());
|
||||
respVO.getAddress().setDistrictName(area.getName());
|
||||
respVO.getAddress().setCityId(area.getParent().getId());
|
||||
respVO.getAddress().setCityName(area.getParent().getName());
|
||||
respVO.getAddress().setProvinceId(area.getParent().getParent().getId());
|
||||
respVO.getAddress().setProvinceName(area.getParent().getParent().getName());
|
||||
}
|
||||
return respVO;
|
||||
}
|
||||
AppTradeOrderSettlementRespVO convert0(TradePriceCalculateRespBO calculate, AddressRespDTO address);
|
||||
|
||||
}
|
||||
|
@ -12,6 +12,7 @@ import org.apache.ibatis.annotations.Mapper;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
@Mapper
|
||||
public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
|
||||
@ -70,4 +71,10 @@ public interface TradeCartMapper extends BaseMapperX<TradeCartDO> {
|
||||
update(updateObject, new LambdaQueryWrapper<TradeCartDO>().in(TradeCartDO::getId, ids));
|
||||
}
|
||||
|
||||
default List<TradeCartDO> selectListByUserId(Long userId, Set<Long> ids) {
|
||||
return selectList(new LambdaQueryWrapper<TradeCartDO>()
|
||||
.eq(TradeCartDO::getUserId, userId)
|
||||
.in(TradeCartDO::getId, ids));
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,10 +4,13 @@ import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartAddReqVO
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartListRespVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartResetReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.cart.vo.AppTradeCartUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* 购物车 Service 接口
|
||||
@ -67,6 +70,15 @@ public interface TradeCartService {
|
||||
*/
|
||||
AppTradeCartListRespVO getCartList(Long userId);
|
||||
|
||||
/**
|
||||
* 查询用户的购物车列表
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param ids 购物项的编号
|
||||
* @return 购物车列表
|
||||
*/
|
||||
List<TradeCartDO> getCartList(Long userId, Set<Long> ids);
|
||||
|
||||
/**
|
||||
* 获得用户的购物车商品 SPU 数量的 Map
|
||||
*
|
||||
|
@ -17,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.Collection;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
@ -166,6 +163,14 @@ public class TradeCartServiceImpl implements TradeCartService {
|
||||
return TradeCartConvert.INSTANCE.convertList(carts, spus, skus);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TradeCartDO> getCartList(Long userId, Set<Long> ids) {
|
||||
if (CollUtil.isEmpty(ids)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
return cartMapper.selectListByUserId(userId, ids);
|
||||
}
|
||||
|
||||
private void deleteCartIfSpuDeleted(List<TradeCartDO> carts, List<ProductSpuRespDTO> spus) {
|
||||
// 如果 SPU 被删除,则删除购物车对应的商品。延迟删除
|
||||
carts.removeIf(cart -> {
|
||||
|
@ -34,6 +34,7 @@ import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderPageRe
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementReqVO;
|
||||
import cn.iocoder.yudao.module.trade.controller.app.order.vo.AppTradeOrderSettlementRespVO;
|
||||
import cn.iocoder.yudao.module.trade.convert.order.TradeOrderConvert;
|
||||
import cn.iocoder.yudao.module.trade.dal.dataobject.cart.TradeCartDO;
|
||||
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.dal.mysql.order.TradeOrderItemMapper;
|
||||
@ -41,6 +42,10 @@ import cn.iocoder.yudao.module.trade.dal.mysql.order.TradeOrderMapper;
|
||||
import cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.*;
|
||||
import cn.iocoder.yudao.module.trade.framework.order.config.TradeOrderProperties;
|
||||
import cn.iocoder.yudao.module.trade.service.cart.TradeCartService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.TradePriceService;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
@ -50,8 +55,7 @@ import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.*;
|
||||
import static cn.iocoder.yudao.module.pay.enums.ErrorCodeConstants.PAY_ORDER_NOT_FOUND;
|
||||
import static cn.iocoder.yudao.module.trade.enums.ErrorCodeConstants.*;
|
||||
|
||||
@ -70,6 +74,11 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
@Resource
|
||||
private TradeOrderItemMapper tradeOrderItemMapper;
|
||||
|
||||
@Resource
|
||||
private TradeCartService tradeCartService;
|
||||
@Resource
|
||||
private TradePriceService tradePriceService;
|
||||
|
||||
@Resource
|
||||
private PriceApi priceApi;
|
||||
@Resource
|
||||
@ -92,7 +101,48 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
|
||||
@Override
|
||||
public AppTradeOrderSettlementRespVO settlementOrder(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
return null;
|
||||
// 1. 获得收货地址
|
||||
AddressRespDTO address = getAddress(userId, settlementReqVO.getAddressId());
|
||||
if (address != null) {
|
||||
settlementReqVO.setAddressId(address.getId());
|
||||
}
|
||||
|
||||
// 2. 计算价格
|
||||
TradePriceCalculateRespBO calculateRespBO = calculatePrice(userId, settlementReqVO);
|
||||
|
||||
// 3. 拼接返回
|
||||
return TradeOrderConvert.INSTANCE.convert(calculateRespBO, address);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获得用户地址
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param addressId 地址编号
|
||||
* @return 地址
|
||||
*/
|
||||
private AddressRespDTO getAddress(Long userId, Long addressId) {
|
||||
if (addressId != null) {
|
||||
return addressApi.getAddress(addressId, userId);
|
||||
}
|
||||
return addressApi.getDefaultAddress(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算订单价格
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @param settlementReqVO 结算信息
|
||||
* @return 订单价格
|
||||
*/
|
||||
private TradePriceCalculateRespBO calculatePrice(Long userId, AppTradeOrderSettlementReqVO settlementReqVO) {
|
||||
// 1. 如果来自购物车,则获得购物车的商品
|
||||
List<TradeCartDO> cartList = tradeCartService.getCartList(userId,
|
||||
convertSet(settlementReqVO.getItems(), AppTradeOrderSettlementReqVO.Item::getCartId));
|
||||
|
||||
// 2. 计算价格
|
||||
TradePriceCalculateReqBO calculateReqBO = TradeOrderConvert.INSTANCE.convert(userId, settlementReqVO, cartList);
|
||||
return tradePriceService.calculatePrice(calculateReqBO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -120,29 +170,6 @@ public class TradeOrderServiceImpl implements TradeOrderService {
|
||||
return tradeOrderDO.getId();
|
||||
}
|
||||
|
||||
// /**
|
||||
// * 校验商品 SKU 是否可出售
|
||||
// *
|
||||
// * @param items 商品 SKU
|
||||
// * @return 商品 SKU 数组
|
||||
// */
|
||||
// private List<ProductSkuRespDTO> validateSkuSaleable(List<Item> items) {
|
||||
// List<ProductSkuRespDTO> skus = productSkuApi.getSkuList(convertSet(items, Item::getSkuId));
|
||||
// // SKU 不存在
|
||||
// if (items.size() != skus.size()) {
|
||||
// throw exception(ORDER_CREATE_SKU_NOT_FOUND);
|
||||
// }
|
||||
// // 校验库存不足
|
||||
// Map<Long, ProductSkuRespDTO> skuMap = convertMap(skus, ProductSkuRespDTO::getId);
|
||||
// items.forEach(item -> {
|
||||
// ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
// if (item.getCount() > sku.getStock()) {
|
||||
// throw exception(ErrorCodeConstants.ORDER_CREATE_SKU_STOCK_NOT_ENOUGH);
|
||||
// }
|
||||
// });
|
||||
// return skus;
|
||||
// }
|
||||
|
||||
/**
|
||||
* 校验商品 SPU 是否可出售
|
||||
*
|
||||
|
@ -3,6 +3,8 @@ package cn.iocoder.yudao.module.trade.service.price;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
import javax.validation.Valid;
|
||||
|
||||
/**
|
||||
* 价格计算 Service 接口
|
||||
*
|
||||
@ -16,6 +18,6 @@ public interface TradePriceService {
|
||||
* @param calculateReqDTO 计算信息
|
||||
* @return 计算结果
|
||||
*/
|
||||
TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqDTO);
|
||||
TradePriceCalculateRespBO calculatePrice(@Valid TradePriceCalculateReqBO calculateReqDTO);
|
||||
|
||||
}
|
||||
|
@ -2,12 +2,16 @@ package cn.iocoder.yudao.module.trade.service.price;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.sku.ProductSkuApi;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.ProductSpuApi;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.enums.spu.ProductSpuStatusEnum;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculator;
|
||||
import cn.iocoder.yudao.module.trade.service.price.calculator.TradePriceCalculatorHelper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import java.util.List;
|
||||
@ -15,8 +19,8 @@ import java.util.Map;
|
||||
|
||||
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertMap;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_NOT_EXISTS;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.SKU_STOCK_NOT_ENOUGH;
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertSet;
|
||||
import static cn.iocoder.yudao.module.product.enums.ErrorCodeConstants.*;
|
||||
import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_CALCULATE_PAY_PRICE_ILLEGAL;
|
||||
|
||||
/**
|
||||
@ -25,22 +29,28 @@ import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.PRICE_C
|
||||
* @author 芋道源码
|
||||
*/
|
||||
@Service
|
||||
@Validated
|
||||
@Slf4j
|
||||
public class TradePriceServiceImpl implements TradePriceService {
|
||||
|
||||
@Resource
|
||||
private ProductSkuApi productSkuApi;
|
||||
@Resource
|
||||
private ProductSpuApi productSpuApi;
|
||||
|
||||
@Resource
|
||||
private List<TradePriceCalculator> priceCalculators;
|
||||
|
||||
@Override
|
||||
public TradePriceCalculateRespBO calculatePrice(TradePriceCalculateReqBO calculateReqBO) {
|
||||
// 1. 获得商品 SKU 数组
|
||||
List<ProductSkuRespDTO> skuList = checkSkus(calculateReqBO);
|
||||
// 1.1 获得商品 SKU 数组
|
||||
List<ProductSkuRespDTO> skuList = checkSkuList(calculateReqBO);
|
||||
// 1.2 获得商品 SPU 数组
|
||||
List<ProductSpuRespDTO> spuList = checkSpuList(skuList);
|
||||
|
||||
// 2.1 计算价格
|
||||
TradePriceCalculateRespBO calculateRespBO = TradePriceCalculatorHelper
|
||||
.buildCalculateResp(calculateReqBO, skuList);
|
||||
.buildCalculateResp(calculateReqBO, spuList, skuList);
|
||||
priceCalculators.forEach(calculator -> calculator.calculate(calculateReqBO, calculateRespBO));
|
||||
// 2.2 如果最终支付金额小于等于 0,则抛出业务异常
|
||||
if (calculateRespBO.getPrice().getPayPrice() <= 0) {
|
||||
@ -51,7 +61,7 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
return calculateRespBO;
|
||||
}
|
||||
|
||||
private List<ProductSkuRespDTO> checkSkus(TradePriceCalculateReqBO reqBO) {
|
||||
private List<ProductSkuRespDTO> checkSkuList(TradePriceCalculateReqBO reqBO) {
|
||||
// 获得商品 SKU 数组
|
||||
Map<Long, Integer> skuIdCountMap = convertMap(reqBO.getItems(),
|
||||
TradePriceCalculateReqBO.Item::getSkuId, TradePriceCalculateReqBO.Item::getCount);
|
||||
@ -70,4 +80,20 @@ public class TradePriceServiceImpl implements TradePriceService {
|
||||
return skus;
|
||||
}
|
||||
|
||||
private List<ProductSpuRespDTO> checkSpuList(List<ProductSkuRespDTO> skuList) {
|
||||
// 获得商品 SPU 数组
|
||||
List<ProductSpuRespDTO> spus = productSpuApi.getSpuList(convertSet(skuList, ProductSkuRespDTO::getSpuId));
|
||||
|
||||
// 校验商品 SPU
|
||||
spus.forEach(spu -> {
|
||||
if (spu == null) {
|
||||
throw exception(SPU_NOT_EXISTS);
|
||||
}
|
||||
if (!ProductSpuStatusEnum.isEnable(spu.getStatus())) {
|
||||
throw exception(SPU_NOT_ENABLE);
|
||||
}
|
||||
});
|
||||
return spus;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ public class TradePriceCalculateReqBO {
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer orderType;
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 用户编号
|
||||
|
@ -1,5 +1,6 @@
|
||||
package cn.iocoder.yudao.module.trade.service.price.bo;
|
||||
|
||||
import cn.iocoder.yudao.module.product.api.property.dto.ProductPropertyValueDetailRespDTO;
|
||||
import cn.iocoder.yudao.module.promotion.enums.common.PromotionTypeEnum;
|
||||
import cn.iocoder.yudao.module.trade.enums.order.TradeOrderTypeEnum;
|
||||
import lombok.Data;
|
||||
@ -23,7 +24,7 @@ public class TradePriceCalculateRespBO {
|
||||
*
|
||||
* 枚举 {@link TradeOrderTypeEnum}
|
||||
*/
|
||||
private Integer orderType;
|
||||
private Integer type;
|
||||
|
||||
/**
|
||||
* 订单价格
|
||||
@ -163,7 +164,26 @@ public class TradePriceCalculateRespBO {
|
||||
*/
|
||||
private Integer payPrice;
|
||||
|
||||
// TODO 芋艿:这里补充下基本信息,简单一点。
|
||||
// ========== 商品信息 ==========
|
||||
/**
|
||||
* 商品名
|
||||
*/
|
||||
private String spuName;
|
||||
/**
|
||||
* 商品图片
|
||||
*
|
||||
* 优先级:SKU.picUrl > SPU.picUrl
|
||||
*/
|
||||
private String picUrl;
|
||||
/**
|
||||
* 分类编号
|
||||
*/
|
||||
private Long categoryId;
|
||||
|
||||
/**
|
||||
* 商品属性数组
|
||||
*/
|
||||
private List<ProductPropertyValueDetailRespDTO> properties;
|
||||
|
||||
}
|
||||
|
||||
@ -189,12 +209,6 @@ public class TradePriceCalculateRespBO {
|
||||
* 枚举 {@link PromotionTypeEnum}
|
||||
*/
|
||||
private Integer type;
|
||||
/**
|
||||
* 营销级别
|
||||
*
|
||||
* 枚举 {@link PromotionLevelEnum}
|
||||
*/
|
||||
private Integer level;
|
||||
/**
|
||||
* 计算时的原价(总),单位:分
|
||||
*/
|
||||
|
@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.trade.service.price.calculator;
|
||||
import cn.hutool.core.lang.Assert;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.product.api.sku.dto.ProductSkuRespDTO;
|
||||
import cn.iocoder.yudao.module.product.api.spu.dto.ProductSpuRespDTO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateReqBO;
|
||||
import cn.iocoder.yudao.module.trade.service.price.bo.TradePriceCalculateRespBO;
|
||||
|
||||
@ -14,6 +15,8 @@ import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.
|
||||
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.getSumValue;
|
||||
import static java.util.Collections.singletonList;
|
||||
|
||||
// TODO 芋艿:改成父类
|
||||
|
||||
/**
|
||||
* {@link TradePriceCalculator} 的工具类
|
||||
*
|
||||
@ -24,25 +27,42 @@ import static java.util.Collections.singletonList;
|
||||
public class TradePriceCalculatorHelper {
|
||||
|
||||
public static TradePriceCalculateRespBO buildCalculateResp(TradePriceCalculateReqBO param,
|
||||
List<ProductSkuRespDTO> skuList) {
|
||||
List<ProductSpuRespDTO> spuList, List<ProductSkuRespDTO> skuList) {
|
||||
// 创建 PriceCalculateRespDTO 对象
|
||||
TradePriceCalculateRespBO result = new TradePriceCalculateRespBO();
|
||||
result.setOrderType(param.getOrderType());
|
||||
result.setType(param.getType());
|
||||
result.setPromotions(new ArrayList<>());
|
||||
|
||||
// 创建它的 OrderItem 属性
|
||||
Map<Long, TradePriceCalculateReqBO.Item> skuItemMap = convertMap(param.getItems(),
|
||||
TradePriceCalculateReqBO.Item::getSkuId);
|
||||
result.setItems(new ArrayList<>(skuItemMap.size()));
|
||||
skuList.forEach(sku -> {
|
||||
TradePriceCalculateReqBO.Item skuItem = skuItemMap.get(sku.getId());
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem()
|
||||
// SKU 字段
|
||||
.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.setCount(skuItem.getCount()).setCartId(skuItem.getCartId()).setSelected(skuItem.getSelected())
|
||||
// 价格字段
|
||||
.setPrice(sku.getPrice()).setPayPrice(sku.getPrice() * skuItem.getCount())
|
||||
.setDiscountPrice(0).setDeliveryPrice(0).setCouponPrice(0).setPointPrice(0);
|
||||
result.setItems(new ArrayList<>(param.getItems().size()));
|
||||
Map<Long, ProductSpuRespDTO> spuMap = convertMap(spuList, ProductSpuRespDTO::getId);
|
||||
Map<Long, ProductSkuRespDTO> skuMap = convertMap(skuList, ProductSkuRespDTO::getId);
|
||||
param.getItems().forEach(item -> {
|
||||
ProductSkuRespDTO sku = skuMap.get(item.getSkuId());
|
||||
if (sku == null) {
|
||||
return;
|
||||
}
|
||||
ProductSpuRespDTO spu = spuMap.get(sku.getSpuId());
|
||||
if (spu == null) {
|
||||
return;
|
||||
}
|
||||
// 商品项
|
||||
TradePriceCalculateRespBO.OrderItem orderItem = new TradePriceCalculateRespBO.OrderItem();
|
||||
result.getItems().add(orderItem);
|
||||
orderItem.setSpuId(sku.getSpuId()).setSkuId(sku.getId())
|
||||
.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);
|
||||
// sku 信息
|
||||
orderItem.setPicUrl(sku.getPicUrl()).setProperties(sku.getProperties());
|
||||
// spu 信息
|
||||
orderItem.setSpuName(spu.getName()).setCategoryId(spu.getCategoryId());
|
||||
if (orderItem.getPicUrl() == null) {
|
||||
orderItem.setPicUrl(spu.getPicUrl());
|
||||
}
|
||||
});
|
||||
|
||||
// 创建它的 Price 属性
|
||||
result.setPrice(new TradePriceCalculateRespBO.Price());
|
||||
recountAllPrice(result);
|
||||
|
@ -69,7 +69,7 @@ public class TradeAfterSaleServiceTest extends BaseDbUnitTest {
|
||||
.setApplyPicUrls(asList("https://www.baidu.com/1.png", "https://www.baidu.com/2.png"));
|
||||
// mock 方法(交易订单项)
|
||||
TradeOrderItemDO orderItem = randomPojo(TradeOrderItemDO.class, o -> {
|
||||
o.setOrderId(111L).setUserId(userId).setOrderDividePrice(200);
|
||||
o.setOrderId(111L).setUserId(userId).setPayPrice(200);
|
||||
o.setAfterSaleStatus(TradeOrderItemAfterSaleStatusEnum.NONE.getStatus());
|
||||
});
|
||||
when(tradeOrderService.getOrderItem(eq(1024L), eq(1L)))
|
||||
|
@ -18,4 +18,12 @@ public interface AddressApi {
|
||||
*/
|
||||
AddressRespDTO getAddress(Long id, Long userId);
|
||||
|
||||
/**
|
||||
* 获得用户默认收件地址
|
||||
*
|
||||
* @param userId 用户编号
|
||||
* @return 用户收件地址
|
||||
*/
|
||||
AddressRespDTO getDefaultAddress(Long userId);
|
||||
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ public class AddressRespDTO {
|
||||
/**
|
||||
* 地区编号
|
||||
*/
|
||||
private Long areaId;
|
||||
private Integer areaId;
|
||||
/**
|
||||
* 邮编
|
||||
*/
|
||||
|
@ -25,4 +25,9 @@ public class AddressApiImpl implements AddressApi {
|
||||
return AddressConvert.INSTANCE.convert02(addressService.getAddress(userId, id));
|
||||
}
|
||||
|
||||
@Override
|
||||
public AddressRespDTO getDefaultAddress(Long userId) {
|
||||
return AddressConvert.INSTANCE.convert02(addressService.getDefaultUserAddress(userId));
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user