Procházet zdrojové kódy

promotion:完善限时折扣的修改逻辑

YunaiV před 2 roky
rodič
revize
941782fb10

+ 10 - 6
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/DiscountActivityController.java

@@ -2,12 +2,10 @@ package cn.iocoder.yudao.module.promotion.controller.admin.discount;
 
 import cn.iocoder.yudao.framework.common.pojo.CommonResult;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityPageReqVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityRespVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
 import cn.iocoder.yudao.module.promotion.convert.discount.DiscountActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
+import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
 import cn.iocoder.yudao.module.promotion.service.discount.DiscountActivityService;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiImplicitParam;
@@ -18,6 +16,7 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.annotation.Resource;
 import javax.validation.Valid;
+import java.util.List;
 
 import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
 
@@ -67,9 +66,14 @@ public class DiscountActivityController {
     @ApiOperation("获得限时折扣活动")
     @ApiImplicitParam(name = "id", value = "编号", required = true, example = "1024", dataTypeClass = Long.class)
     @PreAuthorize("@ss.hasPermission('promotion:discount-activity:query')")
-    public CommonResult<DiscountActivityRespVO> getDiscountActivity(@RequestParam("id") Long id) {
+    public CommonResult<DiscountActivityDetailRespVO> getDiscountActivity(@RequestParam("id") Long id) {
         DiscountActivityDO discountActivity = discountActivityService.getDiscountActivity(id);
-        return success(DiscountActivityConvert.INSTANCE.convert(discountActivity));
+        if (discountActivity == null) {
+            return success(null);
+        }
+        // 拼接结果
+        List<DiscountProductDO> discountProducts = discountActivityService.getDiscountProductsByActivityId(id);
+        return success(DiscountActivityConvert.INSTANCE.convert(discountActivity, discountProducts));
     }
 
     @GetMapping("/page")

+ 21 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/discount/vo/DiscountActivityDetailRespVO.java

@@ -0,0 +1,21 @@
+package cn.iocoder.yudao.module.promotion.controller.admin.discount.vo;
+
+import io.swagger.annotations.ApiModel;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.ToString;
+
+import java.util.List;
+
+@ApiModel("管理后台 - 限时折扣活动的详细 Response VO")
+@Data
+@EqualsAndHashCode(callSuper = true)
+@ToString(callSuper = true)
+public class DiscountActivityDetailRespVO extends DiscountActivityRespVO {
+
+    /**
+     * 商品列表
+     */
+    private List<Product> products;
+
+}

+ 53 - 4
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/convert/discount/DiscountActivityConvert.java

@@ -1,14 +1,13 @@
 package cn.iocoder.yudao.module.promotion.convert.discount;
 
+import cn.hutool.core.util.ObjectUtil;
 import cn.iocoder.yudao.framework.common.pojo.PageResult;
 import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
 import cn.iocoder.yudao.framework.common.util.collection.MapUtils;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityBaseVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityCreateReqVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityRespVO;
-import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.DiscountActivityUpdateReqVO;
+import cn.iocoder.yudao.module.promotion.controller.admin.discount.vo.*;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProductDO;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
 import cn.iocoder.yudao.module.promotion.service.discount.bo.DiscountProductDetailBO;
 import org.mapstruct.Mapper;
 import org.mapstruct.factory.Mappers;
@@ -50,4 +49,54 @@ public interface DiscountActivityConvert {
 
     DiscountProductDO convert(DiscountActivityBaseVO.Product bean);
 
+    DiscountActivityDetailRespVO convert(DiscountActivityDO activity, List<DiscountProductDO> products);
+
+    // =========== 比较是否相等 ==========
+    /**
+     * 比较两个限时折扣商品是否相等
+     *
+     * @param productDO 数据库中的商品
+     * @param productVO 前端传入的商品
+     * @return 是否匹配
+     */
+    @SuppressWarnings("DuplicatedCode")
+    default boolean isEquals(DiscountProductDO productDO, DiscountActivityBaseVO.Product productVO) {
+        if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
+                || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
+                || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
+            return false;
+        }
+        if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
+            return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
+        }
+        if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
+            return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
+        }
+        return true;
+    }
+
+    /**
+     * 比较两个限时折扣商品是否相等
+     * 注意,比较时忽略 id 编号
+     *
+     * @param productDO 商品 1
+     * @param productVO 商品 2
+     * @return 是否匹配
+     */
+    @SuppressWarnings("DuplicatedCode")
+    default boolean isEquals(DiscountProductDO productDO, DiscountProductDO productVO) {
+        if (ObjectUtil.notEqual(productDO.getSpuId(), productVO.getSpuId())
+                || ObjectUtil.notEqual(productDO.getSkuId(), productVO.getSkuId())
+                || ObjectUtil.notEqual(productDO.getDiscountType(), productVO.getDiscountType())) {
+            return false;
+        }
+        if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PRICE.getType())) {
+            return ObjectUtil.equal(productDO.getDiscountPrice(), productVO.getDiscountPrice());
+        }
+        if (productDO.getDiscountType().equals(PromotionDiscountTypeEnum.PERCENT.getType())) {
+            return ObjectUtil.equal(productDO.getDiscountPercent(), productVO.getDiscountPercent());
+        }
+        return true;
+    }
+
 }

