秒杀验证码实现

package com.java1234.miaosha.controller;

import com.java1234.miaosha.constant.Constant;
import com.java1234.miaosha.entity.Order;
import com.java1234.miaosha.entity.R;
import com.java1234.miaosha.entity.User;
import com.java1234.miaosha.service.IMiaoShaGoodsService;
import com.java1234.miaosha.service.IMiaoShaService;
import com.java1234.miaosha.service.IOrderService;
import com.java1234.miaosha.util.RedisUtil;
import com.java1234.miaosha.util.StringUtil;
import com.java1234.miaosha.vo.MiaoShaGoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

/**
 * 秒杀控制器
 */
@RestController
@RequestMapping("/miaoSha")
public class MiaoShaController {

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private IMiaoShaGoodsService miaoShaGoodsService;

    @Autowired
    private IMiaoShaService miaoShaService;

    @Autowired
    private IOrderService orderService;

    /**
     * 执行秒杀
     * @param request
     * @param miaoShaGoodsId
     * @return
     */
    @RequestMapping("/exec")
    public R exec(HttpServletRequest request, Integer miaoShaGoodsId,String verifyCode){
        if(StringUtil.isEmpty(verifyCode)){
            return R.error("验证码不能为空!");
        }
        //第一步:根据token得到用户user对象
        String token = request.getHeader("token");
        System.out.println("token:"+token);
        User user = (User)redisUtil.get(Constant.REDIS_TOKEN_PREFIX, token);
        System.out.println(user);

        Object rnd=redisUtil.get(Constant.REDIS_VERIFYCODE_PREFIX, user.getId() + "," + miaoShaGoodsId);
        if(rnd==null){
            return R.error("验证码过期,请重新点击刷新验证码!");
        }
        if(!verifyCode.equals(String.valueOf(rnd))){
            return R.error("验证码结果错误!");
        }

        //第二步:判断库存是否足够
        MiaoShaGoodsVo miaoShaGoods = miaoShaGoodsService.findById(miaoShaGoodsId);
        Integer stock = miaoShaGoods.getStock();
        if(stock<=0){
            return R.error("库存不足!");
        }

        //第三步:判断用户是否重复秒杀
        HashMap map = new HashMap();
        map.put("user_id",user.getId());
        map.put("miaosha_goods_id",miaoShaGoodsId);
        Order orderE = orderService.findByUserIdAndMiaoShaGoodsId(map);
        if(orderE!=null){
            return R.error("您已经秒杀过此商品,不能重复秒杀");
        }

        //第四步:减库存,下订单,必须同一个事务
        String orderId = miaoShaService.miaoSha(user, miaoShaGoods);
        if(orderId!=null){
            Map<String,Object> resultMap=new HashMap<>();
            resultMap.put("orderId",orderId);
            return R.ok(resultMap);
        }else{
            return R.error("系统异常,请稍后重试!");
        }
    }
}

package com.java1234.miaosha.controller;


import com.java1234.miaosha.constant.Constant;
import com.java1234.miaosha.entity.R;
import com.java1234.miaosha.entity.User;
import com.java1234.miaosha.util.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import javax.imageio.ImageIO;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;


/**
 * 订单控制器
 * @author java1234_小锋
 * @site www.java1234.com
 * @company Java知识分享网
 * @create 2020-12-13 11:20
 */
@Controller
@RequestMapping("/verifyCode")
public class VerifyCodeController {



    @Autowired
    private RedisUtil redisUtil;


    /**
     * 返回验证码图片
     * @param miaoShaGoodsId
     * @return
     */
    @RequestMapping(value = "/get",method = RequestMethod.GET)
    public R get(HttpServletResponse response,String token, Integer miaoShaGoodsId){
        System.out.println("token:"+token);
        System.out.println("miaoShaGoodsId:"+miaoShaGoodsId);
    /*    String token = request.getParameter("token");
        System.out.println("token:"+token);*/
        Object o=redisUtil.get(Constant.REDIS_TOKEN_PREFIX,token);
        if(o==null){
            return null;
        }
        if(miaoShaGoodsId==null || miaoShaGoodsId <=0){
            return null;
        }
        try {
            BufferedImage image = this.createVerifyCodeImage(((User) o).getId(), miaoShaGoodsId);
            ServletOutputStream out = response.getOutputStream();
            ImageIO.write(image,"JPEG",out);
            out.flush();
            out.close();
            return null;
        }catch(Exception e){
            e.printStackTrace();
            return R.error("服务端异常");
        }
    }

