简介
作为一款游戏要拉动用户日活必须要有一点的奖励或者是付费,这就要设计到福利系统的,包括每日登录,日卡,月卡等等的。而我们要面对这么多的福利功能的时候,一个设计得当的福利系统框架会减少我们很多的工作量,而且系统稳定,后续维护少等优点。
哪问题来了,该如何设计了?
设计的思路
要设计一个系统或一个框架的时候,我们第一时间需要搬出来的就是设计模式。对设计模式六大原则中的两个原则进项
- 里氏替换原则(LSP liskov substitution principle):子类可以扩展父类的功能,但不能改变原有父类的功能(目的:增强程序的健壮性)实际项目中,每个子类对应不同的业务含义,使父类作为参数,传递不同的子类完成不同的业务逻辑。
- 开闭原则(open closed principle):用抽象构建架构,用实现扩展原则
基于上面的原则,那么我们就需要对类和类之间进行处理了。
设计流程图
- 使用枚举的方式来管理和初始化要福利管理类
- 在福利管理类中通过泛型的方式转换为要处理的具体的福利对象
具体的实战代码
/**
* 个人福利数据
* */
@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "name")
public class OtherWelfareRoleData{
/**
* 福利活动配置id
* */
private int otherWelfareId;
public int getOtherWelfareId() {
return otherWelfareId;
}
public void setOtherWelfareId(int otherWelfareId) {
this.otherWelfareId = otherWelfareId;
}
}
如下是具体的数据库中的福利数据
/**
* 事件监听者接口
*/
public interface EventListenerIF {
/**
* 初始化监听的事件类型
*/
void initStruct();
/**
* 增加事件监听
*/
default void addEventListener(EventType eventType) {
ListenerMgrHolder.getInstance().addEventListener(eventType, this);
}
/**
* 事件监听
*/
void onListener(EventType<?> eventType, EventParam eventParam);
}
package com.server.game.scene.otherWelfare;
import com.server.game.listener.EventType;
import com.server.game.listener.event.EventListenerIF;
import com.server.game.listener.event.EventParam;
import com.server.game.scene.otherWelfare.mgr.OtherWelfareManager;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
/**
* 其他福利服务基类
*/
public abstract class OtherWelfareService implements EventListenerIF {
public static final Logger log = LogManager.getLogger(OtherWelfareService.class);
protected OtherWelfareEnum otherWelfareEnum;
@Override
public void onListener(EventType<?> eventType, EventParam eventParam) {
}
/**
* 初始化监听器那些
*/
public void initStruct() {
}
/**
* 点击页签执行
*/
public void onClick(long roleId) {
}
/**
* 登陆初始数据执行
*/
public void onLoginInitData(long roleId) {
}
/**
* 登陆监听执行
*/
public void onLogin(long roleId) {
}
/**
* 当功能开启
*/
public void onFuncOpen(long roleId) {
}
/**
* 登录是否下发
*/
public boolean loginPush(long roleId) {
return true;
}
/**
* 取出玩家数据
*/
@SuppressWarnings("unchecked")
protected <R extends OtherWelfareRoleData> R getRoleData(long roleId) {
HashMap<Integer, OtherWelfareRoleData> roleDataHashMap = OtherWelfareManager.getInstance().getById(roleId).getData();
R roleData = (R) roleDataHashMap.get(otherWelfareEnum.getType());
if (roleData == null) {
roleData = createRoleData(roleId);
if (roleDataHashMap.putIfAbsent(otherWelfareEnum.getType(), roleData) == null) {
return roleData;
}
return (R) roleDataHashMap.get(otherWelfareEnum.getType());
}
return roleData;
}
/**
* 新建玩家数据
*/
protected abstract <R extends OtherWelfareRoleData> R createRoleData(long roleId);
public void setOtherWelfareEnum(OtherWelfareEnum otherWelfareEnum) {
this.otherWelfareEnum = otherWelfareEnum;
}
}
package com.server.game.scene.otherWelfare.impl.giftPush;
import com.server.core.thread.GlobalThreadPool;
import com.server.game.constant.ModuleConst;
import com.server.game.data.config.bean.ActivityGiftPushRewardConfig;
import com.server.game.data.config.container.ActivityGiftConditionConfigContainer;
import com.server.game.data.config.container.ActivityGiftPushRewardConfigContainer;
import com.server.game.data.config.container.PayConfigConfigContainer;
import com.server.game.data.config.decorator.ActivityGiftConditionConfigDecorator;
import com.server.game.data.config.decorator.ActivityGiftPushRewardConfigDecorator;
import com.server.game.data.config.decorator.PayConfigConfigDecorator;
import com.server.game.listener.EventType;
import com.server.game.listener.event.EventParam;
import com.server.game.listener.event.paramImpl.TwoIntParam;
import com.server.game.listener.requirement.ConditionCompareUtil;
import com.server.game.listener.requirement.RequireGroup;
import com.server.game.logic.PushEventLogic;
import com.server.game.quartz.QuartzMgr;
import com.server.game.recharge.RechargeType;
import com.server.game.scene.item.manager.ResourceLogic;
import com.server.game.scene.otherWelfare.OtherWelfareRoleData;
import com.server.game.scene.otherWelfare.OtherWelfareService;
import com.server.game.scene.otherWelfare.impl.giftPush.data.GiftPushInfo;
import com.server.game.scene.otherWelfare.impl.giftPush.dto.SCActivityGiftPushData;
import com.server.game.scene.otherWelfare.impl.giftPush.job.GiftPushJob;
import com.server.game.utils.DateUtil;
import java.util.AbstractMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.TimeUnit;
/**
* 礼包推送服务
* */
public class GiftPushService extends OtherWelfareService {
/**
* 上次的定时的礼物推送时间
*/
private static long lastTimeGift = 0L;
@Override
public void onListener(EventType<?> eventType, EventParam eventParam) {
if(eventType == EventType.Recharge){
TwoIntParam twoIntParam = (TwoIntParam) eventParam;
PayConfigConfigDecorator payConfig = PayConfigConfigContainer.getInstance().getDecorator(twoIntParam.getParam1());
if(payConfig.getType() == RechargeType.GIFT_PUSH.getType()) {
ActivityGiftPushRewardConfigDecorator config = ActivityGiftPushRewardConfigContainer.getInstance().getByPayId(payConfig.getId());
ResourceLogic.addResourceNotEnoughEmail(eventParam.getRoleId(), config.getReward(), ModuleConst.GiftPush);
GiftPushData giftPushData = getRoleData(eventParam.getRoleId());
if(config.getId() == giftPushData.getGiftPushRewardId()){
giftPushData.setGiftPushRewardId(0);
giftPushData.setOverTime(0L);
pushData(eventParam.getRoleId(), giftPushData, false);
}
}
}
}
@Override
public void initStruct() {
addEventListener(EventType.Recharge);
}
/**
* 初始化数据
* */
public void initData(){
for (ActivityGiftConditionConfigDecorator config : GiftPushEnum.CRONTIME_TYPE.getConfigs()) {
long nextTime = config.getTime();
if(nextTime < DateUtil.getSecondLevelMillis())
continue;
GlobalThreadPool.schedule(new GiftPushJob(config), nextTime - DateUtil.getSecondLevelMillis(), TimeUnit.MILLISECONDS);
}
}
@Override
protected <R extends OtherWelfareRoleData> R createRoleData(long roleId) {
GiftPushData giftPushData = new GiftPushData();
giftPushData.setAlreadyFinish(new HashSet<>());
giftPushData.setCondition(new ConcurrentHashMap<>());
return (R) giftPushData;
}
@Override
public void onLoginInitData(long roleId) {
GiftPushData giftPushData = getRoleData(roleId);
//登陆初始化条件
for (ActivityGiftConditionConfigDecorator config : GiftPushEnum.CONDITION_TYPE.getConfigs()) {
if (giftPushData.getAlreadyFinish().contains(config.getId()))
continue;
Map<Integer, GiftPushInfo> conditions = giftPushData.getCondition();
if (conditions.containsKey(config.getId())) {
GiftPushInfo giftPushInfo = conditions.get(config.getId());
RequireGroup newRequire = ConditionCompareUtil.transToRequireGroup(config.getCondition());
giftPushInfo.setProgress(ConditionCompareUtil.compareUpdateRequire(giftPushInfo.getProgress(), newRequire));
giftPushInfo.loadByProgress();
} else {
GiftPushInfo giftPushInfo = new GiftPushInfo();
conditions.put(config.getId(), giftPushInfo);
giftPushInfo.setConditionId(config.getId());
giftPushInfo.setRoleId(roleId);
giftPushInfo.initProgressByOne(ConditionCompareUtil.transToRequireGroup(config.getCondition()));
}
}
}
@Override
public void onLogin(long roleId) {
GiftPushData data = getRoleData(roleId);
int rewardId = data.getGiftPushRewardId();
ActivityGiftPushRewardConfigDecorator timeConfig = GiftPushEnum.CRONTIME_TYPE.getRewardConfig(roleId, null);
ActivityGiftPushRewardConfigDecorator myConfig = ActivityGiftPushRewardConfigContainer.getInstance().getDecorator(data.getGiftPushRewardId());
long timeStartTime = lastTimeGift;
if (myConfig == null || timeStartTime > data.getOverTime() - myConfig.getDuration() * DateUtil.MINUTE) {
data.setOverTime(lastTimeGift + timeConfig.getDuration() * DateUtil.MINUTE);
data.setGiftPushRewardId(timeConfig.getId());
}
if (data.getGiftPushRewardId() != 0 && data.getOverTime() >= DateUtil.getSecondLevelMillis()) {
pushData(roleId, data, rewardId != data.getGiftPushRewardId());
}
}
@Override
public void onFuncOpen(long roleId) {
onLogin(roleId);
}
/**
* 完成对应档位
* */
public void finish(long roleId, int giftConditionId){
ActivityGiftConditionConfigDecorator conditionConfig = ActivityGiftConditionConfigContainer.getInstance().getDecorator(giftConditionId);
if(conditionConfig == null) {
log.error("推送礼包配置不存在{}", giftConditionId);
return;
}
GiftPushData data = getRoleData(roleId);
if(data.getAlreadyFinish().contains(giftConditionId))
return;
data.getAlreadyFinish().add(giftConditionId);
data.getCondition().remove(giftConditionId);
//推送礼包
pushGift(roleId, conditionConfig.getRewardId());
}
/**
* 推送礼包
* */
public void pushGift(long roleId, int giftRewardId){
GiftPushData data = getRoleData(roleId);
ActivityGiftPushRewardConfigDecorator rewardConfig = ActivityGiftPushRewardConfigContainer.getInstance().getDecorator(giftRewardId);
if(rewardConfig == null) {
log.error("推送礼包配置不存在{}", giftRewardId);
return;
}
data.setGiftPushRewardId(rewardConfig.getId());
data.setOverTime(DateUtil.getSecondLevelMillis() + rewardConfig.getDuration() * DateUtil.MINUTE);
pushData(roleId, data, true);
}
/**
* 推送
*/
private void pushData(long roleId, GiftPushData data, boolean isShow){
SCActivityGiftPushData scActivityGiftPushData = new SCActivityGiftPushData();
scActivityGiftPushData.setOverTime(data.getOverTime());
scActivityGiftPushData.setGiftRewardId(data.getGiftPushRewardId());
scActivityGiftPushData.setTip(isShow);
PushEventLogic.pushEvent(roleId, scActivityGiftPushData);
}
/**
* 设置定时的时间礼包
* */
public static void setTimeGift(){
lastTimeGift = DateUtil.getSecondLevelMillis();
}
}
最后通过一个枚举类联系服务类和数据类
package com.server.game.scene.otherWelfare;
import com.server.game.scene.funcOpen.FuncType;
import com.server.game.scene.otherWelfare.impl.dayGift.DayGiftWelfareService;
import com.server.game.scene.otherWelfare.impl.firstRecharge.FirstRechargeService;
import com.server.game.scene.otherWelfare.impl.giftPush.GiftPushService;
import com.server.game.scene.otherWelfare.impl.levelInvest.LevelInvestWelfareService;
import com.server.game.scene.otherWelfare.impl.warTrophies.service.WarTrophiesService;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
public enum OtherWelfareEnum {
/**
* 每日礼包
*/
DAY_GIFT(2, new DayGiftWelfareService(), FuncType.ActivityDayGift),
/**
* 等级投资
*/
LEVEL_INVEST(4, new LevelInvestWelfareService(), FuncType.ActivityLevelInvest),
/**
* 战利品
*/
WAR_TROPHIES(5, new WarTrophiesService(), FuncType.Activitytreasury),
/**
* 礼包推送
*/
GIFT_PUSH(6, new GiftPushService(), FuncType.ActivityGiftPush),
/**
* 首冲
*/
FirstRecharge(7, new FirstRechargeService(), FuncType.FirstRecharge),
;
private final int type;
private final OtherWelfareService service;
private final FuncType funcType;
OtherWelfareEnum(int type, OtherWelfareService service, FuncType funcType) {
this.type = type;
this.service = service;
this.service.setOtherWelfareEnum(this);
this.funcType = funcType;
}
public int getType() {
return type;
}
public OtherWelfareService getService() {
return service;
}
public FuncType getFuncType() {
return funcType;
}
private static Map<Integer, OtherWelfareEnum> map = Stream.of(OtherWelfareEnum.values()).collect(Collectors.toMap(v -> v.type, v -> v));
public static OtherWelfareEnum getByType(int type) {
return map.get(type);
}
}
调用的方式如下:
GiftPushService service = (GiftPushService) OtherWelfareEnum.GIFT_PUSH.getService();
service.pushGift(onlineRole.getRoleId(), GiftPushEnum.CRONTIME_TYPE.getRewardConfig(onlineRole.getRoleId(), null).getId());
总结
就是通过枚举的方式获得对应的福利的服务类,然后通过服务类和福利的具体数据来出来相应的数据。其中服务类中继承需要从父服务类中继承的接口来处理具体的逻辑。
结语
通过上面的方式可以设计一个只需要定义好具体福利即可,其他具体的处理放入到具体的服务类和数据类中。这样方便后续服务的扩张。