+ 9 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityService.java

@@ -9,6 +9,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProduct
 
 import javax.validation.Valid;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -73,4 +74,12 @@ public interface DiscountActivityService {
      */
     PageResult<DiscountActivityDO> getDiscountActivityPage(DiscountActivityPageReqVO pageReqVO);
 
+    /**
+     * 获得活动编号,对应对应的商品列表
+     *
+     * @param activityId 活动编号
+     * @return 活动的商品列表
+     */
+    List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId);
+
 }

+ 7 - 4
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImpl.java

@@ -91,8 +91,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
         // 计算要删除的记录
         List<Long> deleteIds = convertList(dbDiscountProducts, DiscountProductDO::getId,
                 discountProductDO -> updateReqVO.getProducts().stream()
-                        .noneMatch(product -> product.getSkuId().equals(discountProductDO.getSkuId())
-                            && product.getDiscountPrice().equals(discountProductDO.getDiscountPrice())));
+                        .noneMatch(product -> DiscountActivityConvert.INSTANCE.isEquals(discountProductDO, product)));
         if (CollUtil.isNotEmpty(deleteIds)) {
             discountProductMapper.deleteBatchIds(deleteIds);
         }
@@ -100,8 +99,7 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
         List<DiscountProductDO> newDiscountProducts = convertList(updateReqVO.getProducts(),
                 product -> DiscountActivityConvert.INSTANCE.convert(product).setActivityId(updateReqVO.getId()));
         newDiscountProducts.removeIf(product -> dbDiscountProducts.stream().anyMatch(
-                dbProduct -> dbProduct.getSkuId().equals(product.getSkuId())
-                        && dbProduct.getDiscountPrice().equals(product.getDiscountPrice()))); // 如果匹配到,说明是更新的
+                dbProduct -> DiscountActivityConvert.INSTANCE.isEquals(dbProduct, product))); // 如果匹配到,说明是更新的
         if (CollectionUtil.isNotEmpty(newDiscountProducts)) {
             discountProductMapper.insertBatch(newDiscountProducts);
         }
@@ -194,4 +192,9 @@ public class DiscountActivityServiceImpl implements DiscountActivityService {
         return discountActivityMapper.selectPage(pageReqVO);
     }
 
+    @Override
+    public List<DiscountProductDO> getDiscountProductsByActivityId(Long activityId) {
+        return discountProductMapper.selectListByActivityId(activityId);
+    }
+
 }

+ 15 - 6
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/discount/DiscountActivityServiceImplTest.java

@@ -11,6 +11,7 @@ import cn.iocoder.yudao.module.promotion.dal.dataobject.discount.DiscountProduct
 import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountActivityMapper;
 import cn.iocoder.yudao.module.promotion.dal.mysql.discount.DiscountProductMapper;
 import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionDiscountTypeEnum;
 import org.junit.jupiter.api.Test;
 import org.springframework.context.annotation.Import;
 
@@ -53,8 +54,10 @@ public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
             // 用于触发进行中的状态
             o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
             // 设置商品
-            o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L).setDiscountPrice(3),
-                    new DiscountActivityBaseVO.Product().setSpuId(10L).setSkuId(20L).setDiscountPrice(30)));
+            o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L)
+                            .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3),
+                    new DiscountActivityBaseVO.Product().setSpuId(10L).setSkuId(20L)
+                            .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30)));
         });
 
         // 调用
@@ -74,7 +77,9 @@ public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
             assertEquals(discountProduct.getActivityId(), discountActivity.getId());
             assertEquals(discountProduct.getSpuId(), product.getSpuId());
             assertEquals(discountProduct.getSkuId(), product.getSkuId());
+            assertEquals(discountProduct.getDiscountType(), product.getDiscountType());
             assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice());
+            assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent());
         }
     }
 
@@ -85,9 +90,9 @@ public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
         discountActivityMapper.insert(dbDiscountActivity);// @Sql: 先插入出一条存在的数据
         // mock 数据(活动)
         DiscountProductDO dbDiscountProduct01 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId())
