【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作)

这篇具有很好参考价值的文章主要介绍了【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

🍔发放优惠券

🎆基本操作

🎄数据库表

普通券

我们来看这一张表
【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java
里面包含了主键,商铺id,使用规则,时间等内容
可以看到里面没有库存,意味着所有人都可以来购买,所以是普通券

秒杀券

我们看下面这一张表

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java
这是一张秒杀券,里面包含了普通券的所有信息,还有秒杀券独有的特点,比如库存,生效时间,生效时间等信息

🛸思路

  • 秒杀是否开始或者结束,如果尚未开始或者已经结束就无法下单
  • 库存是否充足,如果不足,就无法下单

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

🌹代码实现

VoucherOrderController

package com.hmdp.controller;


import com.hmdp.dto.Result;
import com.hmdp.service.IVoucherOrderService;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;

import org.springframework.web.bind.annotation.RestController;

import javax.annotation.Resource;

@RestController
@RequestMapping("/voucher-order")
public class VoucherOrderController {

    @Resource
    private IVoucherOrderService voucherOrderService;

    @PostMapping("seckill/{id}")
    public Result seckillVoucher(@PathVariable("id") Long voucherId) {
        return voucherOrderService.seckillVoucher(voucherId);
    }
}

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java


【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

package com.hmdp.service;

import com.hmdp.dto.Result;
import com.hmdp.entity.VoucherOrder;
import com.baomidou.mybatisplus.extension.service.IService;

/**
 * <p>
 *  服务类
 * </p>
 */
public interface IVoucherOrderService extends IService<VoucherOrder> {

    Result seckillVoucher(Long voucherId);
}


【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;
    
    @Resource
    private RedisIdWorker redisIdWorker;

    @Override
    @Transactional   //由于使用了2张表,这里加上事务比较好,一旦重新了问题,可以及时回滚
    public Result seckillVoucher(Long voucherId) {
        //查询优惠券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //判断秒杀是否开始
        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
            //尚未开始
            return Result.fail("秒杀尚未开始");
        }
        //判断秒杀是否结束
        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
            //已结束
            return Result.fail("秒杀已结束");
        }
        //判断库存是否充足
        if(voucher.getStock() < 1){
            //库存不足
            return Result.fail("库存不足");
        }
        //扣减库存
            //mybatisplus
        boolean success=seckillVoucherService.update().setSql("stock = stock - 1").eq("voucher_id", voucherId).update();
        if(!success){
            return Result.fail("扣减库存失败");
        }
        //创建订单
        VoucherOrder voucherOrder=new VoucherOrder();
            //订单id
        long ordrId=redisIdWorker.nextId("order");
        voucherOrder.setId(ordrId);
            //用户id
        long userId=UserHolder.getUser().getId();
        voucherOrder.setUserId(userId);
            //代金券id
        voucherOrder.setVoucherId(voucherId);
        //把订单写入数据库
        save(voucherOrder);
        //返回订单id
        return Result.ok(ordrId);
        
    }
}

我们使用上面的操作,线程少的话,没问题,可以执行

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

但是线程多的话,就会发生线程安全问题

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

于是我们可以使用下面的方法来解决问题

🎆完善后的操作

🛸乐观锁

乐观锁(Optimistic Locking)是一种并发控制机制,用于多线程或分布式系统中的数据一致性控制。它假设不会有或尽可能减少冲突,因此不会每次都进行锁冲突的检查。在使用乐观锁时,多个用户可以同时读取数据,但只有在更新数据时才检查版本号等机制来判断数据是否被其他用户修改。 在使用乐观锁时,通常会通过在数据上添加版本号(Version)信息来实现。当用户读取数据时,会将数据的版本号一并读取。当用户提交更新请求时,系统会先检查数据的版本号是否与之前读取的一致。如果一致,则更新成功;如果不一致,则表示数据已被其他用户修改,更新失败。

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

🌹代码实现

整体代码都差不多,其实就修改一部分就行了


我们进入VoucherOrderServiceImpl

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java
库存大于0,证明有库存,这样子就行了

🍔一人仅一张优惠券

对于有些优惠力度比较大的券,为了防止黄牛,我们需要设置一人一张券

🛸思路

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

🌹代码

VoucherOrderServiceImpl

@Slf4j
@Service
public class VoucherOrderServiceImpl extends ServiceImpl<VoucherOrderMapper, VoucherOrder> implements IVoucherOrderService {

    @Resource
    private ISeckillVoucherService seckillVoucherService;

    @Resource
    private RedisIdWorker redisIdWorker;