    private BufferedImage createVerifyCodeImage(Integer userId,Integer miaoShaGoodsId) {
        int width=80;
        int height=32;
        BufferedImage image=new BufferedImage(width,height,BufferedImage.TYPE_INT_RGB);
        Graphics g=image.getGraphics();
        g.setColor(new Color(0xDCDCDC));
        g.fillRect(0,0,width,height);
        g.setColor(Color.black);
        g.drawRect(0,0,width-1,height-1);
        Random rdm=new Random();
        for(int i=0;i<50;i++){
            int x=rdm.nextInt(width);
            int y=rdm.nextInt(height);
            g.drawOval(x,y,0,0);
        }

        String verifyCode=createVerifyCode();
        System.out.println("验证:"+verifyCode);
        g.setColor(new Color(0,100,0));
        g.setFont(new Font("Candara",Font.BOLD,24));
        g.drawString(verifyCode+"=",8,24);
        g.dispose();

        int rnd=calc(verifyCode);
        System.out.println("rnd:"+rnd);
        redisUtil.set(Constant.REDIS_VERIFYCODE_PREFIX,userId+","+miaoShaGoodsId,rnd,Constant.REDIS_VERIFYCODE_EXPIRE);

        return image;

    }

    public static void main(String[] args) {
        String verifyCode=createVerifyCode();
        System.out.println(calc(verifyCode));
    }

    /**
     * 计算表达式
     * @param exp
     * @return
     */
    private static int calc(String exp) {
        ScriptEngineManager manager=new ScriptEngineManager();
        ScriptEngine engine = manager.getEngineByName("JavaScript");
        try {
            return (int) engine.eval(exp);
        } catch (ScriptException e) {
            e.printStackTrace();
            return 0;
        }
    }


    private static char[] ops=new char[]{'+','-'};

    /**
     * + - 运算
     * @return
     */
    private static String createVerifyCode() {
        Random rdm=new Random();
        int num1=rdm.nextInt(10);
        int num2=rdm.nextInt(10);
        char op1=ops[rdm.nextInt(2)];
        System.out.println("op1:"+op1);
        String exp=num1+String.valueOf(op1)+num2;
        System.out.println("exp:"+exp);
        return exp;
    }


}

<template>
  <div>
    <el-container>
      <el-header height="120px">
        <miao-sha-header></miao-sha-header>
      </el-header>
      <el-main>
        <p class="goods_head">秒杀商品详情</p>
        <el-form label-position="left" label-width="120px">
            <el-form-item label="商品名称:">
                {{this.miaoShaGoods.goods.name}}
            </el-form-item>
          <el-form-item label="商品图片:">
                <img :src="getSrcUrl(this.miaoShaGoods.goods.image)"/>
          </el-form-item>
          <el-form-item label="商品原价:">
                {{this.miaoShaGoods.goods.price+'元'}}
          </el-form-item>
          <el-form-item label="秒杀价:">
                {{this.miaoShaGoods.price+'元'}}
          </el-form-item>
          <el-form-item label="库存数量:">
            {{this.miaoShaGoods.stock}}
          </el-form-item>
          <el-form-item label="秒杀开始时间:">
            {{this.miaoShaGoods.startTime}}
            <span v-show="miaoShaGoods.miaoShaStatus==0">秒杀倒计时:{{miaoShaGoods.remainBeginSecond}}</span>
            <span v-show="miaoShaGoods.miaoShaStatus==1">秒杀进行中</span>
            <span v-show="miaoShaGoods.miaoShaStatus==2">秒杀结束</span>
          </el-form-item>
          <el-form-item label="秒杀结束时间:">
            {{this.miaoShaGoods.endTime}}
          </el-form-item>
        </el-form>
        <img id="verifyCodeImg" width="80" height="32" :src="verifyCodeImgSrc" v-show="miaoShaGoods.miaoShaStatus==1" @click="refreshVerifyCode">
        <input id="verifyCode" type="text" style="width: 60px;height: 23px;padding: 4px;border: 1px solid gray;" v-model="verifyCode" v-show="miaoShaGoods.miaoShaStatus==1"/>
        <el-button v-show="miaoShaGoods.miaoShaStatus==1" type="primary" size="small" @click="exec_miaosha">立即秒杀</el-button>
      </el-main>
      <el-footer>
        <miao-sha-footer></miao-sha-footer>
      </el-footer>
    </el-container>
  </div>
</template>

<script>

import MiaoShaHeader from './common/Header';
import MiaoShaFooter from './common/Footer';
import axios from 'axios';
import {getServerUrl} from '@/config/sys';