-                .setSpuId(1L).setSkuId(2L));
+                .setSpuId(1L).setSkuId(2L).setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null));
         DiscountProductDO dbDiscountProduct02 = randomPojo(DiscountProductDO.class, o -> o.setActivityId(dbDiscountActivity.getId())
-                .setSpuId(10L).setSkuId(20L));
+                .setSpuId(10L).setSkuId(20L).setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null));
         discountProductMapper.insert(dbDiscountProduct01);
         discountProductMapper.insert(dbDiscountProduct02);
         // 准备参数
@@ -96,8 +101,10 @@ public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
             // 用于触发进行中的状态
             o.setStartTime(addTime(Duration.ofDays(1))).setEndTime(addTime(Duration.ofDays(2)));
             // 设置商品
-            o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L).setDiscountPrice(3),
-                    new DiscountActivityBaseVO.Product().setSpuId(100L).setSkuId(200L).setDiscountPrice(30)));
+            o.setProducts(asList(new DiscountActivityBaseVO.Product().setSpuId(1L).setSkuId(2L)
+                            .setDiscountType(PromotionDiscountTypeEnum.PRICE.getType()).setDiscountPrice(3).setDiscountPercent(null),
+                    new DiscountActivityBaseVO.Product().setSpuId(100L).setSkuId(200L)
+                            .setDiscountType(PromotionDiscountTypeEnum.PERCENT.getType()).setDiscountPercent(30).setDiscountPrice(null)));
         });
 
         // 调用
@@ -115,7 +122,9 @@ public class DiscountActivityServiceImplTest extends BaseDbUnitTest {
             assertEquals(discountProduct.getActivityId(), discountActivity.getId());
             assertEquals(discountProduct.getSpuId(), product.getSpuId());
             assertEquals(discountProduct.getSkuId(), product.getSkuId());
+            assertEquals(discountProduct.getDiscountType(), product.getDiscountType());
             assertEquals(discountProduct.getDiscountPrice(), product.getDiscountPrice());
+            assertEquals(discountProduct.getDiscountPercent(), product.getDiscountPercent());
         }
     }
 

+ 3 - 1
yudao-module-mall/yudao-module-promotion-biz/src/test/resources/sql/create_tables.sql

@@ -112,7 +112,9 @@ CREATE TABLE IF NOT EXISTS "promotion_discount_product" (
      "activity_id" bigint NOT NULL,
      "spu_id" bigint NOT NULL,
      "sku_id" bigint NOT NULL,
-     "discount_price" int NOT NULL,
+     "discount_type" int NOT NULL,
+     "discount_percent" int,
+     "discount_price" int,
      "creator" varchar DEFAULT '',
      "create_time" datetime NOT NULL DEFAULT CURRENT_TIMESTAMP,
      "updater" varchar DEFAULT '',

+ 47 - 30
yudao-ui-admin/src/views/mall/promotion/discountActivity/index.vue

@@ -84,8 +84,8 @@
         </el-form-item>
         <el-form-item label="商品选择">
           <el-select v-model="form.skuIds" placeholder="请选择活动商品" clearable size="small"
-                     multiple filterable style="width: 400px" @change="changeFormSku">
-            <el-option v-for="item in productSkus" :key="item.id" :label="item.name" :value="item.id">
+                     multiple filterable style="width: 880px" @change="changeFormSku">
+            <el-option v-for="item in productSkus" :key="item.id" :label="item.spuName + ' ' + item.name" :value="item.id">
               <span style="float: left">{{ item.spuName }} &nbsp; {{ item.name}}</span>
               <span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.price / 100.0).toFixed(2) }}</span>
             </el-option>
@@ -102,7 +102,7 @@
               </template>
             </el-table-column>
             <el-table-column label="库存" align="center" prop="stock" />
-            <el-table-column label="优惠类型" align="center">
+            <el-table-column label="优惠类型" align="center" property="discountType">
               <template slot-scope="scope">
                 <el-select v-model="scope.row.discountType" placeholder="请选择优惠类型">
                   <el-option v-for="dict in getDictDatas(DICT_TYPE.PROMOTION_DISCOUNT_TYPE)"
@@ -114,7 +114,7 @@
               <template slot-scope="scope">
                 <el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PRICE.type" prop="discountPrice">
                   减 <el-input-number v-model="scope.row.discountPrice" placeholder="请输入优惠金额"
-                                      style="width: 190px" :precision="2" :min="0" /> 元
+                                      style="width: 190px" :precision="2" :min="0" :max="scope.row.price / 100.0 - 0.01" /> 元
                 </el-form-item>
                 <el-form-item v-if="scope.row.discountType === PromotionDiscountTypeEnum.PERCENT.type" prop="discountPercent">
                   打 <el-input-number v-model="scope.row.discountPercent" placeholder="请输入优惠折扣"
@@ -139,12 +139,20 @@
 </template>
 
 <script>
