基于mall4j产品的二开项目后端
lee
2024-12-19 82f2918ca71ff69ea657cfb6285ce7b531d75001
init
38 files added
3140 ■■■■■ changed files
yami-shop-discount/pom.xml 26 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/pom.xml 28 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/config/SwaggerConfiguration.java 35 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/controller/DiscountController.java 113 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/dto/DiscountSumDto.java 34 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/dto/DiscountSumItemDto.java 53 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/listener/SubmitOrderActivityListener.java 66 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/manager/impl/DiscountShopCartManagerImpl.java 191 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/service/DiscountManagerService.java 39 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/service/impl/DiscountManagerServiceImpl.java 323 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/pom.xml 21 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/constants/DiscountStatusEnum.java 39 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountItemMapper.java 36 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountMapper.java 171 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountProdMapper.java 36 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/listener/ShopChangeStatusListener.java 61 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/Discount.java 84 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/DiscountItem.java 49 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/DiscountProd.java 70 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountItemService.java 21 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountProdService.java 21 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountService.java 202 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountItemServiceImpl.java 29 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountProdServiceImpl.java 29 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountServiceImpl.java 335 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountItemMapper.xml 30 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountMapper.xml 219 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountProdMapper.xml 27 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-multishop/pom.xml 29 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/config/SwaggerConfiguration.java 35 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/controller/DiscountController.java 225 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/controller/DiscountProdController.java 74 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/listener/UpdateDiscountProdListener.java 47 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-platform/pom.xml 29 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/config/SwaggerConfiguration.java 34 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/controller/DiscountController.java 150 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/controller/DiscountProdController.java 70 ●●●●● patch | view | raw | blame | history
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/task/DiscountTask.java 59 ●●●●● patch | view | raw | blame | history
yami-shop-discount/pom.xml
New file
@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>yami-shop</artifactId>
        <groupId>com.yami.shop</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>yami-shop-discount</artifactId>
    <description>商城满减模块</description>
    <packaging>pom</packaging>
    <modules>
        <module>yami-shop-discount-api</module>
        <module>yami-shop-discount-common</module>
        <module>yami-shop-discount-multishop</module>
        <module>yami-shop-discount-platform</module>
    </modules>
</project>
yami-shop-discount/yami-shop-discount-api/pom.xml
New file
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>yami-shop-discount</artifactId>
        <groupId>com.yami.shop</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>yami-shop-discount-api</artifactId>
    <description>商城满减模块接口部分</description>
    <dependencies>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-discount-common</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-security-api</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
    </dependencies>
</project>
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/config/SwaggerConfiguration.java
New file
@@ -0,0 +1,35 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.config;
import lombok.AllArgsConstructor;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author yami
 */
