Bootstrap

常见的抽奖逻辑

抽奖逻辑

这段时间大概做了很多这种类似抽奖的活动!苦逼的我本来是搞前端的!但是由于一些原因换了家公司后!现在前后端都得搞!所以就写一些逻辑性很多,很多的东西

不多扯了 说重点吧!

抽奖逻辑这块大概是这样的。每次抽奖都会从奖品表抽取一个送给用户,这里主要需要考虑到库存不足,和并发的可能。什么是并发呢?就是比如甲,乙两个人同时抽奖,但是商品只剩一个了,但是两个都中奖了,那么我该怎么发放奖品呢?

至于处理这个并发呢?我是通过sql实现的

UPDATE price_table  set outnumber = outnumber + 1 , gmt_modified = "${curentTime()}" 

WHERE id=${price_id} AND sumnumber > outnumber`

主要就是通过这句sql看它到底执行还是没执行,如果执行了说明那个人真的抽到奖了,如果没有执行就没有抽到奖!所以需要这句sql判断一下他到底真正的抽到奖了!这里最主要的就是后面where条件限制 让他sumnumer>outnumber 奖品总数大于你出奖数量 一旦出奖数量和这个总数相等那么意思库存不足,这句sql就不会执行,这时候在让用户不中奖!

具体抽奖核心代码

这个抽奖的话

通过抽奖表

[{ "sumnumber": 100, "outnumber": 13, "probability": "0.3", "name": "爱奇艺会员180天", "id": 1, "create_date": "2020-07-24T08:29:33.000Z", "update_date": "2020-07-24T11:21:01.000Z", "entity": 0 },
    { "sumnumber": 10, "outnumber": 2, "probability": "0.3", "name": "海贼王手办一套", "id": 2, "create_date": "2020-07-24T08:31:19.000Z", "update_date": "2020-07-24T08:31:19.000Z", "entity": 1 },
    { "sumnumber": 10, "outnumber": 2, "probability": "0.2", "name": "腾讯Q币20元", "id": 3, "create_date": "2020-07-24T08:33:01.000Z", "update_date": "2020-07-24T08:33:01.000Z", "entity": 0 }]

这是奖品数据 这个probability字段是中这个奖品的概率这个是你设置,你想设置多大就多大

以下是核心代码为了方便大家阅读我把他从项目中单独摘了出来

    let pricelist = [{ "sumnumber": 100, "outnumber": 13, "probability": "0.3", "name": "爱奇艺会员180天", "id": 1, "create_date": "2020-07-24T08:29:33.000Z", "update_date": "2020-07-24T11:21:01.000Z", "entity": 0 },
    { "sumnumber": 10, "outnumber": 2, "probability": "0.3", "name": "海贼王手办一套", "id": 2, "create_date": "2020-07-24T08:31:19.000Z", "update_date": "2020-07-24T08:31:19.000Z", "entity": 1 },
    { "sumnumber": 10, "outnumber": 2, "probability": "0.2", "name": "腾讯Q币20元", "id": 3, "create_date": "2020-07-24T08:33:01.000Z", "update_date": "2020-07-24T08:33:01.000Z", "entity": 0 }]
// 0.3
// 0.3
// 0.2
    let random = Math.random();
    let price_id = 0;
    let min = 0;
    console.log(random);

    for (let i = 0; i < pricelist.length; i++) {
        if (random > min && random < (min + Number(pricelist[i].probability))) {
            console.log('我是上限',min + Number(pricelist[i].probability));
            price_id = pricelist[i].id;
            break;
        } else {
            min = min + Number(pricelist[i].probability)
        }
    }

    console.log(price_id);

这是具体详细项目代码 我想很多人 看不懂 因为业务不同嘛 我这里做的是淘宝小程序的云函数开发写的一个后端抽奖接口

'use strict';

/**
 * @name 抽奖活动后端接口
 * @version 1.0.0
 * @author yangjie
 * @Time 2020年07月24日14:55:51
 */

const DateFormat = require('dateformat-util')

exports.main = async (ctx) => {
    const { mixNick, cloud, userNick } = ctx;
    const retObj = {};

    // 判断用户是否存在
    let user_count = await cloud.dataspace.executeSql(`SELECT * FROM user_table WHERE mixnick = "${mixNick}"`);

     //拿到用户id
     let user_id = user_count[0].id;

    //如果不存在则插入到用户表(由于时区原因插入的时候需要插入东八区时间)
    if (user_count.length == 0) {
        await cloud.dataspace.executeSql(`INSERT INTO user_table (mixnick,gmt_create,gmt_modified) VALUES ('${mixNick}','${DateFormat.format(new Date(), "yyyy-MM-dd hh:mm:ss")}','${DateFormat.format(new Date(), "yyyy-MM-dd hh:mm:ss")}')`);
    }

    //如果铭文昵称存在存入铭文昵称
    if (userNick) {
        await cloud.dataspace.executeSql(`UPDATE user_table SET nickname = "" WHERE user_id = ?`, [user_id]);
    }

    // // //通过用户id判断用户每天抽奖次数

    let pricenumber = await cloud.dataspace.executeSql(`SELECT COUNT(1) AS number FROM luckdraw_table WHERE user_id = ${user_id} AND 'gmt_create' >= "${DateFormat.format(DateFormat.getDateStart(new Date()), "yyyy-MM-dd hh:mm:ss")}"`);


    // retObj.pricenumber = pricenumber;

    // //如果用户抽奖次数小于3的话可以继续抽奖

    if (pricenumber[0].number < 3) {

        //插入用户id到抽奖表
        let priceid = await cloud.dataspace.executeSql(`INSERT INTO luckdraw_table (user_id,gmt_create,gmt_modified) 
         VALUES(${user_id},'${DateFormat.format(new Date(), "yyyy-MM-dd hh:mm:ss")}','${DateFormat.format(new Date(), "yyyy-MM-dd hh:mm:ss")}')`);

        retObj.priceid = priceid;

        //     //拿到本次抽奖的id,后面做更新
        let pricenumber = priceid.insertId

        //     // retObj.pricenumber = pricenumber;

        //查询是否中过实物奖
        let pricestage = await cloud.dataspace.executeSql(`SELECT COUNT(1) as number FROM luckdraw_table AS g LEFT JOIN price_table AS u ON g.Price_id=u.id WHERE u.entity=1 and user_id = ?`, [user_id]);

        // retObj.pricestage = pricestage;

        let select_user_winprice_sql = "SELECT * FROM price_table WHERE sumnumber>outnumber AND probability > 0 ";


        if (pricestage[0].number > 0) {
            select_user_winprice_sql += "AND entity=0 ";
        }


        // 开始抽奖

        //确定奖池

        let pricelist = await cloud.dataspace.executeSql(select_user_winprice_sql);


        let random = Math.random();
        let price_id = 0;
        let min = 0;
        retObj.random = random
        // retObj.pricelist = pricelist;
        // retObj.pricelisLength = pricelist.length;
        // retObj.pricelis1 = pricelist[0].probability

        for (let i = 0; i < pricelist.length; i++) {
            if (random >= min && random < (min + Number(pricelist[i].probability))) {
                price_id = pricelist[i].id;
                break;
            } else {
                min = min + Number(pricelist[i].probability);
            }
        }

        // retObj.price_id = price_id


        if (price_id) {
            //验证是否真正抽到奖了(更新出奖数量)
            let update_price_outnumber_sql = await cloud.dataspace.executeSql(`UPDATE price_table  set outnumber = outnumber + 1 , gmt_modified = "${curentTime()}" WHERE id=${price_id} AND sumnumber > outnumber`);

            // retObj.op = update_price_outnumber_sql.affectedRows

            //如果更新了为1即取反为false  如果没有更新为0 则取反为true
            if (!update_price_outnumber_sql.affectedRows) {
                price_id = 0
            }


        }

        //重新把抽奖表赋值
        await cloud.dataspace.executeSql(`UPDATE luckdraw_table SET price_id = ${price_id} , gmt_modified = "${curentTime()}"  WHERE id = ${pricenumber} `);

        //获取抽奖详情
        if (price_id) {
            let select_price_sql = await cloud.dataspace.executeSql(`SELECT * FROM price_table WHERE id =${price_id}`);

            retObj.ispricedata = select_price_sql
        }

        retObj.price_id = price_id


    } else {

        return await basiCode(7000, retObj);

    }
    return await basiCode(0, retObj);

};

// 返回值处理
var basiCode = async (code, data) => {
    let reobj = {
        errCode: 0,
        msg: 'ok',
        data: data
    };
    if (code) {
        reobj.data = data;
        reobj.errCode = code;
        switch (code) {
            case 7000:
                reobj.msg = '抽奖次数不足!';
                break;
            case 8000:
                reobj.data = String(data);
                reobj.msg = 'System error!';
                break;
            default:
                reobj.msg = 'other error!';
                break;
        }
    } else {
        reobj.data = data;
    }
    try {
        pool.releaseConnection(mysqlConn);
    } catch (e) {
        console.log(e);
    }

    return reobj;
};

// 获取东八区时间
var curentTime = function (time) {
    var d = time ? new Date(time) : new Date();
    var localTime = d.getTime();
    var localOffset = d.getTimezoneOffset() * 60000; //获得当地时间偏移的毫秒数
    var utc = localTime + localOffset + 3600000 * 8;
    var now = new Date(utc);

    var year = now.getFullYear(); //年
    var month = now.getMonth() + 1; //月
    var day = now.getDate(); //日
    var hh = now.getHours(); //时
    var mm = now.getMinutes(); //分
    var ss = now.getSeconds(); //秒

    var clock = year + "-";

    if (month < 10)
        clock += "0";

    clock += month + "-";

    if (day < 10)
        clock += "0";

    clock += day + " ";

    if (hh < 10)
        clock += "0";

    clock += hh + ":";

    if (mm < 10)
        clock += "0";

    clock += mm + ":";

    if (ss < 10)
        clock += "0";

    clock += ss;

    return (clock);
}

;