export default {
  name: 'Detail',
  data() {
    return {
      miaoShaGoods:{
         goods:{
           name:'',
           image:'default.jpg',
           price:0
         }
      },
      verifyCodeImgSrc:"",
      verifyCode:""
    }
  },
  components: {
    MiaoShaHeader,
    MiaoShaFooter
  },
  methods: {
    getSrcUrl(t) {
      return getServerUrl('image/' + t);
    },
    exec_miaosha(){
      if(this.verifyCode==""){
        alert("请输入验证码结果!");
        return;
      }
      let url = getServerUrl("miaoSha/exec");
      let token = window.sessionStorage.getItem("token");
      axios.defaults.headers.common['token'] = token;
      axios.get(url,{
        params:{
          miaoShaGoodsId:this.$route.params.id,
          verifyCode:this.verifyCode
        }
      }).then(response=>{
         let data = response.data;
         if(data.code!=0){
           alert(data.msg);
         }else{
           alert(data.orderId);
         }
      }).catch(error=>{
        alert(error+"-请联系管理员");
      })
    },
    getInfo(){
      let url = getServerUrl("miaoShaGoods/detail");
      let token = window.sessionStorage.getItem("token");
      axios.defaults.headers.common['token'] = token;
      axios.get(url,{
        params:{
          id:this.$route.params.id
        }
      }).then(response=>{
          console.log(response.data.data);
          this.miaoShaGoods = response.data.data;
          this.countDown();
          if(this.miaoShaGoods.remainEndSecond>0){//秒杀还没结束
            setTimeout(()=>{
                this.miaoShaGoods.miaoShaStatus=2;
            },this.miaoShaGoods.remainEndSecond*1000)
          }
        }).catch(error=>{
        alert(error+"-请联系管理员");
      })
    },
    countDown(){
      let timeout;
      let rs=this.miaoShaGoods.remainBeginSecond;
      if(rs>0){   //秒杀还没开始,倒计时
         timeout=setTimeout(()=>{
           this.miaoShaGoods.remainBeginSecond=this.miaoShaGoods.remainBeginSecond-1;
           this.countDown();
         },1000);
      }else if(rs==0){  //秒杀进行中
        this.miaoShaGoods.miaoShaStatus=1;
        let url = getServerUrl("verifyCode/get");
        this.verifyCodeImgSrc=url+"?miaoShaGoodsId="+this.$route.params.id+"&token="+window.sessionStorage.getItem("token");
        if(timeout){
          clearTimeout(timeout);
        }
      }else{  //秒杀结束
         this.miaoShaGoods.miaoShaStatus=2;
      }
    },
    refreshVerifyCode(){
      let url = getServerUrl("verifyCode/get");
      this.verifyCodeImgSrc=url+"?miaoShaGoodsId="+this.$route.params.id+"&token="+window.sessionStorage.getItem("token")+"&timestamp="+new Date().getTime();
    }
  },
  mounted() {
    this.getInfo();
  }
}
</script>

<style scoped>

.goods_head{
  font-weight: bold;
  font-size: 20px;
}
</style>

@charset "utf-8";
.border,
.border-top,
.border-right,
.border-bottom,
.border-left,
.border-topbottom,
.border-rightleft,
.border-topleft,
.border-rightbottom,
.border-topright,
.border-bottomleft {
    position: relative;
}
.border::before,
.border-top::before,
.border-right::before,
.border-bottom::before,
.border-left::before,
.border-topbottom::before,
.border-topbottom::after,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::before,
.border-topleft::after,
.border-rightbottom::before,
.border-rightbottom::after,
.border-topright::before,
.border-topright::after,
.border-bottomleft::before,
.border-bottomleft::after {
    content: "\0020";
    overflow: hidden;
    position: absolute;
}
/* border
 * 因,边框是由伪元素区域遮盖在父级
 * 故,子级若有交互,需要对子级设置
 * 定位 及 z轴
 */
