Эх сурвалжийг харах

promotion:增加满减送活动的关闭功能

YunaiV 2 жил өмнө
parent
commit
00f90e75d9

+ 4 - 0
yudao-module-mall/yudao-module-promotion-api/src/main/java/cn/iocoder/yudao/module/promotion/enums/ErrorCodeConstants.java

@@ -30,5 +30,9 @@ public interface ErrorCodeConstants {
     // ========== 满减送活动 1003006000 ==========
     ErrorCode REWARD_ACTIVITY_NOT_EXISTS = new ErrorCode(1003006000, "满减送活动不存在");
     ErrorCode REWARD_ACTIVITY_SPU_CONFLICTS = new ErrorCode(1003006001, "商品({}) 已经参加满减送活动({})");
+    ErrorCode REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED = new ErrorCode(1003006002, "满减送活动已关闭,不能修改");
+    ErrorCode REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED = new ErrorCode(1003006003, "满减送活动未关闭,不能删除");
+    ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED = new ErrorCode(1003006004, "满减送活动已关闭,不能重复关闭");
+    ErrorCode REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END = new ErrorCode(1003006004, "满减送活动已结束,不能关闭");
 
 }

+ 9 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/controller/admin/reward/RewardActivityController.java

@@ -45,6 +45,15 @@ public class RewardActivityController {
         return success(true);
     }
 
+    @PutMapping("/close")
+    @ApiOperation("关闭满减送活动")
+    @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)
+    @PreAuthorize("@ss.hasPermission('promotion:reward-activity:close')")
+    public CommonResult<Boolean> closeRewardActivity(@RequestParam("id") Long id) {
+        rewardActivityService.closeRewardActivity(id);
+        return success(true);
+    }
+
     @DeleteMapping("/delete")
     @ApiOperation("删除满减送活动")
     @ApiImplicitParam(name = "id", value = "编号", required = true, dataTypeClass = Long.class)

+ 7 - 0
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityService.java

