在游戏开发中经常会有每天限制次数的需求,这样的功能几乎在每个功能都可能出现,这种功能每个模块自己写又是重复的,因此需要统一处理。
比如:每日抽奖的免费次数,比如每天可打的副本次数等等功能。
今天这篇写一下这个每日次数限制的功能的实现。
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、总结
周期性的限制次数的思想很简单,就是任何需要访问数量的地方进行次数重置检测。
我们要做的就是怎么实现的的简单方便,将逻辑进行封装的更顺手。
好了,今天就写到这吧,睡觉。