parent
							
								
									4aac5bd2c6
								
							
						
					
					
						commit
						3909a95495
					
				| 
						 | 
				
			
			@ -64,6 +64,23 @@
 | 
			
		|||
            <groupId>org.apache.commons</groupId>
 | 
			
		||||
            <artifactId>commons-lang3</artifactId>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>org.mybatis</groupId>
 | 
			
		||||
            <artifactId>mybatis</artifactId>
 | 
			
		||||
            <version>3.5.0</version>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.fasterxml.jackson.core</groupId>
 | 
			
		||||
            <artifactId>jackson-databind</artifactId>
 | 
			
		||||
            <version>2.9.8</version>
 | 
			
		||||
            <scope>compile</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
        <dependency>
 | 
			
		||||
            <groupId>com.fasterxml.jackson.core</groupId>
 | 
			
		||||
            <artifactId>jackson-annotations</artifactId>
 | 
			
		||||
            <version>2.9.7</version>
 | 
			
		||||
            <scope>compile</scope>
 | 
			
		||||
        </dependency>
 | 
			
		||||
 | 
			
		||||
    </dependencies>
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,70 @@
 | 
			
		|||
package cn.iocoder.common.framework.mybatis;
 | 
			
		||||
 | 
			
		||||
import com.alibaba.fastjson.JSON;
 | 
			
		||||
import org.apache.ibatis.type.BaseTypeHandler;
 | 
			
		||||
import org.apache.ibatis.type.JdbcType;
 | 
			
		||||
 | 
			
		||||
import java.sql.CallableStatement;
 | 
			
		||||
import java.sql.PreparedStatement;
 | 
			
		||||
import java.sql.ResultSet;
 | 
			
		||||
import java.sql.SQLException;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * TODO 芋艿
 | 
			
		||||
 *
 | 
			
		||||
 * 参考 https://www.cnblogs.com/waterystone/p/5547254.html
 | 
			
		||||
 *
 | 
			
		||||
 * 后续,补充下注释和测试类,以及文章。
 | 
			
		||||
 *
 | 
			
		||||
 * @param <T>
 | 
			
		||||
 */
 | 
			
		||||
public class JSONTypeHandler<T extends Object> extends BaseTypeHandler<T> {
 | 
			
		||||
 | 
			
		||||
    private Class<T> clazz;
 | 
			
		||||
 | 
			