@@ -32,6 +32,13 @@ public interface RewardActivityService {
      */
     void updateRewardActivity(@Valid RewardActivityUpdateReqVO updateReqVO);
 
+    /**
+     * 关闭满减送活动
+     *
+     * @param id 活动编号
+     */
+    void closeRewardActivity(Long id);
+
     /**
      * 删除满减送活动
      *

+ 31 - 6
yudao-module-mall/yudao-module-promotion-biz/src/main/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImpl.java

@@ -8,6 +8,7 @@ import cn.iocoder.yudao.module.promotion.controller.admin.reward.vo.RewardActivi
 import cn.iocoder.yudao.module.promotion.convert.reward.RewardActivityConvert;
 import cn.iocoder.yudao.module.promotion.dal.dataobject.reward.RewardActivityDO;
 import cn.iocoder.yudao.module.promotion.dal.mysql.reward.RewardActivityMapper;
+import cn.iocoder.yudao.module.promotion.enums.common.PromotionActivityStatusEnum;
 import cn.iocoder.yudao.module.promotion.util.PromotionUtils;
 import org.springframework.stereotype.Service;
 import org.springframework.validation.annotation.Validated;
@@ -19,8 +20,7 @@ import java.util.Map;
 import java.util.Set;
 
 import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
-import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_NOT_EXISTS;
-import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.REWARD_ACTIVITY_SPU_CONFLICTS;
+import static cn.iocoder.yudao.module.promotion.enums.ErrorCodeConstants.*;
 
 /**
  * 满减送活动 Service 实现类
@@ -50,7 +50,10 @@ public class RewardActivityServiceImpl implements RewardActivityService {
     @Override
     public void updateRewardActivity(RewardActivityUpdateReqVO updateReqVO) {
         // 校验存在
-        validateRewardActivityExists(updateReqVO.getId());
+        RewardActivityDO dbRewardActivity = validateRewardActivityExists(updateReqVO.getId());
+        if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能修改噢
+            throw exception(REWARD_ACTIVITY_UPDATE_FAIL_STATUS_CLOSED);
+        }
         validateRewardActivitySpuConflicts(updateReqVO.getId(), updateReqVO.getProductSpuIds());
 
         // 更新
@@ -59,18 +62,40 @@ public class RewardActivityServiceImpl implements RewardActivityService {
         rewardActivityMapper.updateById(updateObj);
     }
 
+    @Override
+    public void closeRewardActivity(Long id) {
+        // 校验存在
+        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
+        if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 已关闭的活动,不能关闭噢
+            throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_CLOSED);
+        }
+        if (dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.END.getStatus())) { // 已关闭的活动,不能关闭噢
+            throw exception(REWARD_ACTIVITY_CLOSE_FAIL_STATUS_END);
+        }
+
+        // 更新
+        RewardActivityDO updateObj = new RewardActivityDO().setId(id).setStatus(PromotionActivityStatusEnum.CLOSE.getStatus());
+        rewardActivityMapper.updateById(updateObj);
+    }
+
     @Override
     public void deleteRewardActivity(Long id) {
         // 校验存在
-        validateRewardActivityExists(id);
+        RewardActivityDO dbRewardActivity = validateRewardActivityExists(id);
+        if (!dbRewardActivity.getStatus().equals(PromotionActivityStatusEnum.CLOSE.getStatus())) { // 未关闭的活动,不能删除噢
+            throw exception(REWARD_ACTIVITY_DELETE_FAIL_STATUS_NOT_CLOSED);
+        }
+
         // 删除
         rewardActivityMapper.deleteById(id);
     }
 
-    private void validateRewardActivityExists(Long id) {
-        if (rewardActivityMapper.selectById(id) == null) {
+    private RewardActivityDO validateRewardActivityExists(Long id) {
+        RewardActivityDO activity = rewardActivityMapper.selectById(id);
+        if (activity == null) {
             throw exception(REWARD_ACTIVITY_NOT_EXISTS);
         }
+        return activity;
     }
 
     /**

+ 17 - 2
yudao-module-mall/yudao-module-promotion-biz/src/test/java/cn/iocoder/yudao/module/promotion/service/reward/RewardActivityServiceImplTest.java

@@ -66,7 +66,7 @@ public class RewardActivityServiceImplTest extends BaseDbUnitTest {
     @Test
     public void testUpdateRewardActivity_success() {
         // mock 数据
-        RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class);
+        RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()));
         rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
         // 准备参数
         RewardActivityUpdateReqVO reqVO = randomPojo(RewardActivityUpdateReqVO.class, o -> {
@@ -88,6 +88,21 @@ public class RewardActivityServiceImplTest extends BaseDbUnitTest {
         }
     }
 
+    @Test
+    public void testCloseRewardActivity() {
+        // mock 数据
+        RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.WAIT.getStatus()));
+        rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
+        // 准备参数
+        Long id = dbRewardActivity.getId();
+
+        // 调用
+        rewardActivityService.closeRewardActivity(id);
+        // 校验状态
+        RewardActivityDO rewardActivity = rewardActivityMapper.selectById(id);
+        assertEquals(rewardActivity.getStatus(), PromotionActivityStatusEnum.CLOSE.getStatus());
+    }
+
     @Test
     public void testUpdateRewardActivity_notExists() {
         // 准备参数
@@ -100,7 +115,7 @@ public class RewardActivityServiceImplTest extends BaseDbUnitTest {
     @Test
     public void testDeleteRewardActivity_success() {
         // mock 数据
-        RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class);
+        RewardActivityDO dbRewardActivity = randomPojo(RewardActivityDO.class, o -> o.setStatus(PromotionActivityStatusEnum.CLOSE.getStatus()));
         rewardActivityMapper.insert(dbRewardActivity);// @Sql: 先插入出一条存在的数据
         // 准备参数
         Long id = dbRewardActivity.getId();

+ 52 - 0
yudao-ui-admin/src/api/mall/promotion/rewardActivity.js

@@ -0,0 +1,52 @@
+import request from '@/utils/request'
+
+// 创建满减送活动
+export function createRewardActivity(data) {
+  return request({
+    url: '/promotion/reward-activity/create',
+    method: 'post',
+    data: data
+  })
+}
+
+// 更新满减送活动
+export function updateRewardActivity(data) {
+  return request({
+    url: '/promotion/reward-activity/update',
+    method: 'put',
+    data: data
+  })
+}
+
+// 关闭满减送活动
+export function closeRewardActivity(id) {
+  return request({
+    url: '/promotion/reward-activity/close?id=' + id,
+    method: 'put'
+  })
+}
+
+// 删除满减送活动
+export function deleteRewardActivity(id) {
+  return request({
+    url: '/promotion/reward-activity/delete?id=' + id,
+    method: 'delete'
+  })
+}
+
+// 获得满减送活动
+export function getRewardActivity(id) {
+  return request({
+    url: '/promotion/reward-activity/get?id=' + id,
+    method: 'get'
+  })
+}
+
+// 获得满减送活动分页
+export function getRewardActivityPage(query) {
+  return request({
+    url: '/promotion/reward-activity/page',
+    method: 'get',
+    params: query
+  })
+}

+ 22 - 0
yudao-ui-admin/src/utils/constants.js

@@ -292,3 +292,25 @@ export const PromotionConditionTypeEnum = {
     name: '满 N 件'
   }
 }
+
+/**
+ * 促销活动的状态枚举
+ */
+export const PromotionActivityStatusEnum = {
+  WAIT: {
+    type: 10,
+    name: '未开始'
+  },
+  RUN: {
+    type: 20,
+    name: '进行中'
+  },
+  END: {
+    type: 30,
+    name: '已结束'
+  },
+  CLOSE: {
+    type: 40,
+    name: '已关闭'
+  }
+}

+ 305 - 0
yudao-ui-admin/src/views/mall/promotion/rewardActivity/index.vue

@@ -0,0 +1,305 @@
+<template>
+  <div class="app-container">
+
+    <!-- 搜索工作栏 -->
+    <el-form :model="queryParams" ref="queryForm" size="small" :inline="true" v-show="showSearch" label-width="68px">
+      <el-form-item label="活动名称" prop="name">
+        <el-input v-model="queryParams.name" placeholder="请输入活动名称" clearable @keyup.enter.native="handleQuery"/>
+      </el-form-item>
+      <el-form-item label="活动状态" prop="status">
+        <el-select v-model="queryParams.status" placeholder="请选择活动状态" clearable size="small">
+          <el-option v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_ACTIVITY_STATUS)"
+                       :key="dict.value" :label="dict.label" :value="dict.value"/>
+        </el-select>
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" icon="el-icon-search" @click="handleQuery">搜索</el-button>
+        <el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 操作工具栏 -->
+    <el-row :gutter="10" class="mb8">
+      <el-col :span="1.5">
+        <el-button type="primary" plain icon="el-icon-plus" size="mini" @click="handleAdd"
+                   v-hasPermi="['promotion:reward-activity:create']">新增</el-button>
+      </el-col>
+      <right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
+    </el-row>
+
+    <!-- 列表 -->
+    <el-table v-loading="loading" :data="list">
+      <el-table-column label="活动名称" align="center" prop="name" />
+      <el-table-column label="活动时间" align="center" prop="startTime" width="240">
+        <template slot-scope="scope">
+          <div>开始:{{ parseTime(scope.row.startTime) }}</div>
+          <div>结束:{{ parseTime(scope.row.endTime) }}</div>
+        </template>
+      </el-table-column>
+      <el-table-column label="状态" align="center" prop="status">
+        <template slot-scope="scope">
+          <dict-tag :type="DICT_TYPE.PROMOTION_ACTIVITY_STATUS" :value="scope.row.status" />
+        </template>
+      </el-table-column>
+      <el-table-column label="创建时间" align="center" prop="createTime" width="180">
+        <template slot-scope="scope">
+          <span>{{ parseTime(scope.row.createTime) }}</span>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
+        <template slot-scope="scope">
+          <el-button size="mini" type="text" icon="el-icon-edit" @click="handleUpdate(scope.row)"
+                     v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type"
+                     v-hasPermi="['promotion:reward-activity:update']">修改</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleClose(scope.row)"
+                     v-if="scope.row.status !== PromotionActivityStatusEnum.CLOSE.type &&
+                            scope.row.status !== PromotionActivityStatusEnum.END.type"
+                     v-hasPermi="['promotion:reward-activity:close']">关闭</el-button>
+          <el-button size="mini" type="text" icon="el-icon-delete" @click="handleDelete(scope.row)"
+                     v-if="scope.row.status === PromotionActivityStatusEnum.CLOSE.type"
+                     v-hasPermi="['promotion:reward-activity:delete']">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+    <!-- 分页组件 -->
+    <pagination v-show="total > 0" :total="total" :page.sync="queryParams.pageNo" :limit.sync="queryParams.pageSize"
+                @pagination="getList"/>
+
+    <!-- 对话框(添加 / 修改) -->
+    <el-dialog :title="title" :visible.sync="open" width="600px" v-dialogDrag append-to-body>
+      <el-form ref="form" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="活动名称" prop="name">
+          <el-input v-model="form.name" placeholder="请输入活动名称" />
+        </el-form-item>
+        <el-form-item label="活动时间" prop="startAndEndTime">
+          <el-date-picker clearable v-model="form.startAndEndTime" type="datetimerange" :default-time="['00:00:00', '23:59:59']"
+                          value-format="timestamp" placeholder="选择开始时间" style="width: 480px" />
+        </el-form-item>
+        <el-form-item label="条件类型" prop="conditionType">
+          <el-radio-group v-model="form.conditionType">
+            <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_CONDITION_TYPE)"
+                      :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item label="优惠设置" prop="conditionType">
+          <!-- TODO 芋艿:待实现! -->
+        </el-form-item>
+        <el-form-item label="活动商品" prop="productScope">
+          <el-radio-group v-model="form.productScope">
+            <el-radio v-for="dict in this.getDictDatas(DICT_TYPE.PROMOTION_PRODUCT_SCOPE)"
+                      :key="dict.value" :label="parseInt(dict.value)">{{dict.label}}</el-radio>
+          </el-radio-group>
+        </el-form-item>
+        <el-form-item v-if="form.productScope === PromotionProductScopeEnum.SPU.scope" prop="productSpuIds">
+          <el-select v-model="form.productSpuIds" placeholder="请选择活动商品" clearable size="small"
+                     multiple filterable style="width: 400px">
+            <el-option v-for="item in productSpus" :key="item.id" :label="item.name" :value="item.id">
+              <span style="float: left">{{ item.name }}</span>
+              <span style="float: right; color: #8492a6; font-size: 13px">¥{{ (item.minPrice / 100.0).toFixed(2) }}</span>
+            </el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="备注" prop="remark">
+          <el-input v-model="form.remark" placeholder="请输入备注" />
+        </el-form-item>
+      </el-form>
+      <div slot="footer" class="dialog-footer">
+        <el-button type="primary" @click="submitForm">确 定</el-button>
+        <el-button @click="cancel">取 消</el-button>
+      </div>
+    </el-dialog>
+  </div>
+</template>
+
+<script>
+import {
+  createRewardActivity,
+  updateRewardActivity,
+  deleteRewardActivity,
+  getRewardActivity,
+  getRewardActivityPage,
+  closeRewardActivity
+} from "@/api/mall/promotion/rewardActivity";
+import {
+  PromotionConditionTypeEnum,
+  PromotionProductScopeEnum,
+  PromotionActivityStatusEnum
+} from "@/utils/constants";
+import {getSpuSimpleList} from "@/api/mall/product/spu";
+
+export default {
+  name: "RewardActivity",
+  components: {
+  },
+  data() {
+    return {
+      // 遮罩层
+      loading: true,
+      // 导出遮罩层
+      exportLoading: false,
+      // 显示搜索条件
+      showSearch: true,
+      // 总条数
+      total: 0,
+      // 满减送活动列表
+      list: [],
+      // 弹出层标题
+      title: "",
+      // 是否显示弹出层
+      open: false,
+      // 查询参数
+      queryParams: {
+        pageNo: 1,
+        pageSize: 10,
+        name: null,
+        status: null,
+      },
+      // 表单参数
+      form: {},
+      // 表单校验
+      rules: {
+        name: [{ required: true, message: "活动名称不能为空", trigger: "blur" }],
+        startAndEndTime: [{ required: true, message: "活动时间不能为空", trigger: "blur" }],
+        conditionType: [{ required: true, message: "条件类型不能为空", trigger: "change" }],
+        productScope: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
+        productSpuIds: [{ required: true, message: "商品范围不能为空", trigger: "blur" }],
+      },
+      // 商品列表
+      productSpus: [],
+      // 如下的变量,主要为了 v-if 判断可以使用到
+      PromotionProductScopeEnum: PromotionProductScopeEnum,
+      PromotionActivityStatusEnum: PromotionActivityStatusEnum,
+    };
+  },
+  created() {
+    this.getList();
+    // 查询商品列表
+    getSpuSimpleList().then(response => {
+      this.productSpus = response.data
+    })
+  },
+  methods: {
+    /** 查询列表 */
+    getList() {
+      this.loading = true;
+      // 执行查询
+      getRewardActivityPage(this.queryParams).then(response => {
+        this.list = response.data.list;
+        this.total = response.data.total;
+        this.loading = false;
+      });
+    },
+    /** 取消按钮 */
+    cancel() {
+      this.open = false;
+      this.reset();
+    },
+    /** 表单重置 */
+    reset() {
+      this.form = {
+        id: undefined,
+        name: undefined,
+        startAndEndTime: undefined,
+        startTime: undefined,
+        endTime: undefined,
+        conditionType: PromotionConditionTypeEnum.PRICE.type,
+        remark: undefined,
+        productScope: PromotionProductScopeEnum.ALL.scope,
+        productSpuIds: undefined,
+        rules: undefined,
+      };
+      this.resetForm("form");
+    },
+    /** 搜索按钮操作 */
+    handleQuery() {
+      this.queryParams.pageNo = 1;
+      this.getList();
+    },
+    /** 重置按钮操作 */
+    resetQuery() {
+      this.resetForm("queryForm");
+      this.handleQuery();
+    },
+    /** 新增按钮操作 */
+    handleAdd() {
+      this.reset();
+      this.open = true;
+      this.title = "添加满减送活动";
+    },
+    /** 修改按钮操作 */
+    handleUpdate(row) {
+      this.reset();
+      const id = row.id;
+      getRewardActivity(id).then(response => {
+        this.form = response.data;
+        this.form.startAndEndTime = [response.data.startTime, response.data.endTime];
+        this.open = true;
+        this.title = "修改满减送活动";
+      });
+    },
+    /** 提交按钮 */
+    submitForm() {
+      this.$refs["form"].validate(valid => {
+        if (!valid) {
+          return;
+        }
+        this.form.startTime = this.form.startAndEndTime[0];
+        this.form.endTime = this.form.startAndEndTime[1];
+        // TODO 芋艿:临时实现
+        this.form.rules = [
+          {
+            limit: 1,
+            discountPrice: 10,
+            freeDelivery: true,
+            point: 10,
+            couponIds: [10, 20],
+            couponCounts: [1, 2]
+          }, {
+            limit: 2,
+            discountPrice: 20,
+            freeDelivery: false,
+            point: 20,
+            couponIds: [30, 40],
+            couponCounts: [3, 4]
+          }
+        ];
+        // 修改的提交
+        if (this.form.id != null) {
+          updateRewardActivity(this.form).then(response => {
+            this.$modal.msgSuccess("修改成功");
+            this.open = false;
+            this.getList();
+          });
+          return;
+        }
+        // 添加的提交
+        createRewardActivity(this.form).then(response => {
+          this.$modal.msgSuccess("新增成功");
+          this.open = false;
+          this.getList();
+        });
+      });
+    },
+    /** 删除按钮操作 */
+    handleDelete(row) {
+      const id = row.id;
+      this.$modal.confirm('是否确认删除满减送活动编号为"' + id + '"的数据项?').then(function() {
+          return deleteRewardActivity(id);
+        }).then(() => {
+          this.getList();
+          this.$modal.msgSuccess("删除成功");
+        }).catch(() => {});
+    },
+    /** 关闭按钮操作 */
+    handleClose(row) {
+      const id = row.id;
+      this.$modal.confirm('是否确认关闭满减送活动编号为"' + id + '"的数据项?').then(function() {
+        return closeRewardActivity(id);
+      }).then(() => {
+        this.getList();
+        this.$modal.msgSuccess("关闭成功");
+      }).catch(() => {});
+    }
+  }
+};
+</script>