    @Override
    public Result seckillVoucher(Long voucherId) {
        //查询优惠券
        SeckillVoucher voucher = seckillVoucherService.getById(voucherId);
        //判断秒杀是否开始
        if(voucher.getBeginTime().isAfter(LocalDateTime.now())){
            //尚未开始
            return Result.fail("秒杀尚未开始");
        }
        //判断秒杀是否结束
        if(voucher.getEndTime().isBefore(LocalDateTime.now())){
            //已结束
            return Result.fail("秒杀已结束");
        }
        //判断库存是否充足
        if(voucher.getStock() < 1){
            //库存不足
            return Result.fail("库存不足");
        }

        Long userId=UserHolder.getUser().getId();

        synchronized(userId.toString().intern()) {
            //获取代理对象
            IVoucherOrderService proxy= (IVoucherOrderService) AopContext.currentProxy();
			//代理对象进行调用
            return proxy.createVoucherOrder(voucherId);
        }

    }
    
    
    //上面操作都是查询,不需要添加@Transactional了

    @Transactional
    public Result createVoucherOrder(Long voucherId) {
        //一人一单
        Long userId=UserHolder.getUser().getId();

            //查询订单
            int count = query().eq("user_id", userId).eq("voucher_id", voucherId).count();
            //判断是否存在
            if (count > 0) {
                return Result.fail("用户已购买过该优惠券");
            }

            //扣减库存
            //mybatisplus
            boolean success = seckillVoucherService.update()
                    .setSql("stock = stock - 1")
                    .eq("voucher_id", voucherId).gt("stock", 0) //增加对stock值的判断
                    .update();
            if (!success) {
                return Result.fail("扣减库存失败");
            }

            //count< = 0的时候
            //创建订单
            VoucherOrder voucherOrder = new VoucherOrder();
            //订单id
            long ordrId = redisIdWorker.nextId("order");
            voucherOrder.setId(ordrId);
            //用户id
//        long userId=UserHolder.getUser().getId();
            voucherOrder.setUserId(userId);
            //代金券id
            voucherOrder.setVoucherId(voucherId);
            //把订单写入数据库
            save(voucherOrder);
            //返回订单id
            return Result.ok(ordrId);
    }
}

IVoucherOrderService

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java

要使用代理,需要在pom文件中加入下面的代码

<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
        </dependency>

并且需要在启动类上加上注解@EnableAspectJAutoProxy(exposeProxy = true) 来暴露这个代理对象

⭐代码分析

为什么要加上锁

在该函数中,使用了synchronized关键字加上锁,这是为了确保在多线程环境下,同一时间只有一个线程能够执行该代码块。这样可以避免多个线程同时修改共享资源导致的数据不一致问题。具体来说,在该代码块中,使用了线程的id作为锁,可以确保每个线程都有自己的锁,互不干扰。通过加锁,能够保证在多线程环境下,该代码块的执行是线程安全的。

synchronized(userId.toString().intern()) {
//获取代理对象
IVoucherOrderService proxy= (IVoucherOrderService) AopContext.currentProxy();
//代理对象进行调用
return proxy.createVoucherOrder(voucherId);
}
这段代码里面的获取代理对象有什么用

如果我们不写成 return proxy.createVoucherOrder(voucherId); ,写成 return createVoucherOrder(voucherId); 那么默认的是 this 进行调用 createVoucherOrder(voucherId)
使用 默认的 this 进行调用的话,我们拿到的是当前的 createVoucherOrder(voucherId) ,而不是代理对象
( 这里我们知道,@Transactional要想生效,其实是因为spring对当前类(VoucherOrderServiceImpl) 进行了动态代理 ,拿到了代理对象createVoucherOrder , 然后使用createVoucherOrder进行代理 )

我们使用上面的方法进行代理后,事务就可以生效了

上面那段代码的userId.toString().intern()有什么用

因为我们希望id值一样的 用的是同一把锁,每次请求的都是不同的对象,对象变了,为了保证值一样,我们使用了tostring()方法
但是实际上我们每调用一次tostring()方法,都传入了一个全新的字符串对象,这样子值还是会发生变化
为了保证值不变,我们需要加上intern()方法
intern()方法是去字符串常量池里面,找到和之前id的值一样的字符串地址,然后进行返回
这样子我们就保证了,只要值一样,不论new了多少个新字符串对象,返回的结果都是一样的

在技术的道路上,我们不断探索、不断前行,不断面对挑战、不断突破自我。科技的发展改变着世界,而我们作为技术人员,也在这个过程中书写着自己的篇章。让我们携手并进,共同努力,开创美好的未来!愿我们在科技的征途上不断奋进,创造出更加美好、更加智能的明天!

【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作),SpringBoot,spring boot,后端,java文章来源地址https://www.toymoban.com/news/detail-766179.html