@Configuration("dicountSwaggerConfiguration")
@AllArgsConstructor
public class SwaggerConfiguration {
    @Bean
    public GroupedOpenApi discountRestApi() {
        return GroupedOpenApi.builder()
                .group("满减活动接口")
                .packagesToScan("com.yami.shop.discount.api.controller")
                .pathsToMatch("/**")
                .build();
    }
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/controller/DiscountController.java
New file
@@ -0,0 +1,113 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.enums.DiscountRule;
import com.yami.shop.bean.model.Product;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.Arith;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.service.ProductService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
/**
 * 促销活动接口
 * @author yami
 */
@RestController
@RequestMapping("/marking/discount")
@Tag(name = "促销活动接口")
public class DiscountController {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private ProductService productService;
    @GetMapping("/getDiscountByProdId")
    @Operation(summary = "通过商品id获取商品所有促销活动" , description = "通过商品id获取商品所有促销活动")
    @Parameter(name = "prodId", description = "商品Id" , required = true)
    public ServerResponseEntity<List<DiscountDto>> getDiscountByProdId(@RequestParam(value = "prodId") Long prodId) {
        Product product = productService.getById(prodId);
        List<DiscountDto> discountDtoList = discountService.listByProdId(prodId,product.getShopId());
        return ServerResponseEntity.success(discountDtoList);
    }
    @GetMapping("/prodList")
    @Operation(summary = "限时特惠(获取满减下的商品列表)" , description = "获取限时特惠商品列表")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<IPage<ProductDto>> discountProdList(PageParam<ProductDto> page,@RequestParam(value = "discountId") Long discountId) {
        IPage<ProductDto> productDtoPage = discountService.discountProdList(page,discountId, I18nMessage.getDbLang());
        List<ProductDto> records = productDtoPage.getRecords();
        if (CollectionUtil.isNotEmpty(records)) {
            Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
            // 最优惠的那个满减项
            DiscountItem discountItem = discount.getDiscountItems().get(0);
            // 折扣百分比
            double discountProportion;
            // 打折
            if (Objects.equals(discount.getDiscountRule(), DiscountRule.M2D.value()) || Objects.equals(discount.getDiscountRule(), DiscountRule.P2D.value())) {
                discountProportion = Arith.sub(1,Arith.div(discountItem.getDiscount(), 10L));
            } else {
                discountProportion = Arith.div(discountItem.getDiscount(), discountItem.getNeedAmount());
            }
            for (ProductDto prod : records) {
                // 将要减去的价格
                double subPrice = Arith.mul(prod.getPrice(), discountProportion);
                // 如果有活动价格上限
                if (discount.getMaxReduceAmount() != null && subPrice > discount.getMaxReduceAmount()) {
                    subPrice = discount.getMaxReduceAmount();
                }
                //价格最低只能为0.01
                double activityPrice = Arith.sub(prod.getPrice(), subPrice);
                if (activityPrice<0.01){
                    activityPrice = 0.01;
                }
                prod.setActivityPrice(activityPrice);
            }
        }
        return ServerResponseEntity.success(productDtoPage);
    }
    @GetMapping("/getDiscountList")
    @Operation(summary = "获取促销活动列表" , description = "获取促销活动列表")
    public ServerResponseEntity<IPage<DiscountDto>> getDiscountList(PageParam<Discount> page) {
        IPage<DiscountDto> productDtoPage = discountService.getDiscountList(page);
        return ServerResponseEntity.success(productDtoPage);
    }
    @GetMapping("/getDiscountByDiscountId/{discountId}")
    @Operation(summary = "获取促销活动详情" , description = "通过活动id获取促销活动详情")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<DiscountDto> getDiscountByDiscountId(@PathVariable("discountId") Long discountId) {
        DiscountDto discountDto = discountService.getDiscountByDiscountId(discountId);
        if (Objects.isNull(discountDto)){
            // 该活动不存在或者已结束
            throw new YamiShopBindException("yami.activity.cannot.find.check");
        }
        return ServerResponseEntity.success(discountDto);
    }
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/dto/DiscountSumDto.java
New file
@@ -0,0 +1,34 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.Map;
/**
 * 某个店铺的满江活动的活动信息
 * @author LGH
 */
@Data
public class DiscountSumDto implements Serializable {
    /**
     * 以优惠活动id为key, 优惠活动金额 为value的map
     * {优惠活动id:优惠活动金额}
     */
    private Map<Long, DiscountSumItemDto> discountIdDiscountSumItemMap;
    /**
     * 所有优惠活动加起来的金额
     */
    private double totalReduceAmount = 0.0;
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/dto/DiscountSumItemDto.java
New file
@@ -0,0 +1,53 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.dto;
import lombok.Data;
import java.io.Serializable;
/**
 * 每个满减活动的活动信息
 * @author LGH
 */
@Data
public class DiscountSumItemDto implements Serializable {
    /**
     * 需要商品金额/数量
     */
    private double needAmount = 0.00;
    /**
     * 优惠项
     */
    private Long discountItemId = -1L;
    /**
     * 优惠(元/折)
     */
    private double discount = 0.00;
    /**
     * 该满减活动实际优惠金额
     * 如果活动优惠金额有上限,且优惠金额达到了上限,
     * 那么这个金额就是活动优惠金额的上限
     */
    private double reduceAmount = 0.0;
    /**
     * 参与满减满折优惠的商品数量
     */
    private int prodCount = 0;
    /**
     * 参与满减满折优惠的商品金额
     */
    private double prodsPrice = 0.00;
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/listener/SubmitOrderActivityListener.java
New file
@@ -0,0 +1,66 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.listener;
import cn.hutool.core.collection.CollUtil;
import com.yami.shop.bean.app.dto.ChooseDiscountItemDto;
import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
import com.yami.shop.bean.app.dto.ShopCartOrderDto;
import com.yami.shop.bean.event.SubmitOrderActivityEvent;
import com.yami.shop.bean.order.SubmitOrderActivityOrder;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.discount.common.dao.DiscountMapper;
import lombok.AllArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.Set;
/**
 * 提交订单时检查下满减活动状态
 * @author LGH
 */
@Component("CheckDiscountStatusListener")
@AllArgsConstructor
public class SubmitOrderActivityListener {
    private final DiscountMapper discountMapper;
    @EventListener(SubmitOrderActivityEvent.class)
    @Order(SubmitOrderActivityOrder.DISCOUNT)
    public void checkDiscountStatusListener(SubmitOrderActivityEvent event) {
        List<ShopCartOrderDto> shopCartOrders = event.getShopCartOrderMergerDto().getShopCartOrders();
        Set<Long> discountIds = new HashSet<>();
        //获取已经选择的所有满减活动
        for (ShopCartOrderDto shopCartOrder : shopCartOrders) {
            List<ShopCartItemDiscountDto> shopCartItemDiscounts = shopCartOrder.getShopCartItemDiscounts();
            if (CollUtil.isNotEmpty(shopCartItemDiscounts)) {
                for (ShopCartItemDiscountDto shopCartItemDiscount : shopCartItemDiscounts) {
                    ChooseDiscountItemDto chooseDiscountItemDto = shopCartItemDiscount.getChooseDiscountItemDto();
                    if (Objects.nonNull(chooseDiscountItemDto)) {
                        discountIds.add(chooseDiscountItemDto.getDiscountId());
                    }
                }
            }
        }
        if (CollUtil.isEmpty(discountIds)) {
            return;
        }
        int num = discountMapper.countNormalDiscount(discountIds);
        if (num < discountIds.size()) {
            //当前选择的满减活动已经过期,请返回重新提交订单
            throw new YamiShopBindException("yami.order.discount.expire.check");
        }
    }
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/manager/impl/DiscountShopCartManagerImpl.java
New file
@@ -0,0 +1,191 @@
/*
 * Copyright (c) 2018-2999 广州亚米信息科技有限公司 All rights reserved.
 *
 * https://www.gz-yami.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.manager.impl;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.BooleanUtil;
import com.google.common.collect.Lists;
import com.yami.shop.bean.app.dto.ChooseDiscountItemDto;
import com.yami.shop.bean.app.dto.ShopCartDto;
import com.yami.shop.bean.app.dto.ShopCartItemDiscountDto;
import com.yami.shop.bean.app.dto.ShopCartItemDto;
import com.yami.shop.bean.enums.OrderActivityType;
import com.yami.shop.bean.vo.ShopCartWithAmountVO;
import com.yami.shop.common.util.Arith;
import com.yami.shop.discount.api.dto.DiscountSumDto;
import com.yami.shop.discount.api.service.DiscountManagerService;
import com.yami.shop.manager.DiscountShopCartManager;
import lombok.AllArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.*;
import java.util.stream.Collectors;
/**
 * 默认的购物车链进行组装时的操作
 *
 * @author FrozenWatermelon
 */
@Component
@AllArgsConstructor
public class DiscountShopCartManagerImpl implements DiscountShopCartManager {
    private final DiscountManagerService discountManagerService;
    /**
     * 计算满减,并组合购物车 + 购物车金额
     *
     * @param shopCartWithAmount 购物车列表和金额信息
     */
    @Override
    public void calculateDiscountAndMakeUpShopCartAndAmount(ShopCartWithAmountVO shopCartWithAmount) {
        List<ShopCartDto> shopCarts = shopCartWithAmount.getShopCarts();
        shopCarts = calculateDiscountAndMakeUpShopCart(shopCarts);
        shopCartWithAmount.setShopCarts(shopCarts);
    }
    /**
     * 计算满减,并重新组合购物车
     * @param shopCarts 购物项
     * @return void
     */
    @Override
    public List<ShopCartDto> calculateDiscountAndMakeUpShopCart(List<ShopCartDto> shopCarts) {
        List<ShopCartDto> newShopCart = new ArrayList<>();
        for (ShopCartDto shopCart : shopCarts) {
            List<ShopCartItemDto> canDiscountShopCartItems = shopCart.getShopCartItemDiscounts()
                    .get(0).getShopCartItems().stream().filter(shopCartItemDto ->
                        // 套餐不参与满减
                        (Objects.isNull(shopCartItemDto.getComboId()) || shopCartItemDto.getComboId() == 0)
//                        // 预售不参与满减
//                        && (Objects.isNull(shopCartItemDto.getPreSellStatus()) || Objects.equals(shopCartItemDto.getPreSellStatus(), 0))
                    )
                    .collect(Collectors.toList());
            if (CollUtil.isEmpty(canDiscountShopCartItems)) {
                newShopCart.add(shopCart);
            } else {
                newShopCart.add(reBuildShopCart(shopCart, canDiscountShopCartItems));
            }
        }
        return newShopCart;
    }
    /**
     * 重新组装购物车
     */
    private ShopCartDto reBuildShopCart(ShopCartDto shopCart, List<ShopCartItemDto> shopCartItems) {
        // 购物车项顺序
        List<Long> cartOrderArr = shopCartItems.stream().map(ShopCartItemDto::getBasketId).collect(Collectors.toList());
        // 订单项金额从小到大排序
        shopCartItems = shopCartItems.stream().sorted(Comparator.comparingDouble(ShopCartItemDto::getActualTotal)).collect(Collectors.toList());
        shopCart.setShopReduce(0.0);
        shopCart.setDiscountReduce(0.0);
        // 1.获取总的优惠金额
        // 2.将商品活动进行保存
        DiscountSumDto discountSum = discountManagerService.calculateDiscount(shopCart.getShopId(), shopCartItems);
        List<ShopCartItemDiscountDto> shopCartItemDiscountVOList = Lists.newArrayList();
        // 对数据进行组装
        // 通过活动项id划分,获取有活动的商品项
        Map<Long, List<ShopCartItemDto>> hasDiscountShopCartItemMap = shopCartItems.stream().collect(Collectors.groupingBy(ShopCartItemDto::getDiscountId));
        double reduce = 0.0;
        double total = 0.0;
        int totalCount = 0;
        for (Long discountId : hasDiscountShopCartItemMap.keySet()) {
            // 获取该列表
            List<ShopCartItemDto> shopCartItemList = hasDiscountShopCartItemMap.get(discountId);
            ChooseDiscountItemDto chooseDiscountItemDto = discountManagerService.getChooseDiscountItemDto(discountSum, discountId);
            double discountAmount = 0.0;
            // 分摊优惠大于订单项金额的数量
            double shareReduceBiggerThenShopItemAmount = 0;
            // 计算分摊优惠金额
            for (int index = 0; index < shopCartItemList.size(); index++) {
                ShopCartItemDto shopCartItem = shopCartItemList.get(index);
                total = Arith.add(total, shopCartItem.getProductTotalAmount());
                totalCount += shopCartItem.getProdCount();
                if (!BooleanUtil.isTrue(shopCartItem.getIsChecked()) && Objects.nonNull(shopCartItem.getIsChecked())) {
                    continue;
                }
                double shareReduce = 0.0;
                if (Objects.isNull(chooseDiscountItemDto)) {
                    shopCartItem.setShareReduce(shareReduce);
                    shopCartItem.setActualTotal(shopCartItem.getActualTotal() - shareReduce);
                    continue;
                }
                double reduceAmount = Arith.roundByBanker(chooseDiscountItemDto.getReduceAmount(),2);
                if (index + 1 == shopCartItems.size()) {
                    shareReduce = reduceAmount - discountAmount;
                    // 如果分到最后一项的钱太多了,比订单项目金额都要多,订单项的分摊金额就当作订单项金额咯
                    if (shareReduce > shopCartItem.getActualTotal()) {
                        shareReduceBiggerThenShopItemAmount = Arith.sub(shareReduce, shopCartItem.getActualTotal());
                        shareReduce = shopCartItem.getActualTotal();
                        // 给得太多了,减少一点,以免变成负数
                        chooseDiscountItemDto.setReduceAmount(Arith.sub(reduceAmount, shareReduceBiggerThenShopItemAmount));
                    }
                } else {
                    // 分摊金额 = 该优惠活动优惠金额 * (商品金额 / 参与该活动的商品总金额)
                    shareReduce = Arith.roundByBanker(Arith.mul(chooseDiscountItemDto.getReduceAmount(), Arith.div(shopCartItem.getProductTotalAmount(),
                            chooseDiscountItemDto.getProdsPrice())),2);
                }
                // 第一个优惠就是满减
                shopCartItem.setShareReduce(shareReduce);
                shopCartItem.setDiscountAmount(shareReduce);
                shopCartItem.setActualTotal(Arith.sub(shopCartItem.getActualTotal(), shareReduce));
                discountAmount = Arith.add(discountAmount, shareReduce);
            }
            ShopCartItemDiscountDto shopCartItemDiscount = new ShopCartItemDiscountDto();
            // 不要把这行放到上面,因为上面会重新计算该值
            if (chooseDiscountItemDto != null) {
                reduce = Arith.add(reduce, Arith.roundByBanker(chooseDiscountItemDto.getReduceAmount(),2));
                shopCartItemDiscount.setActivityType(OrderActivityType.DISCOUNT.value());
            }
            // 把购物车项的商品顺序还原
            shopCartItemList.sort(Comparator.comparingInt(prev -> cartOrderArr.indexOf(prev.getBasketId())));
            shopCartItemDiscount.setShopCartItems(shopCartItemList);
            shopCartItemDiscount.setChooseDiscountItemDto(chooseDiscountItemDto);
            shopCartItemDiscountVOList.add(shopCartItemDiscount);
        }
        // 倒序排序,将拥有优惠活动的商品放到最上面
        Collections.reverse(shopCartItemDiscountVOList);
        // 除满减外其他活动的商品构建
        buildOtherShopCartItem(shopCart, shopCartItemDiscountVOList);
        shopCart.setShopCartItemDiscounts(shopCartItemDiscountVOList);
        shopCart.setTotal(total);
        shopCart.setTotalCount(totalCount);
        shopCart.setActualTotal(Arith.sub(total, reduce));
        shopCart.setDiscountReduce(reduce);
        // 最开始的店铺满减
        shopCart.setShopReduce(reduce);
        return shopCart;
    }
    private void buildOtherShopCartItem(ShopCartDto shopCart, List<ShopCartItemDiscountDto> shopCartItemDiscountVOList) {
        List<ShopCartItemDto> comboShopCartItemList = shopCart.getShopCartItemDiscounts().get(0).getShopCartItems().stream().filter(shopCartItemDto ->
                (Objects.nonNull(shopCartItemDto.getComboId()) && shopCartItemDto.getComboId() != 0)).collect(Collectors.toList());
        if (CollUtil.isNotEmpty(comboShopCartItemList)) {
            shopCartItemDiscountVOList.add(new ShopCartItemDiscountDto(comboShopCartItemList));
        }
//        List<ShopCartItemDto> preSellShopCartItemList = shopCart.getShopCartItemDiscounts().get(0).getShopCartItems().stream().filter(shopCartItemDto ->
//                (Objects.nonNull(shopCartItemDto.getPreSellStatus()) && Objects.equals(shopCartItemDto.getPreSellStatus(), 1))).collect(Collectors.toList());
//        if (CollUtil.isNotEmpty(preSellShopCartItemList)) {
//            shopCartItemDiscountVOList.add(new ShopCartItemDiscountDto(preSellShopCartItemList));
//        }
    }
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/service/DiscountManagerService.java
New file
@@ -0,0 +1,39 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.service;
import com.yami.shop.bean.app.dto.ChooseDiscountItemDto;
import com.yami.shop.bean.app.dto.ProductItemDto;
import com.yami.shop.discount.api.dto.DiscountSumDto;
import java.util.List;
/**
 * @author yami
 */
public interface DiscountManagerService {
    /**
     * 计算折扣活动信息
     * @param shopId 店铺ID
     * @param productItems 商品列表
     * @return 某个店铺的满江活动的活动信息
     */
    DiscountSumDto calculateDiscount(Long shopId, List<? extends ProductItemDto> productItems);
    /**
     * 根据折扣活动ID获得选择的折扣满减活动项信息
     * @param discountSumDto 某个店铺的满江活动的活动信息
     * @param discountId 折扣活动ID
     * @return 购物车中选中的满减活动项信息
     */
    ChooseDiscountItemDto getChooseDiscountItemDto(DiscountSumDto discountSumDto, Long discountId);
}
yami-shop-discount/yami-shop-discount-api/src/main/java/com/yami/shop/discount/api/service/impl/DiscountManagerServiceImpl.java
New file
@@ -0,0 +1,323 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.api.service.impl;
import cn.hutool.core.util.BooleanUtil;
import com.yami.shop.bean.app.dto.ChooseDiscountItemDto;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductItemDto;
import com.yami.shop.bean.enums.DiscountRule;
import com.yami.shop.common.config.Constant;
import com.yami.shop.common.util.Arith;
import com.yami.shop.discount.api.dto.DiscountSumDto;
import com.yami.shop.discount.api.dto.DiscountSumItemDto;
import com.yami.shop.discount.api.service.DiscountManagerService;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import ma.glasnost.orika.MapperFacade;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author yami
 */
@Service
public class DiscountManagerServiceImpl implements DiscountManagerService {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private MapperFacade mapperFacade;
    /**
     * 方法的作用
     * 1.计算优惠金额
     * 2. 并往productItems 设置满减优惠信息
     *
     * @param shopId
     * @param productItems
     * @return
     */
    @Override
    public DiscountSumDto calculateDiscount(Long shopId, List<? extends ProductItemDto> productItems) {
        DiscountSumDto discountSumDto = new DiscountSumDto();
        List<Discount> discounts = discountService.listDiscountsAndItemsByShopId(shopId);
        discounts = discounts.stream().sorted((Comparator.comparing(Discount::getMaxReduceAmount).reversed())).collect(Collectors.toList());
        // 旧的商品数据,也就是第一次传入的商品数据
        List<ProductItemDto> oldProdItems = new ArrayList<>(productItems);
        // 以优惠活动id为key, 每个满减活动的活动信息 为value的map
        // {优惠活动id:每个满减活动的活动信息}
        Map<Long, DiscountSumItemDto> discountIdDiscountSumItemMap = new HashMap<>(productItems.size());
        //保存满减的金额
        double amount = 0.0;
        if (productItems.size() == 1 && Objects.equals(productItems.get(0).getDiscountId(), 0L)) {
            // 立即提交订单, 选择满减最高的活动
            ProductItemDto productItemDto = productItems.get(0);
            for (Discount discount : discounts) {
                //判断商品是否参与该活动
                boolean hasDiscount = isHasDiscount(discount, productItemDto.getProdId());
                if (!hasDiscount) {
                    continue;
                }
                //添加到活动列表
                DiscountDto discountDto = new DiscountDto();
                discountDto.setDiscountName(discount.getDiscountName());
                discountDto.setDiscountId(discount.getDiscountId());
                productItemDto.getDiscounts().add(discountDto);
                //获取最高满减的活动
                DiscountSumItemDto discountSumItemDto = new DiscountSumItemDto();
                discountSumItemDto.setProdCount(productItemDto.getProdCount());
                if (BooleanUtil.isTrue(productItemDto.getIsChecked()) || Objects.isNull(productItemDto.getIsChecked())) {
                    discountSumItemDto.setProdsPrice(productItemDto.getProductTotalAmount());
                } else {
                    discountSumItemDto.setProdsPrice(0.00);
                }
                findDiscountItemAndGetReduceAmount(discount, discountSumItemDto);
                discountIdDiscountSumItemMap.put(discount.getDiscountId(), discountSumItemDto);
                //该活动的满减金额大于前一个,则把满减活动替换成当前的活动
                if (productItemDto.getDiscountId() == 0 || amount < discountSumItemDto.getReduceAmount()){
                    productItemDto.setDiscountId(discount.getDiscountId());
                    discountSumDto.setTotalReduceAmount(discountSumItemDto.getReduceAmount());
                    amount = discountSumItemDto.getReduceAmount();
                }
            }
        } else {
            //购物车
            for (Discount discount : discounts) {
                // 获取当前优惠的商品
                List<ProductItemDto> hasCurrentDiscountProds = mergeDiscountProd(oldProdItems, discount);
                DiscountSumItemDto discountSumItemDto = new DiscountSumItemDto();
                // 计算参与该活动的所有商品的商品金额,商品数量
                for (ProductItemDto hasDiscountProd : hasCurrentDiscountProds) {
                    if (Objects.isNull(hasDiscountProd.getIsChecked()) || hasDiscountProd.getIsChecked()){
                        discountSumItemDto.setProdCount(discountSumItemDto.getProdCount() + hasDiscountProd.getProdCount());
                        discountSumItemDto.setProdsPrice(Arith.add(discountSumItemDto.getProdsPrice(), hasDiscountProd.getProductTotalAmount()));
                    }
                }
                findDiscountItemAndGetReduceAmount(discount, discountSumItemDto);
                discountIdDiscountSumItemMap.put(discount.getDiscountId(), discountSumItemDto);
                discountSumDto.setTotalReduceAmount(Arith.add(discountSumDto.getTotalReduceAmount(), discountSumItemDto.getReduceAmount()));
            }
        }
        discountSumDto.setDiscountIdDiscountSumItemMap(discountIdDiscountSumItemMap);
        return discountSumDto;
    }
    @Override
    public ChooseDiscountItemDto getChooseDiscountItemDto(DiscountSumDto discountSumDto, Long discountId) {
        ChooseDiscountItemDto chooseDiscountItemDto = null;
        // 参与活动
        if (discountId != -1 && discountId != 0) {
            Map<Long, DiscountSumItemDto> discountIdDiscountSumItemMap = discountSumDto.getDiscountIdDiscountSumItemMap();
            DiscountSumItemDto discountSumItemDto = discountIdDiscountSumItemMap.get(discountId);
            if (discountSumItemDto == null) {
                return null;
            }
            Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
            chooseDiscountItemDto = mapperFacade.map(discount, ChooseDiscountItemDto.class);
            chooseDiscountItemDto.setProdsPrice(discountSumItemDto.getProdsPrice());
            chooseDiscountItemDto.setProdCount(discountSumItemDto.getProdCount());
            chooseDiscountItemDto.setNeedAmount(discountSumItemDto.getNeedAmount());
            chooseDiscountItemDto.setDiscountItemId(discountSumItemDto.getDiscountItemId());
            chooseDiscountItemDto.setDiscount(discountSumItemDto.getDiscount());
            chooseDiscountItemDto.setReduceAmount(discountSumItemDto.getReduceAmount());
        }
        return chooseDiscountItemDto;
    }
    /**
     * 1. 找出当前优惠活动属于那个优惠项
     * 2. 计算满减
     * @param discount 优惠活动
     * @return 优惠金额
     */
    private DiscountSumItemDto findDiscountItemAndGetReduceAmount(Discount discount, DiscountSumItemDto discountSumItemDto) {
        // 枚举DiscountRule(0 满钱减钱 1满件减钱 2 满钱打折 3满件打折)
        Integer discountRule = discount.getDiscountRule();
        // 减免类型 0按满足最高层级减一次 1每满一次减一次
        Integer discountType = discount.getDiscountType();
        List<DiscountItem> discountItems = discount.getDiscountItems();
        double prodCount = discountSumItemDto.getProdCount();
        double prodsPrice = discountSumItemDto.getProdsPrice();
        Double totalReduceAmount = 0.0;
        Long discountItemId = -1L;
        for (DiscountItem discountItem : discountItems) {
            // 优惠(元/折)
            Double reduceAmount = discountItem.getDiscount();
            discountItemId = discountItem.getDiscountItemId();
            Double needAmount = discountItem.getNeedAmount();
            discountSumItemDto.setNeedAmount(needAmount);
            discountSumItemDto.setDiscount(reduceAmount);
            // 当规则为满钱减钱时
            if (Objects.equals(discountRule, DiscountRule.M2M.value()) && prodsPrice >= needAmount) {
                totalReduceAmount = getTotalReduceAmountBySubMoney(discountType, prodsPrice, reduceAmount, needAmount);
                break;
            }
            // 当规则为满件减钱时
            else if (Objects.equals(discountRule, DiscountRule.P2M.value()) && prodCount >= needAmount) {
                // 当商品价格大于最高层级时
                totalReduceAmount = getTotalReduceAmountBySubMoney(discountType, prodCount, reduceAmount, needAmount);
                break;
            }
            // 当规则为满钱打折时
            else if (Objects.equals(discountRule, DiscountRule.M2D.value()) && prodsPrice >= needAmount) {
                totalReduceAmount = Arith.mul(prodsPrice, Arith.sub(1, Arith.div(reduceAmount, 10L)));
                break;
            }
            // 当规则为满件打折时
            else if (Objects.equals(discountRule, DiscountRule.P2D.value()) && prodCount >= needAmount) {
                totalReduceAmount = Arith.mul(prodsPrice, Arith.sub(1, Arith.div(reduceAmount, 10L)));
                break;
            }
        }
        // 如果是每满一次减一次,则需要判断上限 不为满钱减钱或者满足最高层级减一次情况下,需要判断上限
        if(discountType != 0 || !Objects.equals(discount.getDiscountRule(),0)) {
            totalReduceAmount = Math.min(discount.getMaxReduceAmount(), totalReduceAmount);
        }
        totalReduceAmount = Arith.roundByBanker(totalReduceAmount,2);
        discountSumItemDto.setReduceAmount(totalReduceAmount);
        // 如果满减折扣小于0.01元,就不要给商品满减
        if (Double.compare(discountSumItemDto.getReduceAmount(), Constant.MIN_PRODUCT_AMOUNT) < 0) {
            discountSumItemDto.setReduceAmount(0.0);
        }
        discountSumItemDto.setDiscountItemId(discountItemId);
        return discountSumItemDto;
    }
    private Double getTotalReduceAmountBySubMoney(Integer discountType, double prodsPriceOrCount, Double reduceAmount, Double needAmount) {
        Double totalReduceAmount;
        if (discountType == 0) {
            // 当商品价格大于最高层级时
            totalReduceAmount = reduceAmount;
        } else {
            // 查看满足多少次条件
            int multi = (int) Math.floor(Arith.div(prodsPriceOrCount, needAmount));
            totalReduceAmount = Arith.mul(reduceAmount, multi);
        }
        return totalReduceAmount;
    }
    /**
     * 合并当前优惠的商品
     * 注:如果oldProdItems商品有该优惠,则将其抽取出来,并且在oldProdItems中移除该商品
     *
     * @param oldProdItems 商品项
     * @param discount     活动
     * @return
     */
    private List<ProductItemDto> mergeDiscountProd(List<ProductItemDto> oldProdItems, Discount discount) {
        // 拥有当前优惠活动的商品
        List<ProductItemDto> hasCurrentDiscountProds = new ArrayList<>();
        for (ProductItemDto productItem : oldProdItems) {
            // 查看商品是否包含优惠活动
            boolean hasDiscount = isHasDiscount(discount, productItem.getProdId());
            if (hasDiscount) {
                DiscountDto discountDto = new DiscountDto();
                discountDto.setDiscountId(discount.getDiscountId());
                discountDto.setDiscountName(discount.getDiscountName());
                productItem.getDiscounts().add(discountDto);
            }
            // 商品选择了这个活动
            boolean prodChooseDiscount = Objects.equals(productItem.getDiscountId(), discount.getDiscountId());
            // 如果商品不包含该优惠活动
            // 或用户选择不参与该活动,将活动清除
            if (!hasDiscount && prodChooseDiscount) {
                //  将往productItems 设置满减优惠信息为【-1】
                productItem.setDiscountId(-1L);
            }
            // 如果 商品参与了该活动,将会分为几个步骤
            // 判断商品是否在这个活动中(如果用户没有为商品主动选择优惠活动,或选择的优惠活动与该满减的优惠活动一样 则视为商品在活动中)
            else if (hasDiscount && prodChooseDiscount) {
                // 2. 将往productItems 设置满减优惠信息为【当前活动】
                productItem.setDiscountId(discount.getDiscountId());
                // 3. 将该活动的商品统一起来
                hasCurrentDiscountProds.add(productItem);
            }
            // 如果商品参与该活动
            // 且用户没有主动放弃该活动
            else if (hasDiscount && productItem.getDiscountId() == 0) {
                // 2. 将往productItems 设置满减优惠信息为【当前活动】
                productItem.setDiscountId(discount.getDiscountId());
                // 3. 将该活动的商品统一起来
                hasCurrentDiscountProds.add(productItem);
            }
        }
        return hasCurrentDiscountProds;
    }
    /**
     * 查看商品是否包含优惠活动
     *
     * @param discount 当前优惠活动信息
     * @param prodId   当前商品id
     * @return
     */
    private boolean isHasDiscount(Discount discount, Long prodId) {
        boolean hasDiscount = false;
        List<DiscountProd> discountProds = discount.getDiscountProds();
        // 所有商品都含有该优惠
        if (discount.getSuitableProdType() == 0) {
            hasDiscount = true;
        } else if (discount.getSuitableProdType() == 1) {
            // 指定商品含有该优惠
            for (DiscountProd discountProd : discountProds) {
                if (Objects.equals(discountProd.getProdId(), prodId)) {
                    hasDiscount = true;
                }
            }
        } else {
            // 指定商品不含有该优惠
            for (DiscountProd discountProd : discountProds) {
                if (Objects.equals(discountProd.getProdId(), prodId)) {
                    hasDiscount = true;
                }
            }
            hasDiscount = !hasDiscount;
        }
        return hasDiscount;
    }
}
yami-shop-discount/yami-shop-discount-common/pom.xml
New file
@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>yami-shop-discount</artifactId>
        <groupId>com.yami.shop</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>
    <artifactId>yami-shop-discount-common</artifactId>
    <dependencies>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-security-common</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
    </dependencies>
</project>
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/constants/DiscountStatusEnum.java
New file
@@ -0,0 +1,39 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.constants;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
 * 满减满折活动状态枚举
 * @author yami
 */
@Getter
@AllArgsConstructor
public enum DiscountStatusEnum {
    /**  关闭    */
    CLOSE(0, "关闭"),
    /**  启动    */
    RUN(1, "启动"),
    /**  违规下线    */
    OFFLINE(2, "违规下线"),
    /**  平台审核    */
    PLATFORM_AUDIT(3, "平台审核");
    private Integer value;
    private String desc;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountItemMapper.java
New file
@@ -0,0 +1,36 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.model.DiscountProd;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author LGH
 */
public interface DiscountItemMapper extends BaseMapper<DiscountItem> {
    /**
     * 批量插入折扣优惠类目
     * @param discountItems 折扣优惠类目列表
     */
    void insertDiscountItems(@Param("discountItems") List<DiscountItem> discountItems);
    /**
     * 根据商品ID批量获取折扣商品列表
     * @param prodIds
     * @return 折扣商品列表
     */
    List<DiscountProd> listDiscountByProdIds(@Param("prodIds") List<Long> prodIds);
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountMapper.java
New file
@@ -0,0 +1,171 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountProd;
import org.apache.ibatis.annotations.Param;
import java.util.List;
import java.util.Set;
/**
 * @author yami
 */
public interface DiscountMapper extends BaseMapper<Discount> {
    /**
     * 通过折扣活动ID获取满减满折优惠信息
     * @param discountId 折扣活动ID
     * @return 满减满折优惠
     */
    Discount getDiscountAndItemAndProdById(@Param("discountId") Long discountId);
    /**
     * 通过店铺ID获取满减满折优惠列表
     * @param shopId
     * @return 满减满折优惠列表
     */
    List<Discount> getDiscountsAndItemsByShopId(@Param("shopId") Long shopId);
    /**
     * 根据满减满折优惠ID与店铺ID删除满减满折优惠信息
     * @param id 满减满折优惠ID
     * @param shopId 店铺ID
     * @return 成功删除的数量
     */
    int deleteDiscounts(@Param("id") Long id, @Param("shopId") Long shopId);
    /**
     * 根据商品ID与店铺ID批量获取减满折优惠信息
     * @param prodId 商品ID
     * @param shopId 店铺ID
     * @return 减满折优惠信息列表
     */
    List<DiscountDto> listByProdId(@Param("prodId") Long prodId, @Param("shopId") Long shopId);
    /**
     * 分页获取折扣商品信息
     * @param page 分页参数
     * @param discount 减满折优惠信息
     * @param dbLang 当前语言
     * @return 商品列表
     */
    IPage<ProductDto> discountProdList(PageParam<ProductDto> page, @Param("discount") Discount discount, @Param("dbLang") Integer dbLang);
    /**
     * 分页获取平台折扣信息
     * @param page 分页参数
     * @param discount 减满折优惠信息
     * @return 减满折优惠信息列表
     */
    IPage<Discount> getPlatformDiscountPage(@Param("page") PageParam<Discount> page, @Param("discount") Discount discount);
    /**
     * 通过折扣ID更新折扣状态
     * @param discountId 减满折优惠ID
     * @param status 状态
     * @return 成功更新数量
     */
    int updateStatusByDiscountId(@Param("discountId") Long discountId, @Param("status") Integer status);
    /**
     * 分页获取折扣信息
     * @param page 分页参数
     * @return 折扣信息列表
     */
    IPage<DiscountDto> getDiscountList(PageParam<Discount> page);
    /**
     * 根据折扣ID获取折扣信息
     * @param discountId 折扣ID
     * @return 折扣信息
     */
    DiscountDto getDiscountByDiscountId(@Param("discountId") Long discountId);
    /**
     * 通过更改状态关闭折扣
     */
    void closeDiscountBySetStatus();
    /**
     * 获取要被定时任务关闭的满减活动的店铺id
     * @return
     */
    List<Long> listShopIdsWhichWillClose();
    /**
     * 根据店铺ID获取店铺参与类型为所有商品参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return 数量
     */
    Integer countProdIsSeckillAll(@Param("shopId") Long shopId, @Param("prodId") Long prodId);
    /**
     * 根据店铺ID与商品ID获取店铺参与类型为指定商品参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return 数量
     */
    Integer countProdIsSeckillSome(@Param("shopId") Long shopId,@Param("prodId") Long prodId);
    /**
     * 根据店铺ID与商品ID获取店铺参与类型为指定商品不参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return 数量
     */
    Integer countProdIsSeckillExcept(@Param("shopId") Long shopId, @Param("prodId") Long prodId);
    /**
     * 批量获取店铺下不是折扣商品的商品列表
     * @param shopId 店铺ID
     * @return 折扣商品列表
     */
    List<DiscountProd> getProdIsSeckillExcept(@Param("shopId") Long shopId);
    /**
     * 根据折扣分页获取商品列表
     * @param page 分页参数
     * @param product 商品参数
     * @param discount 折扣优惠信息
     * @return 商品列表
     */
    IPage<Product> pageProdByDiscount(PageParam<Product> page, @Param("product") ProductParam product, @Param("discount") Discount discount);
    /**
     * 根据店铺id与活动状态获取活动id集合
     * @param shopId
     * @param status
     * @return
     */
    List<Long> listIdByShopIdAndStatus(@Param("shopId") Long shopId, @Param("status") Integer status);
    /**
     * 获取正常的活动数目
     * @param discountIds
     * @return
     */
    int countNormalDiscount(@Param("discountIds")Set<Long> discountIds);
    /**
     * 获取在这一分钟内开始的满减活动对应的店铺id,清除缓存
     * @return
     */
    List<Long> listShopIdsWhichStart();
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/dao/DiscountProdMapper.java
New file
@@ -0,0 +1,36 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.yami.shop.discount.common.model.DiscountProd;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
 * @author yami
 */
public interface DiscountProdMapper extends BaseMapper<DiscountProd> {
    /**
     * 批量插入折扣商品信息
     * @param discountProds 满减满折商品关联列表
     */
    void insertDiscountProds(@Param("discountProds") List<DiscountProd> discountProds);
    /**
     * 根据折扣ID批量获取折扣商品列表
     * @param discountId 折扣优惠ID
     * @return 满减满折商品关联列表
     */
    List<DiscountProd> getDiscountProdByDiscountId(@Param("discountId") Long discountId);
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/listener/ShopChangeStatusListener.java
New file
@@ -0,0 +1,61 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.listener;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yami.shop.bean.enums.ShopStatus;
import com.yami.shop.bean.event.ShopChangeStatusEvent;
import com.yami.shop.discount.common.constants.DiscountStatusEnum;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.service.DiscountService;
import lombok.AllArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import java.util.List;
import java.util.Objects;
/**
 * 店铺改变状态监听
 *
 * @Author lth
 * @Date 2021/11/22 15:04
 */
@Component("discountShopChangeStatusListener")
@AllArgsConstructor
public class ShopChangeStatusListener {
    private final DiscountService discountService;
    @EventListener(ShopChangeStatusEvent.class)
    public void discountShopChangeStatusListener(ShopChangeStatusEvent event) {
        Long shopId = event.getShopId();
        ShopStatus shopStatus = event.getShopStatus();
        if (Objects.isNull(shopId) || Objects.isNull(shopStatus)) {
            return;
        }
        if (Objects.equals(shopStatus, ShopStatus.OFFLINE)) {
            // 店铺下线时,把所有满减活动失效
            List<Long> discountIds = discountService.listIdByShopIdAndStatus(shopId, DiscountStatusEnum.RUN.getValue());
            if (CollUtil.isEmpty(discountIds)) {
                return;
            }
            // 失效活动
            discountService.update(Wrappers.lambdaUpdate(Discount.class)
                    .set(Discount::getStatus, DiscountStatusEnum.CLOSE.getValue())
                    .in(Discount::getDiscountId, discountIds)
            );
            // 清除缓存
            discountIds.forEach(discountService::removeDiscountAndItemAndProdCacheById);
            discountService.removeDiscountsAndItemsCacheByShopId(shopId);
        }
    }
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/Discount.java
New file
@@ -0,0 +1,84 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import org.springframework.format.annotation.DateTimeFormat;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
 * @author yami
 */
@Data
@TableName("tz_discount")
public class Discount implements Serializable {
    private static final long serialVersionUID = -82534421471027766L;
    @TableId
    @Schema(description = "满减满折优惠id" )
    private Long discountId;
    @Schema(description = "活动名称" )
    private String discountName;
    @Schema(description = "枚举DiscountRule(0 满钱减钱 1满件减钱 2 满钱打折 3满件打折)" )
    private Integer discountRule;
    @Schema(description = "减免类型 0按满足最高层级减一次 1每满一次减一次" )
    private Integer discountType;
    @Schema(description = "适用商品类型 0全部商品参与 1指定商品参与 2指定商品不参与" )
    private Integer suitableProdType;
    @Schema(description = "最多减多少" )
    private Double maxReduceAmount;
    @Schema(description = "开始时间" )
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date startTime;
    @Schema(description = "结束时间" )
    @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date endTime;
    @Schema(description = "活动状态:1 开启 0 关闭" )
    private Integer status;
    @Schema(description = "手机端活动图片" )
    private String mobilePic;
    @Schema(description = "pc端活动列表图片" )
    private String pcPic;
    @Schema(description = "pc端活动背景图片" )
    private String pcBackgroundPic;
    @Schema(description = "店铺ID" )
    private Long shopId;
    @Schema(description = "店铺名称" )
    @TableField(exist = false)
    private String shopName;
    @Schema(description = "满减满折优惠项" )
    @TableField(exist = false)
    private List<DiscountItem> discountItems;
    @Schema(description = "满减满折商品" )
    @TableField(exist = false)
    private List<DiscountProd> discountProds;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/DiscountItem.java
New file
@@ -0,0 +1,49 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.model;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;
/**
 * @author yami
 */
@Data
@TableName("tz_discount_item")
public class DiscountItem implements Serializable {
    private static final long serialVersionUID = -35634815438365118L;
    /**
     * 满减满折优惠项id
     */
    @TableId
    private Long discountItemId;
    /**
     * 满减满折优惠id
     */
    private Long discountId;
    /**
     * 所需需要金额
     */
    private Double needAmount;
    /**
     * 优惠(元/折)
     */
    private Double discount;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/model/DiscountProd.java
New file
@@ -0,0 +1,70 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.model;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.yami.shop.common.serializer.json.ImgJsonSerializer;
import lombok.Data;
import java.io.Serializable;
/**
 * @author yami
 */
@Data
@TableName("tz_discount_prod")
public class DiscountProd implements Serializable {
    private static final long serialVersionUID = -5694537556987462303L;
    /**
     * 满减 商品 联合id
     */
    @TableId
    private Long discountProdId;
    /**
     * 满减id
     */
    private Long discountId;
    /**
     * 商品id
     */
    private Long prodId;
    /**
     * 商品主图
     */
    @TableField(exist = false)
    @JsonSerialize(using = ImgJsonSerializer.class)
    private String pic;
    /**
     * 商品图片列表
     */
    @TableField(exist = false)
    private String imgs;
    /**
     * 商品名称
     */
    @TableField(exist = false)
    private String prodName;
    /**
     * 商品状态
     */
    @TableField(exist = false)
    private Integer prodStatus;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountItemService.java
New file
@@ -0,0 +1,21 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.discount.common.model.DiscountItem;
/**
 *
 * @author lgh on 2018/11/21.
 */
public interface DiscountItemService extends IService<DiscountItem> {
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountProdService.java
New file
@@ -0,0 +1,21 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.discount.common.model.DiscountProd;
/**
 *
 * @author lgh on 2018/11/21.
 */
public interface DiscountProdService extends IService<DiscountProd> {
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/DiscountService.java
New file
@@ -0,0 +1,202 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.IService;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import javax.validation.Valid;
import java.util.List;
/**
 * @author lgh on 2018/11/21.
 */
public interface DiscountService extends IService<Discount> {
    /**
     * 插入满减满折优惠信息与满减满折优惠商品
     * @param discount 满减满折优惠信息
     */
    void insertDiscountAndItemAndProd(@Valid Discount discount);
    /**
     * 更新满减满折优惠信息与满减满折优惠商品
     * @param discount 满减满折优惠信息
     */
    void updateDiscountAndItemAndProd(@Valid Discount discount);
    /**
     * 删除满减满折优惠信息与满减满折优惠商品
     * @param discountId 满减满折优惠ID
     * @param shopId 店铺ID
     */
    void deleteDiscountsAndItemsAndProds(Long discountId, Long shopId);
    /**
     * 通过ID获取折扣和折扣商品
     * @param discountId 满减满折优惠ID
     * @return 满减满折优惠信息
     */
    Discount getDiscountAndItemAndProdById(Long discountId);
    /**
     * 根据ID删除满减满折优惠信息和满减满折优惠商品的缓存
     * @param discountId 满减满折优惠ID
     */
    void removeDiscountAndItemAndProdCacheById(Long discountId);
    /**
     * 根据店铺ID批量获取折扣和折扣商品
     * @param shopId 店铺ID
     * @return 满减满折优惠列表
     */
    List<Discount> listDiscountsAndItemsByShopId(Long shopId);
    /**
     * 通过商店ID删除折扣和折扣商品缓存
     * @param shopId 店铺ID
     */
    void removeDiscountsAndItemsCacheByShopId(Long shopId);
    /**
     * 根据商品ID批量获取折扣信息
     * @param prodId 商品ID
     * @param shopId 店铺ID
     * @return 满减满折优惠列表
     */
    List<DiscountDto> listByProdId(Long prodId, Long shopId);
    /**
     * 根据折扣ID分页获取折扣商品列表
     * @param page 分页参数
     * @param discountId 满减满折优惠ID
     * @param dbLang 当前选择语言
     * @return 商品列表
     */
    IPage<ProductDto> discountProdList(PageParam<ProductDto> page, Long discountId, Integer dbLang);
    /**
     * 分页获取平台满减数据
     * @param page 分页参数
     * @param discount 满减满折优惠
     * @return 满减满折优惠列表
     */
    IPage<Discount> getPlatformDiscountPage(PageParam<Discount> page, Discount discount);
    /**
     * 平台 --下线活动
     * @param discount 满减满折优惠信息
     * @param offlineReason 下线原因
     * @param sysUserId 系统用户ID
     */
    void offline(Discount discount, String offlineReason, Long sysUserId);
    /**
     * 商家 --申请活动
     * @param eventId 事件ID
     * @param discountId 满减满折优惠ID
     * @param reapplyReason 申请原因
     */
    void auditApply(Long eventId, Long discountId, String reapplyReason);
    /**
     * 平台 --审核活动
     * @param offlineHandleEventAuditParam
     * @param sysUserId 系统用户ID
     */
    void auditDiscount(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId);
    /**
     * 分页获取折扣信息
     * @param page 分页参数
     * @return 折扣信息列表
     */
    IPage<DiscountDto> getDiscountList(PageParam<Discount> page);
    /**
     * 根据ID获取折扣信息
     * @param discountId 折扣信息ID
     * @return 折扣信息
     */
    DiscountDto getDiscountByDiscountId(Long discountId);
    /**
     * 通过更改状态关闭折扣
     */
    void closeDiscountBySetStatus();
    /**
     * 根据店铺ID获取店铺参与类型为所有商品参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return
     */
    Integer countProdIsSeckillAll(Long shopId, Long prodId);
    /**
     * 根据店铺ID与商品ID获取店铺参与类型为指定商品参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return 数量
     */
    Integer countProdIsSeckillSome(Long shopId, Long prodId);
    /**
     * 根据店铺ID与商品ID获取店铺参与类型为指定商品不参与的折扣活动数量
     * @param shopId 店铺ID
     * @param prodId 商品ID
     * @return 数量
     */
    Integer countProdIsSeckillExcept(Long shopId, Long prodId);
    /**
     * 批量获取店铺下不是折扣商品的商品ID列表
     * @param shopId 店铺ID
     * @return 商品ID列表
     */
    List<Long> getProdIsSeckillExcept(Long shopId);
    /**
     * 根据折扣分页获取商品列表
     * @param page 分页参数
     * @param product 商品参数
     * @param discount 满减满折优惠信息
     * @return 商品列表
     */
    IPage<Product> pageProdByDiscount(PageParam<Product> page, ProductParam product, Discount discount);
    /**
     * 修改满减活动的可用商品列表
     * @param prodIds 商品ID列表
     * @return 可以商品ID列表
     */
    List<Long> updateDiscountProdByIds(List<Long> prodIds);
    /**
     * 获取要被定时任务关闭的满减活动的店铺id
     * @return
     */
    List<Long> listShopIdsWhichWillClose();
    /**
     * 根据店铺id与活动状态获取活动id集合
     * @param shopId
     * @param status
     * @return
     */
    List<Long> listIdByShopIdAndStatus(Long shopId, Integer status);
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountItemServiceImpl.java
New file
@@ -0,0 +1,29 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.discount.common.dao.DiscountItemMapper;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.service.DiscountItemService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 *
 * @author lgh on 2018/11/21.
 */
@Service
public class DiscountItemServiceImpl extends ServiceImpl<DiscountItemMapper, DiscountItem> implements DiscountItemService {
    @Autowired
    private DiscountItemMapper discountItemMapper;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountProdServiceImpl.java
New file
@@ -0,0 +1,29 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.discount.common.dao.DiscountProdMapper;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountProdService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
 *
 * @author lgh on 2018/11/21.
 */
@Service
public class DiscountProdServiceImpl extends ServiceImpl<DiscountProdMapper, DiscountProd> implements DiscountProdService {
    @Autowired
    private DiscountProdMapper discountProdMapper;
}
yami-shop-discount/yami-shop-discount-common/src/main/java/com/yami/shop/discount/common/service/impl/DiscountServiceImpl.java
New file
@@ -0,0 +1,335 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.common.service.impl;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yami.shop.bean.app.dto.DiscountDto;
import com.yami.shop.bean.app.dto.ProductDto;
import com.yami.shop.bean.enums.OfflineHandleEventStatus;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.model.OfflineHandleEvent;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.constants.DiscountStatusEnum;
import com.yami.shop.discount.common.dao.DiscountItemMapper;
import com.yami.shop.discount.common.dao.DiscountMapper;
import com.yami.shop.discount.common.dao.DiscountProdMapper;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountItem;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.service.OfflineHandleEventService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.validation.Valid;
import java.util.*;
import java.util.stream.Collectors;
/**
 * @author lgh on 2018/11/21.
 */
@Service
@AllArgsConstructor
public class DiscountServiceImpl extends ServiceImpl<DiscountMapper, Discount> implements DiscountService {
    private final DiscountMapper discountMapper;
    private final DiscountItemMapper discountItemMapper;
    private final DiscountProdMapper discountProdMapper;
    private final OfflineHandleEventService offlineHandleEventService;
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discount.discountId"),
            @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#discount.shopId")
    })
    public void updateDiscountAndItemAndProd(@Valid Discount discount) {
        Discount selectById = discountMapper.selectById(discount.getDiscountId());
        if (selectById.getStatus() > DiscountStatusEnum.RUN.getValue() && discount.getStatus() < DiscountStatusEnum.OFFLINE.getValue()){
            // 该活动已被平台下线,不能再更改状态
            throw new YamiShopBindException("yami.activity.status.check");
        }
        if (!Objects.equals(selectById.getShopId(), discount.getShopId())) {
            throw new YamiShopBindException("当前活动信息不属于您的店铺");
        }
        discountMapper.updateById(discount);
        discountItemMapper.delete(new LambdaQueryWrapper<DiscountItem>().eq(DiscountItem::getDiscountId, discount.getDiscountId()));
        List<DiscountItem> discountItems = discount.getDiscountItems();
        insertDiscountItems(discount, discountItems);
        discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().eq(DiscountProd::getDiscountId, discount.getDiscountId()));
        if (discount.getSuitableProdType() != 0) {
            List<DiscountProd> discountProds = discount.getDiscountProds();
            for (DiscountProd discountProd : discountProds) {
                discountProd.setDiscountId(discount.getDiscountId());
            }
            if (CollectionUtil.isNotEmpty(discountProds)) {
                discountProdMapper.insertDiscountProds(discountProds);
            }
        }
    }
    /**
     * 插入满减项
     * @param discount
     * @param discountItems
     */
    private void insertDiscountItems(@Valid Discount discount, List<DiscountItem> discountItems) {
        for (DiscountItem discountItem : discountItems) {
            if (discountItem.getNeedAmount() == null || discountItem.getDiscount() == null) {
                // 请填写完整满减信息
                throw new YamiShopBindException("yami.activity.info.check");
            }
            discountItem.setDiscountId(discount.getDiscountId());
        }
        discountItemMapper.insertDiscountItems(discountItems);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#discount.shopId")
    public void insertDiscountAndItemAndProd(@Valid Discount discount) {
        discountMapper.insert(discount);
        List<DiscountItem> discountItems = discount.getDiscountItems();
        if (CollectionUtil.isEmpty(discountItems)) {
            // 活动项不能为空,最少要有一个活动项
            throw new YamiShopBindException("yami.activity.item.check");
        }
        insertDiscountItems(discount, discountItems);
        if (discount.getSuitableProdType() != 0) {
            List<DiscountProd> discountProds = discount.getDiscountProds();
            if (CollectionUtil.isNotEmpty(discountProds)) {
                for (DiscountProd discountProd : discountProds) {
                    discountProd.setDiscountId(discount.getDiscountId());
                }
                discountProdMapper.insertDiscountProds(discountProds);
            }
        }
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    @Caching(evict = {
            @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discountId"),
            @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    })
    public void deleteDiscountsAndItemsAndProds(Long discountId, Long shopId) {
        int i = discountMapper.deleteDiscounts(discountId, shopId);
        if (i > 0) {
            discountItemMapper.delete(new LambdaQueryWrapper<DiscountItem>().eq(DiscountItem::getDiscountId, discountId));
            discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().eq(DiscountProd::getDiscountId, discountId));
        }
    }
    @Override
    @Cacheable(cacheNames = "DiscountAndItemAndProd", key = "#discountId")
    public Discount getDiscountAndItemAndProdById(Long discountId) {
        return discountMapper.getDiscountAndItemAndProdById(discountId);
    }
    @Override
    @CacheEvict(cacheNames = "DiscountAndItemAndProd", key = "#discountId")
    public void removeDiscountAndItemAndProdCacheById(Long discountId) {
    }
    @Override
    @Cacheable(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    public List<Discount> listDiscountsAndItemsByShopId(Long shopId) {
        return discountMapper.getDiscountsAndItemsByShopId(shopId);
    }
    @Override
    @CacheEvict(cacheNames = "DiscountAndItemAndProdByShopId", key = "#shopId")
    public void removeDiscountsAndItemsCacheByShopId(Long shopId) {
    }
    @Override
    public List<DiscountDto> listByProdId(Long prodId, Long shopId) {
        return discountMapper.listByProdId(prodId,shopId);
    }
    @Override
    public IPage<ProductDto> discountProdList(PageParam<ProductDto> page, Long discountId, Integer dbLang) {
        Discount discount = getOne(new LambdaQueryWrapper<Discount>().eq(Discount::getDiscountId, discountId)
                .eq(Discount::getStatus, 1).le(Discount::getStartTime, new Date()).gt(Discount::getEndTime, new Date()));
        Map<Long, Long> prodMap = new LinkedHashMap<>();
        if(Objects.isNull(discount)){
            discount = new Discount();
            discount.setDiscountId(0L);
        }else{
            List<DiscountProd> discountProds = discountProdMapper.getDiscountProdByDiscountId(discountId);
            prodMap = discountProds.stream().collect(Collectors.toMap(DiscountProd::getDiscountProdId, DiscountProd::getProdId));
        }
        IPage<ProductDto> iPage = discountMapper.discountProdList(page, discount, dbLang);
        if(discount.getSuitableProdType() == null || discount.getSuitableProdType() != 1){
            return iPage;
        }
        if(!prodMap.isEmpty()){
            List<ProductDto> prodList = new ArrayList<>();
            Map<Long, ProductDto> prodMaps = iPage.getRecords().stream().collect(Collectors.toMap(ProductDto::getProdId, s->s));
            for(Map.Entry<Long, Long> entry : prodMap.entrySet()){
                if(prodMaps.containsKey(entry.getValue())){
                    prodList.add(prodMaps.get(entry.getValue()));
                }
            }
            iPage.setRecords(prodList);
        }
        return iPage;
    }
    @Override
    public IPage<Discount> getPlatformDiscountPage(PageParam<Discount> page, Discount discount) {
        return discountMapper.getPlatformDiscountPage(page, discount);
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void offline(Discount discount, String offlineReason, Long sysUserId) {
        Date now = new Date();
        OfflineHandleEvent offlineHandleEvent = new OfflineHandleEvent();
        offlineHandleEvent.setHandleId(discount.getDiscountId());
        offlineHandleEvent.setHandleType(OfflineHandleEventType.DISCOUNT.getValue());
        offlineHandleEvent.setHandlerId(sysUserId);
        offlineHandleEvent.setCreateTime(now);
        offlineHandleEvent.setOfflineReason(offlineReason);
        offlineHandleEvent.setShopId(discount.getShopId());
        offlineHandleEvent.setStatus(OfflineHandleEventStatus.OFFLINE_BY_PLATFORM.getValue());
        offlineHandleEvent.setUpdateTime(now);
        offlineHandleEventService.save(offlineHandleEvent);
        // 更新活动状态为下线
        discountMapper.updateStatusByDiscountId(discount.getDiscountId(), DiscountStatusEnum.OFFLINE.getValue());
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void auditApply(Long eventId, Long discountId, String reapplyReason) {
        // 更新活动为待审核状态
        discountMapper.updateStatusByDiscountId(discountId, DiscountStatusEnum.PLATFORM_AUDIT.getValue());
        // 更新事件为待审核
        offlineHandleEventService.updateToApply(eventId, reapplyReason);
    }
    @Override
    public void auditDiscount(OfflineHandleEventAuditParam offlineHandleEventAuditParam, Long sysUserId) {
        // 审核通过
        if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.AGREE_BY_PLATFORM.getValue())) {
            // 更新商品状态
            discountMapper.updateStatusByDiscountId(offlineHandleEventAuditParam.getHandleId(), DiscountStatusEnum.CLOSE.getValue());
        }
        // 审核不通过
        else if (Objects.equals(offlineHandleEventAuditParam.getStatus(), OfflineHandleEventStatus.DISAGREE_BY_PLATFORM.getValue())) {
            discountMapper.updateStatusByDiscountId(offlineHandleEventAuditParam.getHandleId(), DiscountStatusEnum.OFFLINE.getValue());
        }
        offlineHandleEventService.auditOfflineEvent(offlineHandleEventAuditParam, sysUserId);
    }
    @Override
    public IPage<DiscountDto> getDiscountList(PageParam<Discount> page) {
        return discountMapper.getDiscountList(page);
    }
    @Override
    public DiscountDto getDiscountByDiscountId(Long discountId) {
        return discountMapper.getDiscountByDiscountId(discountId);
    }
    @Override
    public void closeDiscountBySetStatus() {
        discountMapper.closeDiscountBySetStatus();
    }
    @Override
    public Integer countProdIsSeckillAll(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillAll(shopId,prodId);
    }
    @Override
    public Integer countProdIsSeckillSome(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillSome(shopId,prodId);
    }
    @Override
    public Integer countProdIsSeckillExcept(Long shopId, Long prodId) {
        return discountMapper.countProdIsSeckillExcept(shopId,prodId);
    }
    @Override
    public List<Long> getProdIsSeckillExcept(Long shopId) {
        List<DiscountProd> list = discountMapper.getProdIsSeckillExcept(shopId);
        List<Long> prodIds = list.stream().map(t -> t.getProdId()).distinct().collect(Collectors.toList());
        return prodIds;
    }
    @Override
    public IPage<Product> pageProdByDiscount(PageParam<Product> page, ProductParam product, Discount discount) {
        IPage<Product> iPage = discountMapper.pageProdByDiscount(page, product, discount);
        if(discount.getSuitableProdType() != 1){
            return iPage;
        }
        List<DiscountProd> discountProds = discountProdMapper.getDiscountProdByDiscountId(discount.getDiscountId());
        Map<Long, Long> prodMap = discountProds.stream().collect(Collectors.toMap(DiscountProd::getDiscountProdId, DiscountProd::getProdId));
        List<Product> prodList = new ArrayList<>();
        Map<Long, Product> prodMaps = iPage.getRecords().stream().collect(Collectors.toMap(Product::getProdId, s->s));
        for(Map.Entry<Long, Long> entry : prodMap.entrySet()){
            if(prodMaps.containsKey(entry.getValue())){
                prodList.add(prodMaps.get(entry.getValue()));
            }
        }
        iPage.setRecords(prodList);
        return iPage;
    }
    @Override
    @Transactional(rollbackFor = Exception.class)
    public List<Long> updateDiscountProdByIds(List<Long> prodIds) {
        // 查询出所有为可用商品类型的满减活动的,包含需要处理商品id,进行删除
        List<DiscountProd> discountProd = discountItemMapper.listDiscountByProdIds(prodIds);
        List<Long> prodIdsDb = new ArrayList<>();
        List<Long> ids = new ArrayList<>();
        if(CollectionUtils.isNotEmpty(discountProd)){
            for (DiscountProd prod : discountProd) {
                ids.add(prod.getDiscountId());
                prodIdsDb.add(prod.getProdId());
            }
            discountProdMapper.delete(new LambdaQueryWrapper<DiscountProd>().in(DiscountProd::getProdId,prodIdsDb));
        }
        return ids;
    }
    @Override
    public List<Long> listShopIdsWhichWillClose() {
        return discountMapper.listShopIdsWhichWillClose();
    }
    @Override
    public List<Long> listIdByShopIdAndStatus(Long shopId, Integer status) {
        return discountMapper.listIdByShopIdAndStatus(shopId, status);
    }
}
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountItemMapper.xml
New file
@@ -0,0 +1,30 @@
<?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="com.yami.shop.discount.common.dao.DiscountItemMapper">
  <resultMap id="BaseResultMap" type="com.yami.shop.discount.common.model.DiscountItem">
    <!--
      WARNING - @mbg.generated
    -->
    <id column="discount_item_id" jdbcType="BIGINT" property="discountItemId" />
    <result column="discount_id" jdbcType="BIGINT" property="discountId" />
    <result column="need_amount" jdbcType="DECIMAL" property="needAmount" />
    <result column="discount" jdbcType="DECIMAL" property="discount" />
  </resultMap>
  <insert id="insertDiscountItems">
    insert into tz_discount_item (discount_id,need_amount,discount) values
    <foreach collection="discountItems" item="discountItem" separator=",">
    (#{discountItem.discountId},#{discountItem.needAmount},#{discountItem.discount})
    </foreach>
  </insert>
  <select id="listDiscountByProdIds" resultType="com.yami.shop.discount.common.model.DiscountProd">
    SELECT dp.`prod_id`,d.`discount_id` FROM tz_discount  d
    LEFT JOIN tz_discount_prod dp ON d.`discount_id` = dp.`discount_id`
    WHERE  d.`suitable_prod_type` = 1 AND dp.`prod_id` IN (
    <foreach collection="prodIds" separator="," item="prodId">
      #{prodId}
    </foreach>
    )
  </select>
</mapper>
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountMapper.xml
New file
@@ -0,0 +1,219 @@
<?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="com.yami.shop.discount.common.dao.DiscountMapper">
    <resultMap id="BaseResultMap" type="com.yami.shop.discount.common.model.Discount">
        <!--
          WARNING - @mbg.generated
        -->
        <id column="discount_id" jdbcType="BIGINT" property="discountId"/>
        <result column="discount_name" jdbcType="VARCHAR" property="discountName"/>
        <result column="discount_rule" jdbcType="TINYINT" property="discountRule"/>
        <result column="discount_type" jdbcType="TINYINT" property="discountType"/>
        <result column="suitable_prod_type" jdbcType="TINYINT" property="suitableProdType"/>
        <result column="max_reduce_amount" jdbcType="DECIMAL" property="maxReduceAmount"/>
        <result column="start_time" jdbcType="TIMESTAMP" property="startTime"/>
        <result column="end_time" jdbcType="TIMESTAMP" property="endTime"/>
        <result column="status" jdbcType="TINYINT" property="status"/>
    </resultMap>
    <resultMap id="DiscountAndItemAndProdMap" type="com.yami.shop.discount.common.model.Discount">
        <id column="discount_id" jdbcType="BIGINT" property="discountId"/>
        <result column="shop_id" jdbcType="BIGINT" property="shopId"/>
        <result column="discount_name" jdbcType="VARCHAR" property="discountName"/>
        <result column="mobile_pic" jdbcType="VARCHAR" property="mobilePic"/>
        <result column="pc_pic" jdbcType="VARCHAR" property="pcPic"/>
        <result column="pc_background_pic" jdbcType="VARCHAR" property="pcBackgroundPic"/>
        <result column="discount_rule" jdbcType="TINYINT" property="discountRule"/>
        <result column="discount_type" jdbcType="TINYINT" property="discountType"/>
        <result column="suitable_prod_type" jdbcType="TINYINT" property="suitableProdType"/>
        <result column="max_reduce_amount" jdbcType="DECIMAL" property="maxReduceAmount"/>
        <result column="start_time" jdbcType="TIMESTAMP" property="startTime"/>
        <result column="end_time" jdbcType="TIMESTAMP" property="endTime"/>
        <result column="status" jdbcType="TINYINT" property="status"/>
        <collection property="discountItems" ofType="com.yami.shop.discount.common.model.DiscountItem">
            <result column="discount_item_id" jdbcType="BIGINT" property="discountItemId"/>
            <result column="discount_id" jdbcType="BIGINT" property="discountId"/>
            <result column="need_amount" jdbcType="DECIMAL" property="needAmount"/>
            <result column="discount" jdbcType="DECIMAL" property="discount"/>
        </collection>
        <collection property="discountProds" javaType="list" ofType="com.yami.shop.discount.common.model.DiscountProd">
            <result column="discount_prod_id" jdbcType="BIGINT" property="discountProdId"/>
            <result column="prod_id" jdbcType="BIGINT" property="prodId"/>
            <result column="imgs" jdbcType="VARCHAR" property="imgs"/>
            <result column="pic" jdbcType="VARCHAR" property="pic"/>
            <result column="prod_name" jdbcType="VARCHAR" property="prodName"/>
            <result column="prod_status" jdbcType="TINYINT" property="prodStatus"/>
        </collection>
    </resultMap>
    <select id="getDiscountAndItemAndProdById" resultMap="DiscountAndItemAndProdMap">
    select d.*,di.*,dp.*,tp.imgs,tp.pic,tp.prod_name,tp.status as prodStatus from tz_discount d
    left join tz_discount_item di on d.discount_id = di.discount_id
    left join tz_discount_prod dp on d.discount_id = dp.discount_id
    left join tz_prod tp on dp.prod_id = tp.prod_id
    where d.discount_id = #{discountId}
    ORDER BY di.need_amount ASC
  </select>
    <delete id="deleteDiscounts">
        delete from tz_discount where discount_id = #{id} and shop_id = #{shopId}
    </delete>
    <select id="getDiscountsAndItemsByShopId" resultMap="DiscountAndItemAndProdMap">
        SELECT d.*,dp.*,di.* FROM tz_discount d
        LEFT JOIN tz_discount_item di ON d.discount_id = di.discount_id
        LEFT JOIN tz_discount_prod dp ON d.discount_id = dp.discount_id
        WHERE d.shop_id = #{shopId} and d.status = 1 and d.start_time &lt; NOW() and d.end_time &gt; NOW()
        ORDER BY d.start_time, di.need_amount DESC
  </select>
    <select id="listByProdId" resultType="com.yami.shop.bean.app.dto.DiscountDto">
        SELECT d.*
        FROM tz_discount d
        LEFT JOIN tz_discount_prod dp ON d.discount_id = dp.discount_id AND d.suitable_prod_type
        WHERE d.status = 1 AND d.shop_id = #{shopId}  AND d.start_time &lt; NOW() AND d.end_time &gt; NOW()
        AND (d.suitable_prod_type = 0 OR (dp.prod_id = #{prodId} AND d.suitable_prod_type = 1) OR (dp.prod_id != #{prodId} AND d.suitable_prod_type = 2))
        GROUP BY d.discount_id
        ORDER BY d.start_time
    </select>
    <select id="discountProdList" resultType="com.yami.shop.bean.app.dto.ProductDto">
        SELECT DISTINCT p.prod_id,
        p.pic,
        ifnull(pl.prod_name,p.prod_name) as prod_name,
        ifnull(pl.brief,p.brief) as brief,
        p.price,
        pe.sold_num,
        p.create_time
        FROM tz_discount d,tz_prod p
        LEFT JOIN tz_prod_extension pe on pe.prod_id = p.prod_id
        LEFT JOIN tz_prod_lang pl on pl.prod_id = p.prod_id and pl.lang =#{dbLang}
        WHERE
        p.`status` = 1 AND p.`prod_type` in (0,1)  AND d.`status` = 1 AND d.`start_time` &lt;= NOW() AND d.`end_time` &gt; NOW()
        AND d.discount_id = #{discount.discountId} AND p.`shop_id` = d.shop_id
        AND ((d.suitable_prod_type = 0 )
        OR
        (
        p.`prod_id`
        <if test="discount.suitableProdType == 2 ">
            not
        </if>
        IN(SELECT dp.prod_id FROM tz_discount_prod dp WHERE dp.discount_id = #{discount.discountId})
        ))
        ORDER BY pe.sold_num DESC, p.create_time DESC
    </select>
    <select id="getPlatformDiscountPage" resultType="com.yami.shop.discount.common.model.Discount">
        SELECT d.*,sd.`shop_name` FROM tz_discount d
        LEFT JOIN tz_shop_detail sd ON sd.`shop_id` = d.`shop_id`
        <where>
            <if test="discount.shopName != null">
                and trim(replace(sd.shop_name,' ','')) like trim(replace(concat('%',#{discount.shopName},'%'),' ',''))
            </if>
            <if test="discount.status != null">
                AND d.status = #{discount.status}
            </if>
            <if test="discount.discountName != null">
                and trim(replace(d.discount_name,' ','')) like trim(replace(concat('%',#{discount.discountName},'%'),' ',''))
            </if>
        </where>
        ORDER BY d.`start_time` DESC
    </select>
    <update id="updateStatusByDiscountId">
        UPDATE tz_discount d SET d.`status` = #{status} WHERE d.`discount_id` = #{discountId}
    </update>
    <select id="getDiscountList" resultType="com.yami.shop.bean.app.dto.DiscountDto">
        SELECT d.discount_id,d.discount_name,d.end_time,d.start_time,d.mobile_pic,d.pc_pic,d.pc_background_pic,s.shop_name,s.shop_logo
        FROM tz_discount d
        JOIN tz_shop_detail s ON s.shop_id = d.shop_id
        WHERE d.`status` = 1 AND d.start_time &lt; NOW() AND d.end_time &gt; NOW() AND s.shop_status = 1
        ORDER BY d.end_time, d.discount_id DESC
    </select>
    <select id="getDiscountByDiscountId" resultType="com.yami.shop.bean.app.dto.DiscountDto">
        SELECT d.discount_id,d.discount_name,d.max_reduce_amount,d.end_time,d.start_time,d.mobile_pic,d.pc_pic,d.pc_background_pic,s.shop_name,s.shop_logo
        FROM tz_discount d
        JOIN tz_shop_detail s ON s.shop_id = d.shop_id
        WHERE d.`status` = 1 AND d.discount_id = #{discountId} AND d.start_time &lt; NOW() AND d.end_time &gt; NOW()
    </select>
    <select id="countProdIsSeckillAll" resultType="java.lang.Integer" parameterType="java.lang.Long">
        SELECT COUNT(1) FROM `tz_discount` dt
        WHERE dt.suitable_prod_type = 0 AND dt.status = 1 AND dt.shop_id = #{shopId}
    </select>
    <select id="countProdIsSeckillSome" resultType="java.lang.Integer" parameterType="java.lang.Long">
      SELECT COUNT(1) FROM `tz_discount` dt
      LEFT JOIN `tz_discount_prod` dp ON dp.discount_id = dt.discount_id
      WHERE dt.suitable_prod_type = 1 AND dt.status = 1
      AND dt.shop_id = #{shopId}
      AND dp.prod_id = #{prodId}
    </select>
    <select id="countProdIsSeckillExcept" resultType="java.lang.Integer">
      SELECT COUNT(1) FROM `tz_discount` dt
      LEFT JOIN `tz_discount_prod` dp ON dp.discount_id = dt.discount_id
      WHERE dt.suitable_prod_type = 2 AND dt.status = 1
      AND dt.shop_id = #{shopId}
      AND dp.prod_id = #{prodId}
    </select>
    <select id="getProdIsSeckillExcept" resultType="com.yami.shop.discount.common.model.DiscountProd"
            parameterType="java.lang.Long">
      SELECT dp.* FROM `tz_discount` dt
      LEFT JOIN `tz_discount_prod` dp ON dp.discount_id = dt.discount_id
      WHERE dt.suitable_prod_type = 2 AND dt.status = 1
      AND dt.shop_id = #{shopId}
    </select>
    <update id="closeDiscountBySetStatus">
        UPDATE tz_discount SET `status` = 0
        WHERE `status` = 1 AND end_time &lt; NOW()
    </update>
    <select id="listShopIdsWhichWillClose" resultType="java.lang.Long">
        select shop_id from tz_discount where `status` = 1 AND end_time &lt; NOW()
    </select>
    <select id="pageProdByDiscount" resultType="com.yami.shop.bean.model.Product">
        select ifnull(pl.prod_name,p.prod_name) as prod_name,p.* from tz_prod p
        LEFT JOIN tz_prod_lang pl on pl.prod_id = p.prod_id and pl.lang = #{product.lang}
        WHERE p.`status` = 1 and  p.STATUS = 1 and p.prod_type != 5
        <if test="discount.shopId != 0">
            AND p.shop_id = #{discount.shopId}
        </if>
        <if test="product.prodName != null">
            AND p.prod_name LIKE concat('%',#{product.prodName},'%')
        </if>
        <if test="product.shopCategoryId != null">
            AND p.shop_category_id = #{product.shopCategoryId}
        </if>
        AND p.`prod_id`
        <if test="discount.suitableProdType == 1">
            IN
        </if>
        <if test="discount.suitableProdType == 2">
            NOT IN
        </if>
        (SELECT
        dp.`prod_id`
        FROM
        tz_discount_prod dp
        WHERE
        dp.`discount_id` = #{discount.discountId})
        ORDER BY p.`update_time` DESC
    </select>
    <select id="listIdByShopIdAndStatus" resultType="java.lang.Long">
        SELECT discount_id
        FROM tz_discount
        WHERE shop_id = #{shopId} AND status = #{status}
    </select>
    <select id="countNormalDiscount" resultType="int">
        SELECT IFNULL(COUNT(`discount_id`),0)
        FROM `tz_discount`
        WHERE `status` = 1
          AND `discount_id` IN
        <foreach collection="discountIds" item="discountId" separator="," open="(" close=")">
            #{discountId}
        </foreach>
    </select>
    <select id="listShopIdsWhichStart" resultType="java.lang.Long">
        SELECT shop_id FROM tz_discount
        WHERE start_time &lt; ADDDATE(NOW(), INTERVAL 60 SECOND)
        AND start_time &gt; SUBDATE(NOW(), INTERVAL 60 SECOND) and status = 1
    </select>
</mapper>
yami-shop-discount/yami-shop-discount-common/src/main/resources/mapper/DiscountProdMapper.xml
New file
@@ -0,0 +1,27 @@
<?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="com.yami.shop.discount.common.dao.DiscountProdMapper">
  <resultMap id="BaseResultMap" type="com.yami.shop.discount.common.model.DiscountProd">
    <!--
      WARNING - @mbg.generated
    -->
    <id column="discount_prod_id" jdbcType="BIGINT" property="discountProdId" />
    <result column="discount_id" jdbcType="BIGINT" property="discountId" />
    <result column="prod_id" jdbcType="BIGINT" property="prodId" />
    <result column="pic" jdbcType="VARCHAR" property="pic" />
    <result column="prod_name" jdbcType="VARCHAR" property="prodName" />
  </resultMap>
  <insert id="insertDiscountProds">
    insert into tz_discount_prod (discount_id,prod_id) values
    <foreach collection="discountProds" item="discountProd" separator=",">
    (#{discountProd.discountId},#{discountProd.prodId})
    </foreach>
  </insert>
  <select id="getDiscountProdByDiscountId" resultType="com.yami.shop.discount.common.model.DiscountProd">
    select p.pic,p.prod_name,dp.* from tz_discount_prod dp
    left join tz_prod p on p.prod_id = dp.prod_id
     where dp.discount_id = #{discountId}
  </select>
</mapper>
yami-shop-discount/yami-shop-discount-multishop/pom.xml
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.yami.shop</groupId>
        <artifactId>yami-shop-discount</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>yami-shop-discount-multishop</artifactId>
    <modelVersion>4.0.0</modelVersion>
    <description>商城满减模块店铺管理部分</description>
    <dependencies>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-discount-common</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-security-multishop</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
    </dependencies>
</project>
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/config/SwaggerConfiguration.java
New file
@@ -0,0 +1,35 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.multishop.config;
import lombok.AllArgsConstructor;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author yami
 */
@Configuration("dicountSwaggerConfiguration")
@AllArgsConstructor
public class SwaggerConfiguration {
    @Bean
    public GroupedOpenApi discountRestApi() {
        return GroupedOpenApi.builder()
                .group("满减活动接口")
                .packagesToScan("com.yami.shop.discount.multishop.controller")
                .pathsToMatch("/**")
                .build();
    }
}
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/controller/DiscountController.java
New file
@@ -0,0 +1,225 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.multishop.controller;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.enums.ShopStatus;
import com.yami.shop.bean.model.OfflineHandleEvent;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.model.ShopDetail;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.constants.DiscountStatusEnum;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.security.multishop.util.SecurityUtils;
import com.yami.shop.service.OfflineHandleEventService;
import com.yami.shop.service.ProductService;
import com.yami.shop.service.ShopDetailService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
import java.util.Objects;
/**
 * @author lgh on 2018/11/21.
 */
@RestController
@RequestMapping("/admin/discount")
@Tag(name = "商家端促销活动接口")
public class DiscountController {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private ProductService productService;
    @Autowired
    private OfflineHandleEventService offlineHandleEventService;
    @Autowired
    private ShopDetailService shopDetailService;
    @GetMapping("/page")
    @Operation(summary = "分页获取促销活动列表信息")
    public ServerResponseEntity<IPage<Discount>> page(@ParameterObject Discount discount, PageParam<Discount> page) {
        IPage<Discount> discounts = discountService.page(page,
                new LambdaQueryWrapper<Discount>()
                        .eq(Objects.nonNull(discount.getStatus()), Discount::getStatus, discount.getStatus())
                        .like(StringUtils.isNotBlank(discount.getDiscountName()), Discount::getDiscountName, discount.getDiscountName())
                        .eq(Discount::getShopId, SecurityUtils.getShopUser().getShopId())
//                        .orderByDesc(Discount::getStatus)
                        .orderByDesc(Discount::getStartTime)
        );
        return ServerResponseEntity.success(discounts);
    }
    /**
     * 获取信息
     */
    @GetMapping("/info/{discountId}")
    @Operation(summary = "根据活动id获取促销活动信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<Discount> info(@PathVariable("discountId") Long discountId) {
        Long shopId = SecurityUtils.getShopUser().getShopId();
        Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
        if (discount == null) {
            // 未找到此活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        if (!Objects.equals(shopId, discount.getShopId())) {
            // 您无权获取此活动信息
            throw new YamiShopBindException("yami.activity.no.auth");
        }
        return ServerResponseEntity.success(discount);
    }
    @PostMapping
    @PreAuthorize("@pms.hasPermission('admin:discount:save')")
    @Operation(summary = "新增促销活动信息")
    public ServerResponseEntity<Long> save(@RequestBody @Valid Discount discount) {
        Long shopId = SecurityUtils.getShopUser().getShopId();
        ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId);
        if (Objects.isNull(shopDetail)) {
            throw new YamiShopBindException("找不到当前店铺");
        }
        if (!Objects.equals(shopDetail.getShopStatus(), ShopStatus.STOP.value()) && !Objects.equals(shopDetail.getShopStatus(), ShopStatus.OPEN.value())) {
            throw new YamiShopBindException("店铺处于违规下架状态,不能新增满减活动");
        }
        discount.setShopId(shopId);
        List<DiscountProd> discountProds = discount.getDiscountProds();
        StringBuilder missProdNames = new StringBuilder();
        if (CollectionUtil.isNotEmpty(discountProds)) {
            for (DiscountProd discountProd : discountProds) {
                Product product = productService.getProductByProdId(discountProd.getProdId(), I18nMessage.getDbLang());
                if (product == null) {
                    missProdNames.append(discountProd.getProdName()).append(" ");
                }
            }
        }
        if (missProdNames.length() > 0) {
            String message = I18nMessage.getMessage("yami.activity.prod.status");
            return ServerResponseEntity.showFailMsg(message + "[ " + missProdNames.toString() + "]");
        }
        //输入的数字位数不能大于10的13次方
        if ((discount.getMaxReduceAmount() >= Math.pow(10, 16))) {
            System.out.println(discount.getMaxReduceAmount());
            throw new YamiShopBindException("yami.discount.MaxReduceAmount.limit");
        }
        discountService.insertDiscountAndItemAndProd(discount);
        return ServerResponseEntity.success(discount.getDiscountId());
    }
    @PutMapping
    @PreAuthorize("@pms.hasPermission('admin:discount:update')")
    @Operation(summary = "修改促销活动信息")
    public ServerResponseEntity<Void> update(@RequestBody @Valid Discount discount) {
        Long shopId = SecurityUtils.getShopUser().getShopId();
        ShopDetail shopDetail = shopDetailService.getShopDetailByShopId(shopId);
        if (Objects.isNull(shopDetail)) {
            throw new YamiShopBindException("找不到当前店铺");
        }
        if (!Objects.equals(shopDetail.getShopStatus(), ShopStatus.STOP.value()) && !Objects.equals(shopDetail.getShopStatus(), ShopStatus.OPEN.value())) {
            if (Objects.equals(discount.getStatus(), DiscountStatusEnum.RUN.getValue())) {
                throw new YamiShopBindException("店铺处于违规下架状态,不能修改活动状态为开启状态");
            }
        }
        discount.setShopId(shopId);
        //输入的数字位数不能大于10的13次方
        if ((discount.getMaxReduceAmount() >= Math.pow(10, 13))) {
            System.out.println(discount.getMaxReduceAmount());
            throw new YamiShopBindException("yami.discount.MaxReduceAmount.limit");
        }
        discountService.updateDiscountAndItemAndProd(discount);
        return ServerResponseEntity.success();
    }
    @DeleteMapping("/{discountId}")
    @Operation(summary = "根据活动id删除促销活动信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    @PreAuthorize("@pms.hasPermission('admin:discount:delete')")
    public ServerResponseEntity<Void> delete(@PathVariable Long discountId) {
        Long shopId = SecurityUtils.getShopUser().getShopId();
        Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
        if (discount == null) {
            // 未找到该活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        discountService.deleteDiscountsAndItemsAndProds(discountId, shopId);
        return ServerResponseEntity.success();
    }
    @GetMapping("/getOfflineHandleEventByDiscountId/{discountId}")
    @Operation(summary = "根据活动id获取下线信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<OfflineHandleEvent> getOfflineHandleEventByDiscountId(@PathVariable("discountId") Long discountId) {
        OfflineHandleEvent offlineHandleEvent = offlineHandleEventService.getProcessingEventByHandleTypeAndHandleId(OfflineHandleEventType.DISCOUNT.getValue(), discountId);
        return ServerResponseEntity.success(offlineHandleEvent);
    }
    @PostMapping("/auditApply")
    @PreAuthorize("@pms.hasPermission('admin:discount:auditApply')")
    @Operation(summary = "违规商品提交审核")
    public ServerResponseEntity<Void> auditApply(@RequestBody OfflineHandleEventAuditParam offlineHandleEventAuditParam) {
        Discount discount = discountService.getDiscountAndItemAndProdById(offlineHandleEventAuditParam.getHandleId());
        if (discount == null) {
            // 未找到此活动信息
            throw new YamiShopBindException("yami.activity.cannot.find.check");
        }
        // 提交审核
        discountService.auditApply(offlineHandleEventAuditParam.getEventId(), offlineHandleEventAuditParam.getHandleId(), offlineHandleEventAuditParam.getReapplyReason());
        // 移除缓存
        discountService.removeDiscountsAndItemsCacheByShopId(discount.getShopId());
        discountService.removeDiscountAndItemAndProdCacheById(discount.getDiscountId());
        return ServerResponseEntity.success();
    }
    @PostMapping("/prodIsDiscount")
    @Operation(summary = "查询商品是否在参与满减满折扣活动,并且返回提示")
    @Parameter(name = "prodIds", description = "商品id" , required = true)
    public ServerResponseEntity<String> prodIsDiscount(@RequestBody List<Long> prodIds) {
        // 查询是满减满折是所有商品都参与的
        Long shopId = SecurityUtils.getShopUser().getShopId();
        Integer count = 0;
        String msg = "选择的商品也在限制特惠【满减满折】参与活动,请慎重选择";
        List<Long> seckillProdIds = discountService.getProdIsSeckillExcept(shopId);
        for (Long prodId : prodIds) {
            count = discountService.countProdIsSeckillAll(shopId, prodId);
            if (count > 0) {
                return ServerResponseEntity.success(msg);
            }
            count = discountService.countProdIsSeckillSome(shopId, prodId);
            if (count > 0) {
                return ServerResponseEntity.success(msg);
            }
            if (CollectionUtils.isNotEmpty(seckillProdIds)) {
                if (!seckillProdIds.contains(prodId)) {
                    return ServerResponseEntity.success(msg);
                }
            }
        }
        return ServerResponseEntity.success("");
    }
}
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/controller/DiscountProdController.java
New file
@@ -0,0 +1,74 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.multishop.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.ProdStatusEnums;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.security.multishop.util.SecurityUtils;
import com.yami.shop.service.ProductService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
/**
 * @author lgh on 2018/11/21.
 */
@RestController
@RequestMapping("/admin/discountProd")
@Tag(name = "商家端促销活动商品接口")
public class DiscountProdController {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private ProductService productService;
    @GetMapping("/info/{discountId}")
    @Operation(summary = "根据活动id获取促销活动商品信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<IPage<Product>> info(@PathVariable("discountId") Long discountId, @ParameterObject ProductParam product, PageParam<Product> page) {
        Discount discount = discountService.getById(discountId);
        if (Objects.isNull(discount)) {
            // 该活动不存在或者已结束
            throw new YamiShopBindException("yami.activity.cannot.find.check");
        }
        if (!Objects.equals(SecurityUtils.getShopUser().getShopId(), discount.getShopId())) {
            throw new YamiShopBindException("yami.no.auth");
        }
        product.setLang(I18nMessage.getDbLang());
        product.setShopId(SecurityUtils.getShopUser().getShopId());
        if (Objects.equals(discount.getSuitableProdType(), 0)) {
            product.setStatus(ProdStatusEnums.NORMAL.getValue());
            IPage<Product> products = productService.pageByLang(page, product);
            return ServerResponseEntity.success(products);
        } else {
            IPage<Product> products = discountService.pageProdByDiscount(page, product, discount);
            return ServerResponseEntity.success(products);
        }
    }
}
yami-shop-discount/yami-shop-discount-multishop/src/main/java/com/yami/shop/discount/multishop/listener/UpdateDiscountProdListener.java
New file
@@ -0,0 +1,47 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.multishop.listener;
import com.yami.shop.bean.event.RemoveDiscountProdByIdsEvent;
import com.yami.shop.discount.common.service.DiscountService;
import lombok.AllArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
/**
 * 修改满减活动的可用商品列表
 *
 * @author lhd
 */
@Component("updateDiscountListener")
@AllArgsConstructor
public class UpdateDiscountProdListener {
    private final DiscountService discountService;
    /**
     * 修改满减活动的可用商品列表
     */
    @EventListener(RemoveDiscountProdByIdsEvent.class)
    @Transactional(rollbackFor = Exception.class)
    public void updateDiscountListener(RemoveDiscountProdByIdsEvent event) {
        List<Long> ids = discountService.updateDiscountProdByIds(event.getProdIds());
        if(CollectionUtils.isEmpty(ids)){
            return;
        }
        for (Long id : ids) {
            // 移除缓存
            discountService.removeDiscountAndItemAndProdCacheById(id);
        }
    }
}
yami-shop-discount/yami-shop-discount-platform/pom.xml
New file
@@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <groupId>com.yami.shop</groupId>
        <artifactId>yami-shop-discount</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <artifactId>yami-shop-discount-platform</artifactId>
    <modelVersion>4.0.0</modelVersion>
    <description>商城满减模块平台管理部分</description>
    <dependencies>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-discount-common</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
        <dependency>
            <groupId>com.yami.shop</groupId>
            <artifactId>yami-shop-security-platform</artifactId>
            <version>${yami.shop.version}</version>
        </dependency>
    </dependencies>
</project>
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/config/SwaggerConfiguration.java
New file
@@ -0,0 +1,34 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.platform.config;
import lombok.AllArgsConstructor;
import org.springdoc.core.GroupedOpenApi;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
 * @author yami
 */
@Configuration("dicountSwaggerConfiguration")
@AllArgsConstructor
public class SwaggerConfiguration {
    @Bean
    public GroupedOpenApi discountRestApi() {
        return GroupedOpenApi.builder()
                .group("满减活动接口")
                .packagesToScan("com.yami.shop.discount.platform.controller")
                .pathsToMatch("/**")
                .build();
    }
}
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/controller/DiscountController.java
New file
@@ -0,0 +1,150 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.platform.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.OfflineHandleEventType;
import com.yami.shop.bean.enums.SendType;
import com.yami.shop.bean.event.SendMessageEvent;
import com.yami.shop.bean.model.OfflineHandleEvent;
import com.yami.shop.bean.param.NotifyTemplateParam;
import com.yami.shop.bean.param.OfflineHandleEventAuditParam;
import com.yami.shop.common.enums.StatusEnum;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.model.DiscountProd;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.security.platform.util.SecurityUtils;
import com.yami.shop.service.OfflineHandleEventService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.*;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
 * @author yami
 */
@RestController
@RequestMapping("/platform/discount")
@Tag(name = "平台端端促销活动接口")
public class DiscountController {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private OfflineHandleEventService offlineHandleEventService;
    @Autowired
    private  ApplicationEventPublisher eventPublisher;
    @GetMapping("/page")
    @PreAuthorize("@pms.hasPermission('platform:discount:page')")
    @Operation(summary = "分页获取促销活动列表信息")
    public ServerResponseEntity<IPage<Discount>> page(@ParameterObject Discount discount, PageParam<Discount> page) {
        IPage<Discount> discountPage = discountService.getPlatformDiscountPage(page, discount);
        return ServerResponseEntity.success(discountPage);
    }
    @GetMapping("/info/{discountId}")
    @Operation(summary = "根据活动id获取促销活动信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    @PreAuthorize("@pms.hasPermission('platform:discount:info')")
    public ServerResponseEntity<Discount> info(@PathVariable("discountId") Long discountId) {
        Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
        if (discount == null) {
            // 未找到此活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        // 在折扣商品列表中去掉已经删除的商品
        List<DiscountProd> discountProdList = discount.getDiscountProds().stream().filter(discountProd -> !Objects.equals(discountProd.getProdStatus(), StatusEnum.DELETE.value())).collect(Collectors.toList());
        discount.setDiscountProds(discountProdList);
        return ServerResponseEntity.success(discount);
    }
    @PostMapping("/offline")
    @PreAuthorize("@pms.hasPermission('platform:discount:update')")
    @Operation(summary = "下线活动")
    public ServerResponseEntity<Void> offline(@RequestBody OfflineHandleEvent offlineHandleEvent) {
        Long sysUserId = SecurityUtils.getSysUser().getUserId();
        Discount discount = discountService.getById(offlineHandleEvent.getHandleId());
        if (discount == null) {
            // 未找到此活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        discountService.offline(discount, offlineHandleEvent.getOfflineReason(), sysUserId);
        //发送活动下架提醒给商家
        NotifyTemplateParam shopParam = new NotifyTemplateParam();
        shopParam.setShopId(discount.getShopId());
        shopParam.setActivityId(discount.getDiscountId());
        shopParam.setActivityName(discount.getDiscountName());
        shopParam.setSendType(SendType.ACTIVITY_OFFLINE.getValue());
        eventPublisher.publishEvent(new SendMessageEvent(shopParam));
        // 移除缓存
        discountService.removeDiscountAndItemAndProdCacheById(discount.getDiscountId());
        discountService.removeDiscountsAndItemsCacheByShopId(discount.getShopId());
        return ServerResponseEntity.success();
    }
    @DeleteMapping("/{discountId}")
    @Operation(summary = "根据活动id删除促销活动信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    @PreAuthorize("@pms.hasPermission('platform:discount:delete')")
    public ServerResponseEntity<Void> delete(@PathVariable Long discountId) {
        Discount discount = discountService.getDiscountAndItemAndProdById(discountId);
        if (discount == null) {
            // 未找到该活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        discountService.deleteDiscountsAndItemsAndProds(discountId, discount.getShopId());
        return ServerResponseEntity.success();
    }
    @GetMapping("/getOfflineHandleEventByDiscountId/{discountId}")
    @Operation(summary = "根据活动id获取下线信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    @PreAuthorize("@pms.hasPermission('platform:discount:info')")
    public ServerResponseEntity<OfflineHandleEvent> getOfflineHandleEventByDiscountId(@PathVariable("discountId") Long discountId) {
        OfflineHandleEvent offlineHandleEvent = offlineHandleEventService.getProcessingEventByHandleTypeAndHandleId(OfflineHandleEventType.DISCOUNT.getValue(), discountId);
        return ServerResponseEntity.success(offlineHandleEvent);
    }
    @PostMapping("/auditDiscount")
    @PreAuthorize("@pms.hasPermission('platform:discount:update')")
    @Operation(summary = "审核活动")
    public ServerResponseEntity<Void> auditDiscount(@RequestBody OfflineHandleEventAuditParam offlineHandleEventAuditParam) {
        Long sysUserId = SecurityUtils.getSysUser().getUserId();
        Discount discount = discountService.getDiscountAndItemAndProdById(offlineHandleEventAuditParam.getHandleId());
        if (discount == null) {
            // 未找到该活动信息
            throw new YamiShopBindException("yami.activity.cannot.find");
        }
        discountService.auditDiscount(offlineHandleEventAuditParam, sysUserId);
        //发送活动审核提醒给商家
        NotifyTemplateParam shopParam = new NotifyTemplateParam();
        shopParam.setShopId(discount.getShopId());
        shopParam.setActivityId(discount.getDiscountId());
        shopParam.setActivityName(discount.getDiscountName());
        shopParam.setSendType(SendType.ACTIVITY_AUDIT.getValue());
        eventPublisher.publishEvent(new SendMessageEvent(shopParam));
        // 移除缓存
        discountService.removeDiscountAndItemAndProdCacheById(discount.getDiscountId());
        discountService.removeDiscountsAndItemsCacheByShopId(discount.getShopId());
        return ServerResponseEntity.success();
    }
}
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/controller/DiscountProdController.java
New file
@@ -0,0 +1,70 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.platform.controller;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.yami.shop.bean.enums.ProdStatusEnums;
import com.yami.shop.bean.model.Product;
import com.yami.shop.bean.param.ProductParam;
import com.yami.shop.common.exception.YamiShopBindException;
import com.yami.shop.common.i18n.I18nMessage;
import com.yami.shop.common.response.ServerResponseEntity;
import com.yami.shop.common.util.PageParam;
import com.yami.shop.discount.common.model.Discount;
import com.yami.shop.discount.common.service.DiscountService;
import com.yami.shop.service.ProductService;
import io.swagger.v3.oas.annotations.tags.Tag;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Operation;
import org.springdoc.api.annotations.ParameterObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Objects;
/**
 * @author lgh on 2018/11/21.
 */
@RestController
@RequestMapping("/platform/discountProd")
@Tag(name = "平台端促销活动商品接口")
public class DiscountProdController {
    @Autowired
    private DiscountService discountService;
    @Autowired
    private ProductService productService;
    @GetMapping("/info/{discountId}")
    @Operation(summary = "根据活动id获取促销活动商品信息")
    @Parameter(name = "discountId", description = "活动ID" , required = true)
    public ServerResponseEntity<IPage<Product>> info(@PathVariable("discountId") Long discountId, @ParameterObject ProductParam product, PageParam<Product> page) {
        Discount discount = discountService.getById(discountId);
        if (Objects.isNull(discount)) {
            // 该活动不存在或者已结束
            throw new YamiShopBindException("yami.activity.cannot.find.check");
        }
        product.setLang(I18nMessage.getDbLang());
        product.setShopId(discount.getShopId());
        if (Objects.equals(discount.getSuitableProdType(), 0)) {
            product.setStatus(ProdStatusEnums.NORMAL.getValue());
            IPage<Product> products = productService.pageByLang(page, product);
            return ServerResponseEntity.success(products);
        } else {
            IPage<Product> products = discountService.pageProdByDiscount(page, product, discount);
            return ServerResponseEntity.success(products);
        }
    }
}
yami-shop-discount/yami-shop-discount-platform/src/main/java/com/yami/shop/discount/platform/task/DiscountTask.java
New file
@@ -0,0 +1,59 @@
/*
 * Copyright (c) 2018-2999 广州市蓝海创新科技有限公司 All rights reserved.
 *
 * https://www.mall4j.com/
 *
 * 未经允许,不可做商业用途!
 *
 * 版权所有,侵权必究!
 */
package com.yami.shop.discount.platform.task;
import cn.hutool.core.collection.CollUtil;
import com.xxl.job.core.handler.annotation.XxlJob;
import com.yami.shop.discount.common.dao.DiscountMapper;
import com.yami.shop.discount.common.service.DiscountService;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import java.util.List;
/**
 * @author yami
 */
@Slf4j
@Component
@AllArgsConstructor
public class DiscountTask {
    private final DiscountService discountService;
    private final DiscountMapper discountMapper;
    /**
     * 关闭已经结束的满减折活动
     */
    @XxlJob("closeDiscount")
    public void closeDiscount() {
        List<Long> shopIds = discountService.listShopIdsWhichWillClose();
        discountService.closeDiscountBySetStatus();
        if (CollUtil.isNotEmpty(shopIds)) {
            shopIds.forEach(discountService::removeDiscountsAndItemsCacheByShopId);
        }
    }
    /**
     * 满减活动开启,删除商家活动缓存
     */
    @XxlJob("startDiscount")
    public void startDiscount() {
        // 如果有活动在这1分钟内开始才去清除缓存
        List<Long> shopIds = discountMapper.listShopIdsWhichStart();
        if (CollUtil.isNotEmpty(shopIds)) {
            log.info("满减活动开始,清除缓存");
            // 清除缓存
            shopIds.forEach(discountService::removeDiscountsAndItemsCacheByShopId);
        }
    }
}