.border::before {
    box-sizing: border-box;
    top: 0;
    left: 0;
    height: 100%;
    width: 100%;
    border: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-bottom::before,
.border-topbottom::before,
.border-topbottom::after,
.border-topleft::before,
.border-rightbottom::after,
.border-topright::before,
.border-bottomleft::before {
    left: 0;
    width: 100%;
    height: 1px;
}
.border-right::before,
.border-left::before,
.border-rightleft::before,
.border-rightleft::after,
.border-topleft::after,
.border-rightbottom::before,
.border-topright::after,
.border-bottomleft::after {
    top: 0;
    width: 1px;
    height: 100%;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    border-top: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-right::before,
.border-rightbottom::before,
.border-rightleft::before,
.border-topright::after {
    border-right: 1px solid #eaeaea;
    transform-origin: 100% 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::before {
    border-bottom: 1px solid #eaeaea;
    transform-origin: 0 100%;
}
.border-left::before,
.border-topleft::after,
.border-rightleft::after,
.border-bottomleft::after {
    border-left: 1px solid #eaeaea;
    transform-origin: 0 0;
}
.border-top::before,
.border-topbottom::before,
.border-topleft::before,
.border-topright::before {
    top: 0;
}
.border-right::before,
.border-rightleft::after,
.border-rightbottom::before,
.border-topright::after {
    right: 0;
}
.border-bottom::before,
.border-topbottom::after,
.border-rightbottom::after,
.border-bottomleft::after {
    bottom: 0;
}
.border-left::before,
.border-rightleft::before,
.border-topleft::after,
.border-bottomleft::before {
    left: 0;
}
@media (max--moz-device-pixel-ratio: 1.49), (-webkit-max-device-pixel-ratio: 1.49), (max-device-pixel-ratio: 1.49), (max-resolution: 143dpi), (max-resolution: 1.49dppx) {
    /* 默认值,无需重置 */
}
@media (min--moz-device-pixel-ratio: 1.5) and (max--moz-device-pixel-ratio: 2.49), (-webkit-min-device-pixel-ratio: 1.5) and (-webkit-max-device-pixel-ratio: 2.49), (min-device-pixel-ratio: 1.5) and (max-device-pixel-ratio: 2.49), (min-resolution: 144dpi) and (max-resolution: 239dpi), (min-resolution: 1.5dppx) and (max-resolution: 2.49dppx) {
    .border::before {
        width: 200%;
        height: 200%;
        transform: scale(.5);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.5);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.5);
    }
}
@media (min--moz-device-pixel-ratio: 2.5), (-webkit-min-device-pixel-ratio: 2.5), (min-device-pixel-ratio: 2.5), (min-resolution: 240dpi), (min-resolution: 2.5dppx) {
    .border::before {
        width: 300%;
        height: 300%;
        transform: scale(.33333);
    }
    .border-top::before,
    .border-bottom::before,
    .border-topbottom::before,
    .border-topbottom::after,
    .border-topleft::before,
    .border-rightbottom::after,
    .border-topright::before,
    .border-bottomleft::before {
        transform: scaleY(.33333);
    }
    .border-right::before,
    .border-left::before,
    .border-rightleft::before,
    .border-rightleft::after,
    .border-topleft::after,
    .border-rightbottom::before,
    .border-topright::after,
    .border-bottomleft::after {
        transform: scaleX(.33333);
    }
}
@charset "utf-8";html{background-color:#fff;color:#000;font-size:12px}
body,ul,ol,dl,dd,h1,h2,h3,h4,h5,h6,figure,form,fieldset,legend,input,textarea,button,p,blockquote,th,td,pre,xmp{margin:0;padding:0}
body,input,textarea,button,select,pre,xmp,tt,code,kbd,samp{line-height:1.5;font-family:tahoma,arial,"Hiragino Sans GB",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,small,big,input,textarea,button,select{font-size:100%}
h1,h2,h3,h4,h5,h6{font-family:tahoma,arial,"Hiragino Sans GB","微软雅黑",simsun,sans-serif}
h1,h2,h3,h4,h5,h6,b,strong{font-weight:normal}
address,cite,dfn,em,i,optgroup,var{font-style:normal}
table{border-collapse:collapse;border-spacing:0;text-align:left}
caption,th{text-align:inherit}
ul,ol,menu{list-style:none}
fieldset,img{border:0}
img,object,input,textarea,button,select{vertical-align:middle}
article,aside,footer,header,section,nav,figure,figcaption,hgroup,details,menu{display:block}
audio,canvas,video{display:inline-block;*display:inline;*zoom:1}
blockquote:before,blockquote:after,q:before,q:after{content:"\0020"}
textarea{overflow:auto;resize:vertical}
input,textarea,button,select,a{outline:0 none;border: none;}
button::-moz-focus-inner,input::-moz-focus-inner{padding:0;border:0}
mark{background-color:transparent}
a,ins,s,u,del{text-decoration:none}
sup,sub{vertical-align:baseline}
html {overflow-x: hidden;height: 100%;font-size: 50px;-webkit-tap-highlight-color: transparent;}
body {font-family: Arial, "Microsoft Yahei", "Helvetica Neue", Helvetica, sans-serif;color: #333;font-size: .28em;line-height: 1;-webkit-text-size-adjust: none;}
hr {height: .02rem;margin: .1rem 0;border: medium none;border-top: .02rem solid #cacaca;}
a {color: #25a4bb;text-decoration: none;}

  • 9
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值