到了这里,关于【SpringBoot篇】优惠券秒杀 — 添加优惠劵操作(基本操作 | 一人仅一张券的操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 微信小程序实现一些优惠券/卡券

    👏 前几周有小伙伴问我如何用css实现一些优惠券/卡券,今天就来分享一波吧!速速来Get吧~ 🥇文末分享源代码。记得点赞+关注+收藏! 2.1 实现内凹圆角 假设我们要实现这样的一个效果,两侧透明内圆角+外侧投影,有几种实现方式呢? 2.1.1 方法一:半圆伪元素(投影不准确

    2024年02月09日
    浏览(41)
  • 小程序中如何核销订单和优惠券

    小程序已成为许多商家线上线下开展业务的重要渠道。客户在小程序中下单/领券后,可能需要商家现场扫码核销,例如超市购物、卖票、游乐园等线下场景。下面就介绍小程序中如何核销订单和优惠券。 一、订单核销 订单核销是指商家在小程序中确认顾客已经支付的订单并

    2024年03月21日
    浏览(47)
  • 机器学习:基于逻辑回归对优惠券使用情况预测分析

    作者:i阿极 作者简介:Python领域新星作者、多项比赛获奖者:博主个人首页 😊😊😊如果觉得文章不错或能帮助到你学习,可以点赞👍收藏📁评论📒+关注哦!👍👍👍 📜📜📜如果有小伙伴需要数据集和学习交流,文章下方有交流学习区!一起学习进步!💪 订阅专栏案

    2024年02月02日
    浏览(44)
  • 【java爬虫】将优惠券数据存入数据库排序查询

    本文是在之前两篇文章的基础上进行写作的 (1条消息) 【java爬虫】使用selenium爬取优惠券_haohulala的博客-CSDN博客 (1条消息) 【java爬虫】使用selenium获取某宝联盟淘口令_haohulala的博客-CSDN博客  前两篇文章介绍了如何获取优惠券的基础信息,本文将获取到的基本信息存到数据库中

    2024年02月16日
    浏览(53)
  • 两天撸一个优惠券小程序,记录下开发的小小经验

    下载微信开发者工具😃😃 新建项目文件夹project,比如 D:workProjectproject 在project下创建src目录放微信小程序的源码,.gitignore文件是用来git上传gitee上忽略一些文件用的,另外三个js文件时用来混淆小程序源码的脚本,将脚本和小程序源码分开是为了脚本更好处理混淆过程。

    2023年04月08日
    浏览(55)
  • 【实践篇】教你玩转JWT认证---从一个优惠券聊起

    最近面试过程中,无意中跟候选人聊到了JWT相关的东西,也就联想到我自己关于JWT落地过的那些项目。 关于JWT,可以说是分布式系统下的一个利器,我在我的很多项目实践中,认证系统的第一选择都是JWT。它的优势会让你欲罢不能,就像你领优惠券一样。 大家回忆一下一个

    2024年02月05日
    浏览(43)
  • 智慧影院--java开源电影票优惠券制作系统快速开发

    搭建一个智慧影院可以通过使用Java开源电影票优惠券制作系统来快速开发。这个系统可以帮助影院管理电影票的销售和优惠活动,提供便捷的购票方式和优惠券的生成与使用功能。 首先,我们需要建立一个数据库来存储电影、影厅、放映计划、订单等信息。在数据库中,我

    2024年02月13日
    浏览(71)
  • 淘宝APP商品详情接口(商品信息,价格销量,优惠券信息,详情图等)

    淘宝APP商品详情接口(商品信息接口,价格销量接口,优惠券信息接口,详情图接口等)代码对接如下: 公共参数 名称 类型 必须 描述 key String 是 调用key(必须以GET方式拼接在URL中),点击获取请key和secret secret String 是 调用密钥 api_name String 是 API接口名称(包括在请求地址

    2024年02月12日
    浏览(51)
  • 业务安全情报第16期 | 大促8成优惠券竟被“羊毛党”抢走!?

    近期,某电商小程序举办美食节营销活动,提供高额折扣券,并允许用户进行秒杀。然而,羊毛党团伙利用作弊手段,抢购囤券,然后倒卖变现,严重损害了商家的利益。 根据顶象防御云编号为BSI-2023-rutq业务安全情报发现,某电商平台为吸引人气和促进销售推,推出高额折

    2024年02月07日
    浏览(49)
  • 斐讯1200M四天线双频路由器PSG1208只要88元 快来领取优惠券

    斐讯斐赛克斯专卖店针对1200M斐讯PSG1208无线路由器启动淘抢购活动,现价88元(原价98元),其拥有1200M传输速率、2.4G/5.8G双频并发、WISP中继等特性。 立刻下单 。 斐讯PSG1208使用802.11ac协议,2.4G/5.8G双频并发,数据传输速率达1167Mbps;配以联发科SOC MT7620A处理器、4天线4*4MIMO架构

    2024年02月08日
    浏览(62)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包