跳至主要內容

优惠券

起凡大约 3 分钟

优惠券

优惠券创建(图1)
优惠券创建(图1)
优惠券赠送(图2)
优惠券赠送(图2)

表设计

图1中的表单字段和优惠券表的字段可以一一对应。如果优惠类型是打折,那么discount字段就是必填,amount字段可为空。如果优惠券类型是满减,那么相反。

create table coupon
(
    id                varchar(36)    not null
        primary key,
    created_time      datetime(6)    not null,
    edited_time       datetime(6)    not null,
    creator_id        varchar(36)    not null,
    editor_id         varchar(36)    not null,
    name              varchar(255)   not null comment '优惠券名称',
    threshold_amount  decimal(10, 2) not null comment '使用门槛',
    released_quantity int            not null comment '发放数量',
    coupon_type       varchar(36)    not null comment '优惠类型(打折,满减)',
    scope_type        varchar(36)    not null comment '使用范围(通用,指定商品,类别)',
    amount            decimal(10, 2) null comment '优惠金额',
    discount          decimal(4, 2)  null comment '折扣',
    status            tinyint(1)     null comment '是否启用',
    effective_date    datetime       not null comment '生效日期',
    expiration_dae    datetime       not null comment '失效日期',
    constraint coupon_pk
        unique (name)
)
    comment '优惠券';

用户优惠券关系表,优惠券的来源有很多种,可以使用receive_type表示。这边演示的来源方式只有后台赠送一种。如图2所示,在优惠券上点赠送按钮即可批量选择用户赠送优惠券。

create table coupon_user_rel
(
    id           varchar(36) not null
        primary key,
    created_time datetime(6) not null,
    edited_time  datetime(6) not null,
    creator_id   varchar(36) not null,
    editor_id    varchar(36) not null,
    receive_type varchar(36) not null comment '获得渠道(后台赠送,手动领取)',
    coupon_id    varchar(36) not null comment '优惠券id',
    status       varchar(36) not null comment '使用状态(待使用,已使用,过期)',
    user_id      varchar(36) not null comment '用户id'
)
    comment '用户优惠券';

优惠券赠送

vue

通过<user-choose-table></user-choose-table>组件加上注入userTableHelper提供用户表的分页查询。

选择完需要赠送的用户之后点击确认按钮触发handleConfirm,在方法内使用userTableHelper.getTableSelectedRows()获取已选的数据,得到userIds

将用户id列表和优惠券id传给api.couponForAdminController.gift

<script setup lang="ts">
import UserChooseTable from '@/views/user/components/user-choose-table.vue'
import { Present } from '@element-plus/icons-vue'
import { provide, ref } from 'vue'
import { api } from '@/utils/api-instance'
import { useTableHelper } from '@/components/base/table/table-helper'
import { ElMessage } from 'element-plus'

const userTableHelper = useTableHelper(
  api.userForAdminController.query,
  api.userForAdminController,
  {}
)
provide('userTableHelper', userTableHelper)
const props = defineProps<{ couponId: string }>()
const visible = ref(false)
const handleConfirm = async () => {
  await api.couponForAdminController.gift({
    body: {
      userIds: userTableHelper.getTableSelectedRows().map((row) => row.id),
      id: props.couponId
    }
  })
  ElMessage.success('赠送成功')
  visible.value = false
}
const handleOpen = () => {
  visible.value = true
  userTableHelper.reloadTableData()
}
</script>

<template>
  <el-button type="primary" link size="small" @click="handleOpen">
    <el-icon>
      <present></present>
    </el-icon>
  </el-button>
  <el-dialog append-to-body v-model="visible">
    <user-choose-table></user-choose-table>
    <template #footer>
      <el-button type="primary" @click="handleConfirm">确认</el-button>
    </template>
  </el-dialog>
</template>

<style scoped lang="scss"></style>

优惠券计算

传入优惠券id,校验是否符使用条件,并根据优惠券的优惠类型(满减,折扣)得到优惠金额。

    public BigDecimal calculate(String id, BigDecimal amount) {
        if (!StringUtils.hasText(id)) return BigDecimal.ZERO;
        CouponUserRel couponUserRel = couponUserRelRepository.findById(id, CouponUserRelRepository.COMPLEX_FETCHER_FOR_ADMIN)
                .orElseThrow(() -> new BusinessException(ResultCode.NotFindError, "优惠券不存在"));
        checkCouponUser(couponUserRel);
        Coupon coupon = couponUserRel.coupon();
        if (coupon.couponType().equals(DictConstants.CouponType.DISCOUNT)) {
            return BigDecimal.TEN.subtract(coupon.discount()).divide(BigDecimal.TEN, RoundingMode.DOWN)
                    .multiply(amount);
        } else if (coupon.couponType().equals(DictConstants.CouponType.REDUCE)) {
            return coupon.amount();
        }
        throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券类型错误");
    }
    
    public void checkCouponUser(CouponUserRel couponUserRel) {
        if (!couponUserRel.status().equals(DictConstants.CouponUseStatus.UNUSED)) {
            throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券已使用");
        }
        if (!couponUserRel.user().id().equals(StpUtil.getLoginIdAsString())) {
            throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券非本人");
        }
        if (!couponUserRel.coupon().effectiveDate().isBefore(LocalDateTime.now())) {
            throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券未生效");
        }
        if (!couponUserRel.coupon().expirationDate().isAfter(LocalDateTime.now())) {
            throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券已过期");
        }
        if (!couponUserRel.coupon().status()) {
            throw new BusinessException(ResultCode.ParamSetIllegal, "优惠券已下架");
        }
    }

注意

目前的优惠券的使用范围只有通用券一种,如果要拓展品类券和商品券则需要增加两种配置表,并且优惠券计算参数也需要修改。