		||||
    public JSONTypeHandler(Class<T> clazz) {
 | 
			
		||||
        if (clazz == null) throw new IllegalArgumentException("Type argument cannot be null");
 | 
			
		||||
        this.clazz = clazz;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
 | 
			
		||||
        ps.setString(i, this.toJson(parameter));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
 | 
			
		||||
        return this.toObject(rs.getString(columnName), clazz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
 | 
			
		||||
        return this.toObject(rs.getString(columnIndex), clazz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
 | 
			
		||||
        return this.toObject(cs.getString(columnIndex), clazz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private String toJson(T object) {
 | 
			
		||||
        try {
 | 
			
		||||
            return JSON.toJSONString(object);
 | 
			
		||||
        } catch (Exception e) {
 | 
			
		||||
            throw new RuntimeException(e);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private T toObject(String content, Class<?> clazz) {
 | 
			
		||||
        if (content != null && !content.isEmpty()) {
 | 
			
		||||
            try {
 | 
			
		||||
                return (T) JSON.parseObject(content, clazz);
 | 
			
		||||
            } catch (Exception e) {
 | 
			
		||||
                throw new RuntimeException(e);
 | 
			
		||||
            }
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -120,6 +120,16 @@ export function getCartConfirmCreateOrder(skuId, quantity) {
 | 
			
		|||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function getCartCalcSkuPrice(skuId) {
 | 
			
		||||
  return request({
 | 
			
		||||
    url: '/order-api/users/cart/calc_sku_price',
 | 
			
		||||
    method: 'get',
 | 
			
		||||
    params: {
 | 
			
		||||
      skuId,
 | 
			
		||||
    }
 | 
			
		||||
  });
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// 物流信息
 | 
			
		||||
 | 
			
		||||
export function getLogisticsInfo(params) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,20 +7,6 @@
 | 
			
		|||
      </van-swipe-item>
 | 
			
		||||
    </van-swipe>
 | 
			
		||||
      <!-- TODO 这里需要优化下,芋艿 -->
 | 
			
		||||
      <div class="limit-dising-banner">
 | 
			
		||||
          <div class="row no-wrap flex-center limit-price">
 | 
			
		||||
              <span class="font-13 pusht">¥</span><span class="price">2.8</span>
 | 
			
		||||
              <div class="pushl">
 | 
			
		||||
                  <div><del>¥14</del></div>
 | 
			
		||||
                  <div><span class="tag">限时抢购</span></div>
 | 
			
		||||
              </div>
 | 
			
		||||
          </div>
 | 
			
		||||
          <div class="counting">
 | 
			
		||||
              <p>距离结束仅剩</p>
 | 
			
		||||
              <div class="row no-wrap flex-center counting-clock" style="display: block;"><span class="num">71</span><i class="wxIcon wxIcon-colon"></i><span class="num">42</span><i class="wxIcon wxIcon-colon"></i><span class="num">02</span></div>
 | 
			
		||||
          </div>
 | 
			
		||||
      </div>
 | 
			
		||||
 | 
			
		||||
      <van-cell-group>
 | 
			
		||||
          <van-cell>
 | 
			
		||||
            <span class="goods-price">{{ formatPrice(initialSku.price) }}</span>
 | 
			
		||||
| 
						 | 
				
			
			@ -79,6 +65,25 @@
 | 
			
		|||
 | 
			
		||||
    </van-cell-group>
 | 
			
		||||
 | 
			
		||||
<!--      <van-cell is-link @click="sorry">-->
 | 
			
		||||
<!--          <template slot="title">-->
 | 
			
		||||
<!--              <van-tag type="danger">多买优惠</van-tag>-->
 | 
			
		||||
<!--              <span> 满2件,总价打9折</span>-->
 | 
			
		||||
<!--          </template>-->
 | 
			
		||||
<!--      </van-cell>-->
 | 
			
		||||
      <van-cell is-link @click="sorry">
 | 
			
		||||
          <template slot="title">
 | 
			
		||||
              <van-tag type="danger">满减</van-tag>
 | 
			
		||||
              <span> 满100元减50元</span>
 | 
			
		||||
          </template>
 | 
			
		||||
      </van-cell>
 | 
			
		||||
      <van-cell is-link @click="sorry">
 | 
			
		||||
          <template slot="title">
 | 
			
		||||
              <van-tag type="danger">限购</van-tag>
 | 
			
		||||
              <span> 购买不超过5件时享受单件价¥8.00,超出数量以结算价为准</span>
 | 
			
		||||
          </template>
 | 
			
		||||
      </van-cell>
 | 
			
		||||
 | 
			
		||||
    <div class="goods-info">
 | 
			
		||||
      <p class="goods-info-title">图文详情</p>
 | 
			
		||||
      <div v-html="spu.description"></div>
 | 
			
		||||
| 
						 | 
				
			
			@ -99,28 +104,6 @@
 | 
			
		|||
      </van-goods-action-big-btn>
 | 
			
		||||
    </van-goods-action>
 | 
			
		||||
 | 
			
		||||
    <!--<van-actionsheet v-model="show" title="促销" style="font-size:14px;">-->
 | 
			
		||||
    <!---->
 | 
			
		||||
    <!--<van-cell  is-link @click="sorry" >-->
 | 
			
		||||
    <!--<template slot="title">-->
 | 
			
		||||
    <!--<van-tag type="danger">多买优惠</van-tag>-->
 | 
			
		||||
    <!--<span> 满2件,总价打9折</span>-->
 | 
			
		||||
    <!--</template>-->
 | 
			
		||||
    <!--</van-cell>-->
 | 
			
		||||
    <!--<van-cell  is-link @click="sorry" >-->
 | 
			
		||||
    <!--<template slot="title">-->
 | 
			
		||||
    <!--<van-tag type="danger">满减</van-tag>-->
 | 
			
		||||
    <!--<span> 满100元减50元</span>-->
 | 
			
		||||
    <!--</template>-->
 | 
			
		||||
    <!--</van-cell>-->
 | 
			
		||||
    <!--<van-cell  is-link @click="sorry" >-->
 | 
			
		||||
    <!--<template slot="title">-->
 | 
			
		||||
    <!--<van-tag type="danger">限购</van-tag>-->
 | 
			
		||||
    <!--<span> 购买不超过5件时享受单件价¥8.00,超出数量以结算价为准</span>-->
 | 
			
		||||
    <!--</template>-->
 | 
			
		||||
    <!--</van-cell>-->
 | 
			
		||||
    <!--</van-actionsheet>-->
 | 
			
		||||
 | 
			
		||||
    <!--<van-actionsheet v-model="showTag" title="服务说明" style="font-size:14px;">-->
 | 
			
		||||
    <!---->
 | 
			
		||||
    <!--<van-cell>-->
 | 
			
		||||
| 
						 | 
				
			
			@ -181,8 +164,8 @@
 | 
			
		|||
<script>
 | 
			
		||||
  // import skuData from '../../data/sku';
 | 
			
		||||
  import {getProductSpuInfo} from '../../api/product';
 | 
			
		||||
  import {addCart, countCart} from '../../api/order';
 | 
			
		||||
  import { Dialog } from 'vant';
 | 
			
		||||
  import {addCart, countCart, getCartCalcSkuPrice} from '../../api/order';
 | 
			
		||||
  import {Dialog} from 'vant';
 | 
			
		||||
 | 
			
		||||
  export default {
 | 
			
		||||
    components: {},
 | 
			
		||||
| 
						 | 
				
			
			@ -212,6 +195,10 @@
 | 
			
		|||
 | 
			
		||||
        cartCount: 0,
 | 
			
		||||
 | 
			
		||||
        calSkuPriceResult: {
 | 
			
		||||
 | 
			
		||||
        },
 | 
			
		||||
 | 
			
		||||
      };
 | 
			
		||||
    },
 | 
			
		||||
    methods: {
 | 
			
		||||
| 
						 | 
				
			
			@ -233,6 +220,7 @@
 | 
			
		|||
        this.initialSku.quantity = value;
 | 
			
		||||
      },
 | 
			
		||||
      skuSelected({skuValue, selectedSku, selectedSkuComb}) { // 选择 sku
 | 
			
		||||
        // TODO 芋艿,需要改下,禁用用户取消选中。
 | 
			
		||||
        // console.log(skuValue);
 | 
			
		||||
        // console.log(selectedSku);
 | 
			
		||||
        // console.log(selectedSkuComb);
 | 
			
		||||
| 
						 | 
				
			
			@ -240,8 +228,14 @@
 | 
			
		|||
          ...selectedSkuComb,
 | 
			
		||||
          quantity: 1,
 | 
			
		||||
        };
 | 
			
		||||
        // 执行 sku 价格计算
 | 
			
		||||
        this.doCalcSkuPrice(this.initialSku.id)
 | 
			
		||||
      },
 | 
			
		||||
      doCalcSkuPrice(skuId) {
 | 
			
		||||
        getCartCalcSkuPrice(skuId).then(data => {
 | 
			
		||||
          this.calSkuPriceResult = data;
 | 
			
		||||
        });
 | 
			
		||||
      },
 | 
			
		||||
 | 
			
		||||
      onClickCart() {
 | 
			
		||||
        this.$router.push('/cart');
 | 
			
		||||
      },
 | 
			
		||||
| 
						 | 
				
			
			@ -347,6 +341,8 @@
 | 
			
		|||
        // TODO 芋艿,需要处理下第一个有效的 sku
 | 
			
		||||
        this.initialSku = vanSku.list[0];
 | 
			
		||||
        this.initialSku.quantity = 1;
 | 
			
		||||
        // 执行 sku 价格计算
 | 
			
		||||
        this.doCalcSkuPrice(this.initialSku.id);
 | 
			
		||||
      });
 | 
			
		||||
      // 获得购物车数量
 | 
			
		||||
      countCart().then(data => {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,9 +4,11 @@ import cn.iocoder.common.framework.vo.CommonResult;
 | 
			
		|||
import cn.iocoder.mall.order.api.CartService;
 | 
			
		||||
import cn.iocoder.mall.order.api.OrderService;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcOrderPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CartItemBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.dto.CalcOrderPriceDTO;
 | 
			
		||||
import cn.iocoder.mall.order.application.convert.CartConvert;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersCalcSkuPriceVO;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersCartDetailVO;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
 | 
			
		||||
import cn.iocoder.mall.user.sdk.context.UserSecurityContextHolder;
 | 
			
		||||
| 
						 | 
				
			
			@ -129,6 +131,17 @@ public class UsersCartController {
 | 
			
		|||
        return cartService.calcOrderPrice(calcOrderPriceDTO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @GetMapping("/calc_sku_price")
 | 
			
		||||
    public CommonResult<UsersCalcSkuPriceVO> calcSkuPrice(@RequestParam("skuId") Integer skuId) {
 | 
			
		||||
        // 计算 sku 的价格
 | 
			
		||||
        CommonResult<CalcSkuPriceBO> calcSkuPriceResult = cartService.calcSkuPrice(skuId);
 | 
			
		||||
        // 返回结果
 | 
			
		||||
        if (calcSkuPriceResult.isError()) {
 | 
			
		||||
            return CommonResult.error(calcSkuPriceResult);
 | 
			
		||||
        }
 | 
			
		||||
        return CommonResult.success(CartConvert.INSTANCE.convert2(calcSkuPriceResult.getData()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    public CommonResult<Object> confirmOrder() {
 | 
			
		||||
        // 查询购物车列表(选中的)
 | 
			
		||||
//        cartService.list(userId, true);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,6 +1,8 @@
 | 
			
		|||
package cn.iocoder.mall.order.application.convert;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcOrderPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersCalcSkuPriceVO;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersCartDetailVO;
 | 
			
		||||
import cn.iocoder.mall.order.application.vo.UsersOrderConfirmCreateVO;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
| 
						 | 
				
			
			@ -15,4 +17,6 @@ public interface CartConvert {
 | 
			
		|||
 | 
			
		||||
    UsersCartDetailVO convert2(CalcOrderPriceBO calcOrderPriceBO);
 | 
			
		||||
 | 
			
		||||
    UsersCalcSkuPriceVO convert2(CalcSkuPriceBO calcSkuPriceBO);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,34 @@
 | 
			
		|||
package cn.iocoder.mall.order.application.vo;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
 | 
			
		||||
import io.swagger.annotations.ApiModel;
 | 
			
		||||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
@ApiModel("计算商品 SKU 价格结果 VO")
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class UsersCalcSkuPriceVO {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 满减送促销活动
 | 
			
		||||
     *
 | 
			
		||||
     * TODO 芋艿,后续改成 VO
 | 
			
		||||
     */
 | 
			
		||||
    private PromotionActivityBO fullPrivilege;
 | 
			
		||||
    /**
 | 
			
		||||
     * 电视和折扣促销活动
 | 
			
		||||
     *
 | 
			
		||||
     * TODO 芋艿,后续改成 VO
 | 
			
		||||
     */
 | 
			
		||||
    private PromotionActivityBO timeLimitedDiscount;
 | 
			
		||||
    /**
 | 
			
		||||
     * 原价格,单位:分。
 | 
			
		||||
     */
 | 
			
		||||
    private Integer originalPrice;
 | 
			
		||||
    /**
 | 
			
		||||
     * 最终价格,单位:分。
 | 
			
		||||
     */
 | 
			
		||||
    private Integer presentPrice;
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -87,24 +87,14 @@ public interface CartService {
 | 
			
		|||
     */
 | 
			
		||||
    CommonResult<CalcOrderPriceBO> calcOrderPrice(CalcOrderPriceDTO calcOrderPriceDTO);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 计算指定商品 SKU 的金额,并返回计算结果
 | 
			
		||||
     *
 | 
			
		||||
     * TODO 芋艿,此处只会计算,限时折扣带来的价格变化。
 | 
			
		||||
     *
 | 
			
		||||
     * @param skuId 商品 SKU 编号
 | 
			
		||||
     * @return 计算订单金额结果
 | 
			
		||||
     */
 | 
			
		||||
    CommonResult<CalcSkuPriceBO> calcSkuPrice(Integer skuId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 获得购物车明细
 | 
			
		||||
     *
 | 
			
		||||
     * TODO 芋艿,可能放在 Controller 更合适
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户编号
 | 
			
		||||
     * @return 购物车明细
 | 
			
		||||
     */
 | 
			
		||||
    CommonResult<CartBO> details(Integer userId);
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 基于购物车创建订单
 | 
			
		||||
     *
 | 
			
		||||
     * @param userId 用户编号
 | 
			
		||||
     * @return 订单信息
 | 
			
		||||
     */
 | 
			
		||||
    CommonResult<OrderCreateBO> createOrder(Integer userId);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,7 +4,9 @@ import cn.iocoder.common.framework.constant.CommonStatusEnum;
 | 
			
		|||
import cn.iocoder.common.framework.util.ServiceExceptionUtil;
 | 
			
		||||
import cn.iocoder.common.framework.vo.CommonResult;
 | 
			
		||||
import cn.iocoder.mall.order.api.CartService;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.*;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcOrderPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CalcSkuPriceBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.bo.CartItemBO;
 | 
			
		||||
import cn.iocoder.mall.order.api.constant.CartItemStatusEnum;
 | 
			
		||||
import cn.iocoder.mall.order.api.constant.OrderErrorCodeEnum;
 | 
			
		||||
import cn.iocoder.mall.order.api.dto.CalcOrderPriceDTO;
 | 
			
		||||
| 
						 | 
				
			
			@ -14,6 +16,11 @@ import cn.iocoder.mall.order.biz.dataobject.CartItemDO;
 | 
			
		|||
import cn.iocoder.mall.product.api.ProductSpuService;
 | 
			
		||||
import cn.iocoder.mall.product.api.bo.ProductSkuBO;
 | 
			
		||||
import cn.iocoder.mall.product.api.bo.ProductSkuDetailBO;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.PromotionActivityService;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PreferentialTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PromotionActivityStatusEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PromotionActivityTypeEnum;
 | 
			
		||||
import com.alibaba.dubbo.config.annotation.Reference;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
| 
						 | 
				
			
			@ -30,6 +37,8 @@ public class CartServiceImpl implements CartService {
 | 
			
		|||
 | 
			
		||||
    @Reference(validation = "true")
 | 
			
		||||
    private ProductSpuService productSpuService;
 | 
			
		||||
    @Reference(validation = "true")
 | 
			
		||||
    private PromotionActivityService promotionActivityService;
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private CartMapper cartMapper;
 | 
			
		||||
| 
						 | 
				
			
			@ -188,17 +197,62 @@ public class CartServiceImpl implements CartService {
 | 
			
		|||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    @SuppressWarnings("Duplicates")
 | 
			
		||||
    public CommonResult<CalcSkuPriceBO> calcSkuPrice(Integer skuId) {
 | 
			
		||||
        return null;
 | 
			
		||||
        // 查询 SKU 是否合法
 | 
			
		||||
        CommonResult<ProductSkuBO> skuResult = productSpuService.getProductSku(skuId);
 | 
			
		||||
        if (skuResult.isError()) {
 | 
			
		||||
            return CommonResult.error(skuResult);
 | 
			
		||||
        }
 | 
			
		||||
        ProductSkuBO sku = skuResult.getData();
 | 
			
		||||
        if (sku == null
 | 
			
		||||
                || CommonStatusEnum.DISABLE.getValue().equals(sku.getStatus())) { // sku 被禁用
 | 
			
		||||
            return ServiceExceptionUtil.error(OrderErrorCodeEnum.CARD_ITEM_SKU_NOT_FOUND.getCode());
 | 
			
		||||
        }
 | 
			
		||||
        // 查询促销活动
 | 
			
		||||
        CommonResult<List<PromotionActivityBO>> activityListResult = promotionActivityService.getPromotionActivityListBySpuId(sku.getSpuId(),
 | 
			
		||||
                Arrays.asList(PromotionActivityStatusEnum.WAIT.getValue(), PromotionActivityStatusEnum.RUN.getValue()));
 | 
			
		||||
        if (activityListResult.isError()) {
 | 
			
		||||
            return CommonResult.error(activityListResult);
 | 
			
		||||
        }
 | 
			
		||||
        // 如果无促销活动,则直接返回默认结果即可
 | 
			
		||||
        List<PromotionActivityBO> activityList = activityListResult.getData();
 | 
			
		||||
        if (activityList.isEmpty()) {
 | 
			
		||||
            return CommonResult.success(new CalcSkuPriceBO().setOriginalPrice(sku.getPrice()).setPresentPrice(sku.getPrice()));
 | 
			
		||||
        }
 | 
			
		||||
        // 如果有促销活动,则开始做计算 TODO 芋艿,因为现在暂时只有限时折扣 + 满减送。所以写的比较简单先
 | 
			
		||||
        PromotionActivityBO fullPrivilege = findPromotionActivityByType(activityList, PromotionActivityTypeEnum.FULL_PRIVILEGE);
 | 
			
		||||
        PromotionActivityBO timeLimitedDiscount = findPromotionActivityByType(activityList, PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT);
 | 
			
		||||
        Integer presentPrice = calcSkuPriceByTimeLimitDiscount(sku, timeLimitedDiscount);
 | 
			
		||||
        // 返回结果
 | 
			
		||||
        return CommonResult.success(new CalcSkuPriceBO().setFullPrivilege(fullPrivilege).setTimeLimitedDiscount(timeLimitedDiscount)
 | 
			
		||||
                .setOriginalPrice(sku.getPrice()).setPresentPrice(presentPrice));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CommonResult<CartBO> details(Integer userId) {
 | 
			
		||||
        return null;
 | 
			
		||||
    private Integer calcSkuPriceByTimeLimitDiscount(ProductSkuBO sku, PromotionActivityBO timeLimitedDiscount) {
 | 
			
		||||
        // 获得对应的优惠项
 | 
			
		||||
        PromotionActivityBO.TimeLimitedDiscount.Item item = timeLimitedDiscount.getTimeLimitedDiscount().getItems().stream()
 | 
			
		||||
                .filter(item0 -> item0.getSpuId().equals(sku.getSpuId()))
 | 
			
		||||
                .findFirst().orElse(null);
 | 
			
		||||
        if (item == null) {
 | 
			
		||||
            throw new IllegalArgumentException(String.format("折扣活动(%s) 不存在商品(%s) 的优惠配置",
 | 
			
		||||
                    timeLimitedDiscount.toString(), sku.toString()));
 | 
			
		||||
        }
 | 
			
		||||
        // 计算价格
 | 
			
		||||
        if (PreferentialTypeEnum.PRICE.getValue().equals(item.getPreferentialType())) { // 减价
 | 
			
		||||
            int presentPrice = sku.getPrice() - item.getPreferentialValue();
 | 
			
		||||
            return presentPrice >= 0 ? presentPrice : sku.getPrice(); // 如果计算优惠价格小于 0 ,则说明无法使用优惠。
 | 
			
		||||
        }
 | 
			
		||||
        if (PreferentialTypeEnum.DISCOUNT.getValue().equals(item.getPreferentialType())) { // 打折
 | 
			
		||||
            return sku.getPrice() * item.getPreferentialValue() / 100;
 | 
			
		||||
        }
 | 
			
		||||
        throw new IllegalArgumentException(String.format("折扣活动(%s) 的优惠类型不正确", timeLimitedDiscount.toString()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CommonResult<OrderCreateBO> createOrder(Integer userId) {
 | 
			
		||||
        return null;
 | 
			
		||||
    private PromotionActivityBO findPromotionActivityByType(List<PromotionActivityBO> activityList, PromotionActivityTypeEnum type) {
 | 
			
		||||
        return activityList.stream()
 | 
			
		||||
                .filter(activity -> type.getValue().equals(activity.getActivityType()))
 | 
			
		||||
                .findFirst().orElse(null);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -103,7 +103,8 @@ public class ProductSpuServiceImpl implements ProductSpuService {
 | 
			
		|||
        // 校验 Sku 规格
 | 
			
		||||
        CommonResult<Boolean> validProductSkuResult = validProductSku(productSpuAddDTO.getSkus(), validAttrResult.getData());
 | 
			
		||||
        if (validProductSkuResult.isError()) {
 | 
			
		||||
            return CommonResult.error(validProductSkuResult);
 | 
			
		||||
//            return CommonResult.error(validProductSkuResult);
 | 
			
		||||
            throw ServiceExceptionUtil.exception(validProductSkuResult.getCode());
 | 
			
		||||
        }
 | 
			
		||||
        productSkuMapper.insertList(skus);
 | 
			
		||||
        // 返回成功
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.Date;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class BannerBO {
 | 
			
		||||
public class BannerBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 编号
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.List;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class BannerPageBO {
 | 
			
		||||
public class BannerPageBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Banner 数组
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.Date;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class CouponCardBO {
 | 
			
		||||
public class CouponCardBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    // ========== 基本信息 BEGIN ==========
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.List;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class CouponCardPageBO {
 | 
			
		||||
public class CouponCardPageBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 优惠劵数组
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.Date;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class CouponTemplateBO {
 | 
			
		||||
public class CouponTemplateBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    // ========== 基本信息 BEGIN ==========
 | 
			
		||||
    /**
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.List;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class CouponTemplatePageBO {
 | 
			
		||||
public class CouponTemplatePageBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 优惠劵(码)数组
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -4,6 +4,7 @@ import cn.iocoder.mall.promotion.api.constant.ProductRecommendTypeEnum;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -11,7 +12,7 @@ import java.util.Date;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class ProductRecommendBO {
 | 
			
		||||
public class ProductRecommendBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 编号
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,6 +3,7 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
| 
						 | 
				
			
			@ -10,7 +11,7 @@ import java.util.List;
 | 
			
		|||
 */
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class ProductRecommendPageBO {
 | 
			
		||||
public class ProductRecommendPageBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * ProductRecommend 数组
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -3,12 +3,12 @@ package cn.iocoder.mall.promotion.api.bo;
 | 
			
		|||
import lombok.Data;
 | 
			
		||||
import lombok.experimental.Accessors;
 | 
			
		||||
 | 
			
		||||
import java.io.Serializable;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
import java.util.Set;
 | 
			
		||||
 | 
			
		||||
@Data
 | 
			
		||||
@Accessors(chain = true)
 | 
			
		||||
public class PromotionActivityBO {
 | 
			
		||||
public class PromotionActivityBO implements Serializable {
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 活动编号
 | 
			
		||||
| 
						 | 
				
			
			@ -23,7 +23,7 @@ public class PromotionActivityBO {
 | 
			
		|||
     *
 | 
			
		||||
     * 参见 {@link cn.iocoder.mall.promotion.api.constant.PromotionActivityTypeEnum} 枚举
 | 
			
		||||
     */
 | 
			
		||||
    private Integer type;
 | 
			
		||||
    private Integer activityType;
 | 
			
		||||
    /**
 | 
			
		||||
     * 活动状态
 | 
			
		||||
     *
 | 
			
		||||
| 
						 | 
				
			
			@ -31,23 +31,27 @@ public class PromotionActivityBO {
 | 
			
		|||
     */
 | 
			
		||||
    private Integer status;
 | 
			
		||||
    /**
 | 
			
		||||
     * 匹配的商品 SPU 编号
 | 
			
		||||
     * 限制折扣
 | 
			
		||||
     */
 | 
			
		||||
    private Set<Integer> spuIds;
 | 
			
		||||
    private TimeLimitedDiscount timeLimitedDiscount;
 | 
			
		||||
    /**
 | 
			
		||||
     * 满减送
 | 
			
		||||
     */
 | 
			
		||||
    private FullPrivilege fullPrivilege;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 限制折扣
 | 
			
		||||
     */
 | 
			
		||||
    @Data
 | 
			
		||||
    @Accessors(chain = true)
 | 
			
		||||
    public static class TimeLimitedDiscount {
 | 
			
		||||
    public static class TimeLimitedDiscount implements Serializable {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 商品折扣
 | 
			
		||||
         */
 | 
			
		||||
        @Data
 | 
			
		||||
        @Accessors(chain = true)
 | 
			
		||||
        public static class Item {
 | 
			
		||||
        public static class Item implements Serializable {
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * 商品 SPU 编号
 | 
			
		||||
| 
						 | 
				
			
			@ -82,14 +86,14 @@ public class PromotionActivityBO {
 | 
			
		|||
     */
 | 
			
		||||
    @Data
 | 
			
		||||
    @Accessors(chain = true)
 | 
			
		||||
    public static class FullPrivilege {
 | 
			
		||||
    public static class FullPrivilege implements Serializable {
 | 
			
		||||
 | 
			
		||||
        /**
 | 
			
		||||
         * 优惠
 | 
			
		||||
         */
 | 
			
		||||
        @Data
 | 
			
		||||
        @Accessors(chain = true)
 | 
			
		||||
        public static class Privilege {
 | 
			
		||||
        public static class Privilege implements Serializable {
 | 
			
		||||
 | 
			
		||||
            /**
 | 
			
		||||
             * 满足类型
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -9,8 +9,8 @@ import java.util.Arrays;
 | 
			
		|||
 */
 | 
			
		||||
public enum PreferentialTypeEnum implements IntArrayValuable {
 | 
			
		||||
 | 
			
		||||
    PRICE(1, "代金卷"),
 | 
			
		||||
    DISCOUNT(2, "折扣卷"),
 | 
			
		||||
    PRICE(1, "减价"),
 | 
			
		||||
    DISCOUNT(2, "打折"),
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(PreferentialTypeEnum::getValue).toArray();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -7,10 +7,10 @@ import java.util.Arrays;
 | 
			
		|||
public enum RangeTypeEnum implements IntArrayValuable {
 | 
			
		||||
 | 
			
		||||
    ALL(10, "所有可用"),
 | 
			
		||||
    PRODUCT_INCLUDE_PRT(20, "部分商品可用,或指定商品可用"),
 | 
			
		||||
    PRODUCT_EXCLUDE_PRT(21, "部分商品不可用,或指定商品可用"),
 | 
			
		||||
    CATEGORY_INCLUDE_PRT(30, "部分分类可用,或指定分类可用"),
 | 
			
		||||
    CATEGORY_EXCLUDE_PRT(31, "部分分类不可用,或指定分类可用"),
 | 
			
		||||
    PRODUCT_INCLUDE_PART(20, "部分商品可用,或指定商品可用"),
 | 
			
		||||
    PRODUCT_EXCLUDE_PART(21, "部分商品不可用,或指定商品可用"),
 | 
			
		||||
    CATEGORY_INCLUDE_PART(30, "部分分类可用,或指定分类可用"),
 | 
			
		||||
    CATEGORY_EXCLUDE_PART(31, "部分分类不可用,或指定分类可用"),
 | 
			
		||||
    ;
 | 
			
		||||
 | 
			
		||||
    public static final int[] ARRAYS = Arrays.stream(values()).mapToInt(RangeTypeEnum::getValue).toArray();
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -16,15 +16,15 @@ public interface ProductRecommendConvert {
 | 
			
		|||
    ProductRecommendConvert INSTANCE = Mappers.getMapper(ProductRecommendConvert.class);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    ProductRecommendBO convertToBO(ProductRecommendDO banner);
 | 
			
		||||
    ProductRecommendBO convertToBO(ProductRecommendDO recommend);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    List<ProductRecommendBO> convertToBO(List<ProductRecommendDO> bannerList);
 | 
			
		||||
    List<ProductRecommendBO> convertToBO(List<ProductRecommendDO> recommendList);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    ProductRecommendDO convert(ProductRecommendAddDTO bannerAddDTO);
 | 
			
		||||
    ProductRecommendDO convert(ProductRecommendAddDTO recommendAddDTO);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    ProductRecommendDO convert(ProductRecommendUpdateDTO bannerUpdateDTO);
 | 
			
		||||
    ProductRecommendDO convert(ProductRecommendUpdateDTO recommendUpdateDTO);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,28 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz.convert;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
 | 
			
		||||
import org.mapstruct.Mapper;
 | 
			
		||||
import org.mapstruct.Mappings;
 | 
			
		||||
import org.mapstruct.factory.Mappers;
 | 
			
		||||
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Mapper
 | 
			
		||||
public interface PromotionActivityConvert {
 | 
			
		||||
 | 
			
		||||
    PromotionActivityConvert INSTANCE = Mappers.getMapper(PromotionActivityConvert.class);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    PromotionActivityBO convertToBO(PromotionActivityDO activity);
 | 
			
		||||
 | 
			
		||||
    @Mappings({})
 | 
			
		||||
    List<PromotionActivityBO> convertToBO(List<PromotionActivityDO> activityList);
 | 
			
		||||
 | 
			
		||||
//    @Mappings({})
 | 
			
		||||
//    PromotionActivityDO convert(PromotionActivityAddDTO activityAddDTO);
 | 
			
		||||
//
 | 
			
		||||
//    @Mappings({})
 | 
			
		||||
//    PromotionActivityDO convert(PromotionActivityUpdateDTO activityUpdateDTO);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,19 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz.dao;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
 | 
			
		||||
import org.apache.ibatis.annotations.Param;
 | 
			
		||||
import org.springframework.stereotype.Repository;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Repository
 | 
			
		||||
public interface PromotionActivityMapper {
 | 
			
		||||
 | 
			
		||||
    PromotionActivityDO selectById(@Param("id") Integer id);
 | 
			
		||||
 | 
			
		||||
    List<PromotionActivityDO> selectListByStatus(@Param("statuses")Collection<Integer> statuses);
 | 
			
		||||
 | 
			
		||||
    void insert(PromotionActivityDO activity);
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -58,11 +58,11 @@ public class PromotionActivityDO extends BaseDO {
 | 
			
		|||
    /**
 | 
			
		||||
     * 限制折扣字符串,使用 JSON 序列化成字符串存储
 | 
			
		||||
     */
 | 
			
		||||
    private String timeLimitedDiscount;
 | 
			
		||||
    private TimeLimitedDiscount timeLimitedDiscount;
 | 
			
		||||
    /**
 | 
			
		||||
     * 满减送字符串,使用 JSON 序列化成字符串存储
 | 
			
		||||
     */
 | 
			
		||||
    private String fullPrivilege;
 | 
			
		||||
    private FullPrivilege fullPrivilege;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 限制折扣
 | 
			
		||||
| 
						 | 
				
			
			@ -173,6 +173,10 @@ public class PromotionActivityDO extends BaseDO {
 | 
			
		|||
         * 指定可用商品列表
 | 
			
		||||
         */
 | 
			
		||||
        private List<Integer> rangeValues;
 | 
			
		||||
        /**
 | 
			
		||||
         * 是否循环
 | 
			
		||||
         */
 | 
			
		||||
        private Boolean cycled;
 | 
			
		||||
        /**
 | 
			
		||||
         * 优惠数组
 | 
			
		||||
         */
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -0,0 +1,12 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz.mybatis;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.common.framework.mybatis.JSONTypeHandler;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
 | 
			
		||||
 | 
			
		||||
public class TestHandler extends JSONTypeHandler<PromotionActivityDO.TimeLimitedDiscount> {
 | 
			
		||||
 | 
			
		||||
    public TestHandler(Class<PromotionActivityDO.TimeLimitedDiscount> clazz) {
 | 
			
		||||
        super(clazz);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,85 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz.service;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.common.framework.vo.CommonResult;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.PromotionActivityService;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.bo.PromotionActivityBO;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PromotionActivityTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.RangeTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.convert.PromotionActivityConvert;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dao.PromotionActivityMapper;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.stereotype.Service;
 | 
			
		||||
import org.springframework.util.Assert;
 | 
			
		||||
 | 
			
		||||
import java.util.Collection;
 | 
			
		||||
import java.util.Collections;
 | 
			
		||||
import java.util.Iterator;
 | 
			
		||||
import java.util.List;
 | 
			
		||||
 | 
			
		||||
@Service // 实际上不用添加。添加的原因是,必须 Spring 报错提示
 | 
			
		||||
@com.alibaba.dubbo.config.annotation.Service(validation = "true")
 | 
			
		||||
public class PromotionActivityServiceImpl implements PromotionActivityService {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private PromotionActivityMapper promotionActivityMapper;
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CommonResult<List<PromotionActivityBO>> getPromotionActivityListBySpuId(Integer spuId, Collection<Integer> activityStatuses) {
 | 
			
		||||
        return this.getPromotionActivityListBySpuIds(Collections.singleton(spuId), activityStatuses);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Override
 | 
			
		||||
    public CommonResult<List<PromotionActivityBO>> getPromotionActivityListBySpuIds(Collection<Integer> spuIds, Collection<Integer> activityStatuses) {
 | 
			
		||||
        if (spuIds.isEmpty() || activityStatuses.isEmpty()) {
 | 
			
		||||
            return CommonResult.success(Collections.emptyList());
 | 
			
		||||
        }
 | 
			
		||||
        // 查询指定状态的促销活动
 | 
			
		||||
        List<PromotionActivityDO> activityList = promotionActivityMapper.selectListByStatus(activityStatuses);
 | 
			
		||||
        if (activityList.isEmpty()) {
 | 
			
		||||
            return CommonResult.success(Collections.emptyList());
 | 
			
		||||
        }
 | 
			
		||||
        // 匹配商品
 | 
			
		||||
        for (Iterator<PromotionActivityDO> iterator = activityList.iterator(); iterator.hasNext();) {
 | 
			
		||||
            PromotionActivityDO activity = iterator.next();
 | 
			
		||||
            boolean matched = false;
 | 
			
		||||
            for (Integer spuId : spuIds) {
 | 
			
		||||
                if (PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType())) {
 | 
			
		||||
                    matched = isSpuMatchTimeLimitDiscount(spuId, activity);
 | 
			
		||||
                } else if (PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType())) {
 | 
			
		||||
                    matched = isSpuMatchFullPrivilege(spuId, activity);
 | 
			
		||||
                }
 | 
			
		||||
                if (matched) {
 | 
			
		||||
                    break;
 | 
			
		||||
                }
 | 
			
		||||
            }
 | 
			
		||||
            // 不匹配,则进行移除
 | 
			
		||||
            if (!matched) {
 | 
			
		||||
                iterator.remove();
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        // 返回最终结果
 | 
			
		||||
        return CommonResult.success(PromotionActivityConvert.INSTANCE.convertToBO(activityList));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isSpuMatchTimeLimitDiscount(Integer spuId, PromotionActivityDO activity) {
 | 
			
		||||
        Assert.isTrue(PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue().equals(activity.getActivityType()),
 | 
			
		||||
                "传入的必须的促销活动必须是限时折扣");
 | 
			
		||||
        return activity.getTimeLimitedDiscount().getItems().stream()
 | 
			
		||||
                .anyMatch(item -> spuId.equals(item.getSpuId()));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private boolean isSpuMatchFullPrivilege(Integer spuId, PromotionActivityDO activity) {
 | 
			
		||||
        Assert.isTrue(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue().equals(activity.getActivityType()),
 | 
			
		||||
                "传入的必须的促销活动必须是满减送");
 | 
			
		||||
        PromotionActivityDO.FullPrivilege fullPrivilege = activity.getFullPrivilege();
 | 
			
		||||
        if (RangeTypeEnum.ALL.getValue().equals(fullPrivilege.getRangeType())) {
 | 
			
		||||
            return true;
 | 
			
		||||
        } else if (RangeTypeEnum.PRODUCT_INCLUDE_PART.getValue().equals(fullPrivilege.getRangeType())) {
 | 
			
		||||
            return fullPrivilege.getRangeValues().contains(spuId);
 | 
			
		||||
        } else {
 | 
			
		||||
            throw new IllegalArgumentException(String.format("促销活动(%s) 可用范围的类型是不正确", activity.toString()));
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,131 @@
 | 
			
		|||
<?xml version="1.0" encoding="UTF-8" ?>
 | 
			
		||||
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
 | 
			
		||||
<mapper namespace="cn.iocoder.mall.promotion.biz.dao.PromotionActivityMapper">
 | 
			
		||||
 | 
			
		||||
    <sql id="FIELDS">
 | 
			
		||||
        id, title, activity_type, status, start_time,
 | 
			
		||||
        end_time, invalid_time, delete_time, time_limited_discount, full_privilege,
 | 
			
		||||
        create_time, update_time
 | 
			
		||||
    </sql>
 | 
			
		||||
 | 
			
		||||
    <resultMap id="PromotionActivityResultMap" type="PromotionActivityDO">
 | 
			
		||||
        <result property="timeLimitedDiscount" column="time_limited_discount" javaType="cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO$TimeLimitedDiscount" typeHandler="cn.iocoder.common.framework.mybatis.JSONTypeHandler"/>
 | 
			
		||||
        <result property="fullPrivilege" column="full_privilege" javaType="cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO$FullPrivilege" typeHandler="cn.iocoder.common.framework.mybatis.JSONTypeHandler"/>
 | 
			
		||||
    </resultMap>
 | 
			
		||||
 | 
			
		||||
<!--    <select id="selectListByPidAndStatusOrderBySort" resultType="PromotionActivityDO">-->
 | 
			
		||||
<!--        SELECT-->
 | 
			
		||||
<!--            <include refid="FIELDS" />-->
 | 
			
		||||
<!--        FROM banner-->
 | 
			
		||||
<!--        WHERE pid = #{pid}-->
 | 
			
		||||
<!--        AND status = #{status}-->
 | 
			
		||||
<!--        AND deleted = 0-->
 | 
			
		||||
<!--        ORDER BY sort ASC-->
 | 
			
		||||
<!--    </select>-->
 | 
			
		||||
 | 
			
		||||
<!--    <select id="selectList" resultType="PromotionActivityDO">-->
 | 
			
		||||
<!--        SELECT-->
 | 
			
		||||
<!--          <include refid="FIELDS" />-->
 | 
			
		||||
<!--        FROM banner-->
 | 
			
		||||
<!--        WHERE deleted = 0-->
 | 
			
		||||
<!--    </select>-->
 | 
			
		||||
 | 
			
		||||
    <select id="selectById" parameterType="Integer" resultMap="PromotionActivityResultMap">
 | 
			
		||||
        SELECT
 | 
			
		||||
            <include refid="FIELDS" />
 | 
			
		||||
        FROM promotion_activity
 | 
			
		||||
        WHERE id = #{id}
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
    <select id="selectListByStatus" resultMap="PromotionActivityResultMap">
 | 
			
		||||
        SELECT
 | 
			
		||||
            <include refid="FIELDS" />
 | 
			
		||||
        FROM promotion_activity
 | 
			
		||||
        WHERE status IN
 | 
			
		||||
            <foreach item="status" collection="statuses" separator="," open="(" close=")" index="">
 | 
			
		||||
                #{status}
 | 
			
		||||
            </foreach>
 | 
			
		||||
    </select>
 | 
			
		||||
 | 
			
		||||
<!--    <select id="selectListByStatus" parameterType="Integer" resultType="PromotionActivityDO">-->
 | 
			
		||||
<!--        SELECT-->
 | 
			
		||||
<!--            <include refid="FIELDS" />-->
 | 
			
		||||
<!--        FROM banner-->
 | 
			
		||||
<!--        <where>-->
 | 
			
		||||
<!--            <if test="status != null">-->
 | 
			
		||||
<!--                status = #{status}-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            AND deleted = 0-->
 | 
			
		||||
<!--        </where>-->
 | 
			
		||||
<!--    </select>-->
 | 
			
		||||
 | 
			
		||||
<!--    <select id="selectListByTitleLike" resultType="PromotionActivityDO">-->
 | 
			
		||||
<!--        SELECT-->
 | 
			
		||||
<!--          <include refid="FIELDS" />-->
 | 
			
		||||
<!--        FROM banner-->
 | 
			
		||||
<!--        <where>-->
 | 
			
		||||
<!--            <if test="title != null">-->
 | 
			
		||||
<!--                title LIKE "%"#{title}"%"-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            AND deleted = 0-->
 | 
			
		||||
<!--        </where>-->
 | 
			
		||||
<!--        LIMIT #{offset}, #{limit}-->
 | 
			
		||||
<!--    </select>-->
 | 
			
		||||
 | 
			
		||||
<!--    <select id="selectCountByTitleLike" resultType="Integer">-->
 | 
			
		||||
<!--        SELECT-->
 | 
			
		||||
<!--          COUNT(1)-->
 | 
			
		||||
<!--        FROM banner-->
 | 
			
		||||
<!--        <where>-->
 | 
			
		||||
<!--            <if test="title != null">-->
 | 
			
		||||
<!--                title LIKE "%"#{title}"%"-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            AND deleted = 0-->
 | 
			
		||||
<!--        </where>-->
 | 
			
		||||
<!--    </select>-->
 | 
			
		||||
 | 
			
		||||
    <insert id="insert" parameterType="PromotionActivityDO" useGeneratedKeys="true" keyColumn="id" keyProperty="id">
 | 
			
		||||
        INSERT INTO promotion_activity (
 | 
			
		||||
          title, activity_type, status, start_time,
 | 
			
		||||
          end_time, invalid_time, delete_time,
 | 
			
		||||
          time_limited_discount,
 | 
			
		||||
          full_privilege,
 | 
			
		||||
          create_time
 | 
			
		||||
        ) VALUES (
 | 
			
		||||
          #{title}, #{activityType}, #{status}, #{startTime},
 | 
			
		||||
          #{endTime}, #{invalidTime}, #{deleteTime},
 | 
			
		||||
          #{timeLimitedDiscount, typeHandler=cn.iocoder.common.framework.mybatis.JSONTypeHandler},
 | 
			
		||||
          #{fullPrivilege, typeHandler=cn.iocoder.common.framework.mybatis.JSONTypeHandler},
 | 
			
		||||
          #{createTime}
 | 
			
		||||
        )
 | 
			
		||||
    </insert>
 | 
			
		||||
 | 
			
		||||
<!--    <update id="update" parameterType="PromotionActivityDO">-->
 | 
			
		||||
<!--        UPDATE banner-->
 | 
			
		||||
<!--        <set>-->
 | 
			
		||||
<!--            <if test="title != null">-->
 | 
			
		||||
<!--                title = #{title},-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="url != null">-->
 | 
			
		||||
<!--                url = #{url},-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="picUrl != null">-->
 | 
			
		||||
<!--                pic_url = #{picUrl} ,-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="sort != null">-->
 | 
			
		||||
<!--                sort = #{sort},-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="status != null">-->
 | 
			
		||||
<!--                status = #{status},-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="memo != null">-->
 | 
			
		||||
<!--                memo = #{memo},-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--            <if test="deleted != null">-->
 | 
			
		||||
<!--                deleted = #{deleted}-->
 | 
			
		||||
<!--            </if>-->
 | 
			
		||||
<!--        </set>-->
 | 
			
		||||
<!--        WHERE id = #{id}-->
 | 
			
		||||
<!--    </update>-->
 | 
			
		||||
 | 
			
		||||
</mapper>
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,7 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz;
 | 
			
		||||
 | 
			
		||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
 | 
			
		||||
 | 
			
		||||
@SpringBootApplication(scanBasePackages = {"cn.iocoder.mall.promotion"})
 | 
			
		||||
public class Application {
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,94 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz.dao;
 | 
			
		||||
 | 
			
		||||
import cn.iocoder.common.framework.util.DateUtil;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PreferentialTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PromotionActivityStatusEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.PromotionActivityTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.api.constant.RangeTypeEnum;
 | 
			
		||||
import cn.iocoder.mall.promotion.biz.dataobject.PromotionActivityDO;
 | 
			
		||||
import org.junit.Ignore;
 | 
			
		||||
import org.junit.Test;
 | 
			
		||||
import org.junit.runner.RunWith;
 | 
			
		||||
import org.springframework.beans.factory.annotation.Autowired;
 | 
			
		||||
import org.springframework.boot.test.context.SpringBootTest;
 | 
			
		||||
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
 | 
			
		||||
 | 
			
		||||
import java.util.ArrayList;
 | 
			
		||||
import java.util.Calendar;
 | 
			
		||||
import java.util.Date;
 | 
			
		||||
 | 
			
		||||
@RunWith(SpringJUnit4ClassRunner.class)
 | 
			
		||||
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.NONE)
 | 
			
		||||
public class PromotionActivityMapperTest {
 | 
			
		||||
 | 
			
		||||
    @Autowired
 | 
			
		||||
    private PromotionActivityMapper promotionActivityMapper;
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 插入限时折扣活动
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    @Ignore
 | 
			
		||||
    public void testInsert01() {
 | 
			
		||||
        // 创建 PromotionActivityDO 对象
 | 
			
		||||
        PromotionActivityDO activityDO = new PromotionActivityDO();
 | 
			
		||||
        activityDO.setTitle("老板跑路了");
 | 
			
		||||
        activityDO.setActivityType(PromotionActivityTypeEnum.TIME_LIMITED_DISCOUNT.getValue());
 | 
			
		||||
        activityDO.setStatus(PromotionActivityStatusEnum.RUN.getValue());
 | 
			
		||||
        activityDO.setStartTime(new Date());
 | 
			
		||||
        activityDO.setEndTime(DateUtil.addDate(new Date(), Calendar.DAY_OF_YEAR, 100));
 | 
			
		||||
        activityDO.setCreateTime(new Date());
 | 
			
		||||
        // 创建 TimeLimitedDiscount 对象
 | 
			
		||||
        PromotionActivityDO.TimeLimitedDiscount discount = new PromotionActivityDO.TimeLimitedDiscount();
 | 
			
		||||
        discount.setQuota(0);
 | 
			
		||||
        discount.setItems(new ArrayList<>());
 | 
			
		||||
        PromotionActivityDO.TimeLimitedDiscount.Item item01 = new PromotionActivityDO.TimeLimitedDiscount.Item();
 | 
			
		||||
        item01.setSpuId(32);
 | 
			
		||||
        item01.setPreferentialType(PreferentialTypeEnum.DISCOUNT.getValue());
 | 
			
		||||
        item01.setPreferentialValue(40);
 | 
			
		||||
        discount.getItems().add(item01);
 | 
			
		||||
        activityDO.setTimeLimitedDiscount(discount);
 | 
			
		||||
        promotionActivityMapper.insert(activityDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 插入满减送活动
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testInsert02() {
 | 
			
		||||
        // 创建 PromotionActivityDO 对象
 | 
			
		||||
        PromotionActivityDO activityDO = new PromotionActivityDO();
 | 
			
		||||
        activityDO.setTitle("老四赶海");
 | 
			
		||||
        activityDO.setActivityType(PromotionActivityTypeEnum.FULL_PRIVILEGE.getValue());
 | 
			
		||||
        activityDO.setStatus(PromotionActivityStatusEnum.RUN.getValue());
 | 
			
		||||
        activityDO.setStartTime(new Date());
 | 
			
		||||
        activityDO.setEndTime(DateUtil.addDate(new Date(), Calendar.DAY_OF_YEAR, 100));
 | 
			
		||||
        activityDO.setCreateTime(new Date());
 | 
			
		||||
        // 创建 TimeLimitedDiscount 对象
 | 
			
		||||
        PromotionActivityDO.FullPrivilege fullPrivilege = new PromotionActivityDO.FullPrivilege();
 | 
			
		||||
        fullPrivilege.setRangeType(RangeTypeEnum.ALL.getValue());
 | 
			
		||||
        fullPrivilege.setCycled(Boolean.FALSE);
 | 
			
		||||
        fullPrivilege.setPrivileges(new ArrayList<>());
 | 
			
		||||
        PromotionActivityDO.FullPrivilege.Privilege privilege01 = new PromotionActivityDO.FullPrivilege.Privilege();
 | 
			
		||||
        privilege01.setMeetType(1); // TODO 芋艿,硬编码
 | 
			
		||||
        privilege01.setMeetValue(20);
 | 
			
		||||
        privilege01.setPreferentialType(PreferentialTypeEnum.DISCOUNT.getValue());
 | 
			
		||||
        privilege01.setPreferentialValue(80);
 | 
			
		||||
        fullPrivilege.getPrivileges().add(privilege01);
 | 
			
		||||
        activityDO.setFullPrivilege(fullPrivilege);
 | 
			
		||||
        promotionActivityMapper.insert(activityDO);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * 查询促销活动
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testSelectById() {
 | 
			
		||||
        PromotionActivityDO activity01 = promotionActivityMapper.selectById(1);
 | 
			
		||||
        System.out.println(activity01);
 | 
			
		||||
 | 
			
		||||
        PromotionActivityDO activity02 = promotionActivityMapper.selectById(2);
 | 
			
		||||
        System.out.println(activity02);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1 @@
 | 
			
		|||
package cn.iocoder.mall.promotion.biz;
 | 
			
		||||
		Loading…
	
		Reference in New Issue