服务端模块:周期性次数重置

2021-06-07 15:47:11 浏览数 (2)

在游戏开发中经常会有每天限制次数的需求,这样的功能几乎在每个功能都可能出现,这种功能每个模块自己写又是重复的,因此需要统一处理。

比如:每日抽奖的免费次数,比如每天可打的副本次数等等功能。

今天这篇写一下这个每日次数限制的功能的实现。

1、设计需求

需要任何模块都可以调用,在查询的时候可以自动重置,并且自动入库,并且支持周,月等周期。

2、数据库设计

n_roleId : 玩家id,作为主键

n_reset_type : 枚举,不同的重置类型定义不同的id,作为主键

n_count :当前周期已经使用的数量,每次重置为0

s_extend :扩展字段,每种类型可以存一些自定义的数据到里面

d_update : 更新时间

d_create : 数据创建的时间

3、代码实现

3.1 枚举类型定义
代码语言:javascript复制
package org.pdool.dayLimit;

/**
 * 重置次数限制类型
 */
public enum ResetLimitType {
    /**
     * 每日抽奖次数
     */
    LOTTERY_COUNT(1),
    /**
     *  每周收礼次数
     */
    RECEIVE_GIFT_COUNT(2,2),
    ;
    /**
     * 限制类型
     */
    private int limitType;
    /**
     * 重置类型 1 每天 2 每周 3 每月
     */
    private int resetType = 1;

    ResetLimitType(int limitType) {
        this.limitType = limitType;
    }

    ResetLimitType(int limitType, int resetType) {
        this.limitType = limitType;
        this.resetType = resetType;
    }

    public int getLimitType() {
        return limitType;
    }

    public int getResetType() {
        return resetType;
    }

    public static ResetLimitType valueOf(int limitType) {
        for (ResetLimitType resetLimitType : values()) {
            if (limitType == resetLimitType.getLimitType()) {
                return resetLimitType;
            }
        }
        return null;
    }
}

说明:枚举定义了2个构造函数。

默认的构造函数重置类型是1,也就是每日重置。

2个参数的构造函数,可以自定义重置类型,重置类型 1 每天 2 每周 3 每月

3.2 日期函数util
代码语言:javascript复制
package org.pdool.dayLimit;

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

public class DateUtil {
    /**
     * 判断两个日期是否同一天
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static boolean isSameDay(Date beginDate, Date endDate) {
        return getDiffDays(beginDate, endDate) == 0;
    }
    /**
     * 判断两个日期是否同一周
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static boolean isSameWeek(Date beginDate, Date endDate) {
        Calendar begin = Calendar.getInstance();
        begin.setTime(beginDate);
        Calendar end = Calendar.getInstance();
        end.setTime(endDate);

        //换算beginDate的周一时间
        int beginDayOfWeek = begin.get(Calendar.DAY_OF_WEEK);
        if (beginDayOfWeek == 1) {
            begin.add(Calendar.DAY_OF_YEAR, -6);
        } else if (beginDayOfWeek > 2) {
            begin.add(Calendar.DAY_OF_YEAR, 2 - beginDayOfWeek);
        }

        //换算endDate的周一时间
        int endDayOfWeek = end.get(Calendar.DAY_OF_WEEK);
        if (endDayOfWeek == 1) {
            end.add(Calendar.DAY_OF_YEAR, -6);
        } else if (endDayOfWeek > 2) {
            end.add(Calendar.DAY_OF_YEAR, 2 - endDayOfWeek);
        }
        return ((end.get(Calendar.YEAR) == begin.get(Calendar.YEAR)) && (end.get(Calendar.DAY_OF_YEAR) == begin.get(Calendar.DAY_OF_YEAR)));
    }

    /**
     * 两个时间相差天数
     *
     * @param beginDate
     * @param endDate
     * @return
     */
    public static int getDiffDays(Date beginDate, Date endDate) {
        Calendar begin = Calendar.getInstance();
        begin.setTime(beginDate);
        begin.set(Calendar.HOUR_OF_DAY, 0);
        begin.set(Calendar.MINUTE, 0);
        begin.set(Calendar.SECOND, 0);
        begin.set(Calendar.MILLISECOND, 0);
        Calendar end = Calendar.getInstance();
        end.setTime(endDate);
        end.set(Calendar.HOUR_OF_DAY, 0);
        end.set(Calendar.MINUTE, 0);
        end.set(Calendar.SECOND, 0);
        end.set(Calendar.MILLISECOND, 0);
        return (int) ((end.getTimeInMillis() - begin.getTimeInMillis()) / (24 * 60 * 60 *1000));
    }