-import { createDiscountActivity, updateDiscountActivity, deleteDiscountActivity, getDiscountActivity, getDiscountActivityPage } from "@/api/mall/promotion/discountActivity";
+import {
+  createDiscountActivity,
+  updateDiscountActivity,
+  deleteDiscountActivity,
+  getDiscountActivity,
+  getDiscountActivityPage,
+  closeDiscountActivity
+} from "@/api/mall/promotion/discountActivity";
 import {
   PromotionActivityStatusEnum, PromotionDiscountTypeEnum,
   PromotionProductScopeEnum
 } from "@/utils/constants";
 import { getSkuOptionList } from "@/api/mall/product/sku";
+import { deepClone } from "@/utils";
 
 export default {
   name: "DiscountActivity",
@@ -165,7 +173,7 @@ export default {
       // 弹出层名称
       title: "",
       // 是否显示弹出层
-      open: true,
+      open: false,
       // 查询参数
       queryParams: {
         pageNo: 1,
@@ -178,28 +186,12 @@ export default {
       form: {
         skuIds: [], // 选中的 SKU
         products: [], // 商品信息
-        // products: [{
-        //   id: 2,
-        //   name: '白色',
-        //   price: 500,
-        //   stock: 20,
-        //   spuId: 1,
-        //   spuName: 'iPhone 14 Pro',
-        //   discountType: 1,
-        // }, {
-        //   id: 10,
-        //   name: '蓝色',
-        //   price: 1000,
-        //   stock: 100,
-        //   spuId: 20,
-        //   spuName: 'iPhone 14 Pro',
-        //   discountType: 2,
-        // }]
       },
       // 表单校验
       rules: {
         name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
         startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
+        skuIds: [{ required: true, message: "选择商品不能为空", trigger: "blur" }],
       },
       // 商品 SKU 列表
       productSkus: [],
@@ -268,7 +260,24 @@ export default {
       const id = row.id;
       getDiscountActivity(id).then(response => {
         this.form = response.data;
+        // 修改数据
         this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
+        this.form.skuIds = response.data.products.map(item => item.skuId);
+        this.form.products.forEach(product => {
+          // 获得对应的 SKU 信息
+          const sku = this.productSkus.find(item => item.id === product.skuId);
+          if (!sku) {
+            return;
+          }
+          // 设置商品信息
+          product.name = sku.name;
+          product.spuName = sku.spuName;
+          product.price = sku.price;
+          product.stock = sku.stock;
+          product.discountPrice = product.discountPrice !== undefined ? product.discountPrice / 100.0 : undefined;
+          product.discountPercent = product.discountPercent !== undefined ? product.discountPercent / 10.0 : undefined;
+        });
+        // 打开弹窗
         this.open = true;
         this.title = "修改限时折扣活动";
       });
@@ -279,11 +288,21 @@ export default {
         if (!valid) {
           return;
         }
-        this.form.startTime = this.form.startAndEndTime[0];
-        this.form.endTime = this.form.startAndEndTime[1];
+        // 处理数据
+        const data = deepClone(this.form); // 必须深拷贝,不然后面的 products 操作会有影响
+        data.startTime = this.form.startAndEndTime[0];
+        data.endTime = this.form.startAndEndTime[1];
+        data.products.forEach(product => {
+          product.discountPrice = product.discountPrice !== undefined ? product.discountPrice * 100 : undefined;
+          product.discountPercent = product.discountPercent !== undefined ? product.discountPercent * 10 : undefined;
+        });
+        if (!valid) {
+          return;
+        }
+
         // 修改的提交
         if (this.form.id != null) {
-          updateDiscountActivity(this.form).then(response => {
+          updateDiscountActivity(data).then(response => {
             this.$modal.msgSuccess("修改成功");
             this.open = false;
             this.getList();
@@ -291,7 +310,7 @@ export default {
           return;
         }
         // 添加的提交
-        createDiscountActivity(this.form).then(response => {
+        createDiscountActivity(data).then(response => {
           this.$modal.msgSuccess("新增成功");
           this.open = false;
           this.getList();
@@ -312,7 +331,7 @@ export default {
     handleClose(row) {
       const id = row.id;
       this.$modal.confirm('是否确认关闭限时折扣活动编号为"' + id + '"的数据项?').then(function() {
-        return closeRewardActivity(id);
+        return closeDiscountActivity(id);
       }).then(() => {
         this.getList();
         this.$modal.msgSuccess("关闭成功");
@@ -325,13 +344,11 @@ export default {
         // 获得对应的 SKU 信息
         const sku = this.productSkus.find(item => item.id === skuId);
         if (!sku) {
-          // debugger
           return;
         }
         // 判断已存在,直接跳过
         const product = this.form.products.find(item => item.skuId === skuId);
         if (product) {
-          // debugger
           return;
         }
         this.form.products.push({