    /**
     * 判断2个时间
     *@param date
     *@param date2
     *@param state  1:比较是否是同一个月,2:比较是否是同一天,3:比较是否是同一年
     *@return boolean
     */
    public static boolean isSameMonth(Date date, Date date2)
    {
        Calendar begin = Calendar.getInstance();
        begin.setTime(date);
        Calendar end = Calendar.getInstance();
        end.setTime(date2);
        return (end.get(Calendar.YEAR) == begin.get(Calendar.YEAR)) && (end.get(Calendar.MONTH) == begin.get(Calendar.MONTH));
    }
}

说明:实现了对天,月 和 周的时间比较,可以判断两个时间是否在同一天,同一周,同一个月。

3.3 重置对象的定义
代码语言:javascript复制
package org.pdool.dayLimit;

import java.util.Date;

/**
 * 重置次数
 */
public class ResetCountVO {
    //  角色id
    private int roleId;
    //  重置的类型
    private ResetLimitType resetType;
    //已经消耗次数
    private int num;
    //最后更新时间
    private Date updateTime;
    //扩展信息
    private String extend;

    public int getRoleId() {
        return roleId;
    }

    public void setRoleId(int roleId) {
        this.roleId = roleId;
    }

    public int getNum() {
        return num;
    }

    public void setNum(int num) {
        this.num = num;
    }

    public Date getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }

    public String getExtend() {
        return extend;
    }

    public void setExtend(String extend) {
        this.extend = extend;
    }

    public ResetLimitType getResetType() {
        return resetType;
    }

    public void setResetType(ResetLimitType resetType) {
        this.resetType = resetType;
    }

    /**
     * 获得次数
     *
     * @return
     */
    public int getCount() {
        int resetType = this.resetType.getResetType();
        Date now = new Date();
        if (resetType == 1) {
            //  每日重置
            if (DateUtil.isSameDay(now, updateTime)) {
                return this.num;
            }
        } else if (resetType == 2) {
            //  每周重置
            if (DateUtil.isSameWeek(now, updateTime)) {
                return this.num;
            }
        } else if (resetType == 3) {
            //  每月重置
            if (DateUtil.isSameMonth(now, updateTime)) {
                return this.num;
            }
        }
        this.num = 0;
        updateTime = now;
        return 0;
    }

    /**
     * 增加次数
     *
     * @param addCountArg
     * @return
     */
    public int addCount(int... addCountArg) {
        int count = getCount();
        int addCount = 1;
        if (addCountArg.length != 0) {
            addCount = addCountArg[0];
        }
        count  = addCount;
        num = count;
        updateTime = new Date();
        return count;
    }


}

说明:重置对象自带的方法比如查和增加次数。

addCount 函数,在不传参数的时候,默认增加次数 1,在当前的基础上增加1 次

传递参数的时候,则使用传递的参数,比如 addCount(50),则在当前的基础上增加50次。

3.4 使用方法
代码语言:javascript复制
package org.pdool.dayLimit;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;

public class Aain {
    public static void main(String[] args) {
        Map<ResetLimitType,ResetCountVO> playerResetMap = new HashMap<>();
        ResetCountVO resetCountVO1 = new ResetCountVO();
        resetCountVO1.setResetType(ResetLimitType.LOTTERY_COUNT);
        resetCountVO1.setUpdateTime(new Date(1621870616000L));
        resetCountVO1.setNum(1);
        playerResetMap.put(ResetLimitType.LOTTERY_COUNT,resetCountVO1 );

        ResetCountVO resetCountVO2 = new ResetCountVO();
        resetCountVO2.setResetType(ResetLimitType.RECEIVE_GIFT_COUNT);
        resetCountVO2.setUpdateTime(new Date(1621870616000L));
        resetCountVO2.setNum(1000);
        playerResetMap.put(ResetLimitType.RECEIVE_GIFT_COUNT,resetCountVO2 );

        int count = resetCountVO1.getCount();
        System.out.println(count);

        int count2 = resetCountVO2.getCount();
        System.out.println(count2);
    }
}
3.5 注意事项

1、没有实现数据的入库操作,这个在使用代码的时候,需要根据自己的项目进行更改。

2、没有封装在枚举类型对应的对象不存在时自动创建操作,可以根据自己的项目代码规则自己实现

在上面的我定义了一个map ,可以使用 下面的代码进行封装实现

代码语言:javascript复制
 ResetCountVO resetCountVO = playerResetMap.computeIfAbsent(ResetLimitType.LOTTERY_COUNT, k -> new ResetCountVO());
        if (resetCountVO.getUpdateTime() == null){
            resetCountVO.setResetType(ResetLimitType.LOTTERY_COUNT);
            resetCountVO.setUpdateTime(new Date(1621870616000L));
            resetCountVO.setNum(1);
        }

4、总结

周期性的限制次数的思想很简单,就是任何需要访问数量的地方进行次数重置检测。

我们要做的就是怎么实现的的简单方便,将逻辑进行封装的更顺手。

好了,今天就写到这吧,睡觉。

0 人点赞