Files
ZYZ/shared/domain/activityField/popUpShopField.ts
2022-07-28 15:59:22 +08:00

579 lines
24 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import moment = require("moment");
import { POP_UP_SHOP_CONDITION_TYPE, POP_UP_SHOP_REFRESH_TIME_TYPE, REFRESH_TIME } from "../../consts";
import { ActivityModelType } from "../../db/Activity";
import { ActivityPopUpShopModelType, PopUpShopItem as ActivityPopUpShopItem } from "../../db/ActivityPopUpShop";
import { RewardInter } from "../../pubUtils/interface";
import { parseNumberList } from "../../pubUtils/util";
import { stringWithTypeToRewardInter } from "../../pubUtils/roleUtil";
import { ActivityBase } from './activityField';
import { PVPConfigModel } from "../../db/SystemConfig";
import { nowSeconds } from "../../pubUtils/timeUtil";
// 数据库格式
interface PopUpShopDataInDb {
packages: PopUpShopPackageInDb[];
vipLevel: PopUpShopVipLevelInDb[];
}
interface PopUpShopPackageInDb {
id: number; // 弹出礼包id
conditionType: number; // 条件类型
canLvUp: number; // 是否可升降
items: PopUpShopItemInDb[]; // 礼包
duration: number; // 礼包弹出之后,持续时间,单位小时
keyItem: PopUpShopKeyItemIbDb[]; // 关键资源限制refreshDay内可以获得Y个
checkPushingItem: number; // 是否在有同类型礼包的时候弹新礼包
refreshTimeType: number; // 刷新时间类型0-不复现 1-自然日 2-自然周 3-自然月 4-从beginTime开始固定天 5-按pvp赛季
refreshDay: number; // 上面刷新时间的具体天数refreshTimeType=0-3都不会使用这个参数refreshTimeType=4时这个表示固定几天
pushCnt: number; // 在自然日cd之内总推送次数不复现的填0不限制这个使用其他限制的填-1如passCnt填次数的时候这里填-1
passCnt: number; // 弹出了没有购买多少次之后不再推送默认填0
}
interface PopUpShopItemInDb {
id: number; // 档位唯一id升降档的时候按照id顺序排升档id+降档id-
name: string; // 包名
conditionParam: string; // 条件类型的参数填法根据conditionType表判断
price: number; // 商品价格
productID: string; // 商品id支付时使用
consume: string; // 消耗资源(这里优先rmb的price价格其次资源消耗)
reward: string; // 任务奖励,格式:1&3&1(类型&id&数量) 类型定义:1.英雄2.物品
rebate: number; // 折扣用于显示
buyCnt: number; // 购买多少次后消失原来的count字段
vipLv: number; // 需要多少级
lv: number; // 队伍等级
}
interface PopUpShopKeyItemIbDb {
gid: number; // 关键资源id
max: number; // 关键资源上限Y
}
interface PopUpShopVipLevelInDb {
lv: number; // 几级
minPayMoney: number; // 最低金额
maxPayMoney: number; // 最高金额5000+的填-1
}
/**
* 弹出商店
*/
export class PopUpShopData extends ActivityBase {
packages: PopUpShopPackage[] = [];
vipLevel: PopUpShopVipLevel[] = [];
private packageMap: Map<number, number> = new Map();
vipLv: number = 0; // 玩家当前等级
lv: number = 0; // 玩家等级
constructor(activityData: ActivityModelType) {
super(activityData, 0, 0);
this.initData(activityData.data)
}
public initData(data: string) {
let dataObj: PopUpShopDataInDb = JSON.parse(data);
for(let pkg of dataObj.packages) {
let obj = new PopUpShopPackage(pkg, this);
this.packages.push(obj);
this.packageMap.set(obj.id, this.packages.length - 1);
}
for(let lv of dataObj.vipLevel) {
let obj = new PopUpShopVipLevel(lv);
this.vipLevel.push(obj);
}
}
//解析玩家开启的商店记录
public setPlayerRecords(datas: ActivityPopUpShopModelType[], latestRecords: ActivityPopUpShopModelType[], totalPay: number, lv: number) {
if (!datas) return;
this.setVipLv(totalPay);
this.setLv(lv);
for(let data of datas) {
let pkg = this.findPackageById(data.id);
pkg.addPlayerRecord(data);
}
for(let data of latestRecords) {
let pkg = this.findPackageById(data.id);
pkg.setLatestBought(data);
}
}
private setVipLv(totalPay: number) {
for(let { lv, minPayMoney, maxPayMoney } of this.vipLevel) {
if(totalPay >= minPayMoney && (maxPayMoney == -1 || totalPay < maxPayMoney)) {
this.vipLv = lv;
break;
}
}
}
private setLv(lv: number) {
if(lv) this.lv = lv;
}
public findPackageById(id: number) {
let index = this.packageMap.get(id);
return this.packages[index];
}
public getShowResult() {
return new PopUpShopShow(this);
}
public checkItem(data: ActivityPopUpShopModelType, productID: string) {
let pkg = this.findPackageById(data.id);
let item = pkg.findItemByProductID(productID);
if(!item) return false
return item.hasBoughtCnt < item.buyCnt;
}
public updateRecord(data: ActivityPopUpShopModelType, productID: string) {
let pkg = this.findPackageById(data.id);
let dataItem = data.items.find(cur => cur.productID == productID);
let item = pkg.findItemByProductID(productID);
if(!item) return null
return item.updateItem(dataItem);
}
public updateRecordById(data: ActivityPopUpShopModelType, id: number) {
let pkg = this.findPackageById(data.id);
let dataItem = data.items.find(cur => cur.id == id);
let item = pkg.findItemById(id);
return item.updateItem(dataItem);
}
}
// 同一礼包类型为一组共享cd档位
export class PopUpShopPackage {
id: number; // 弹出礼包id
conditionType: number; // 条件类型
canLvUp: number; // 是否可升降
checkPushingItem: number; // 是否在同类型礼包
duration: number; // 礼包弹出之后,持续时间,单位小时
refreshTimeType: number; // 刷新时间类型0-不复现 1-自然日 2-自然周 3-自然月 4-从beginTime开始固定天
refreshDay: number; // 复现自然日cd不复现的填0从弹出礼包那一天的凌晨5点开始计数
pushCnt: number; // 在自然日cd之内总推送次数不复现的填0不限制这个使用其他限制的填-1如passCnt填次数的时候这里填-1
passCnt: number; // 弹出了没有购买多少次之后不再推送默认填0
// initData的时候处理的数据
items: PopShopItem[] = []; // 礼包
keyItem: PopUpShopKeyItem[] = []; // 关键资源限制refreshDay内可以获得Y个
private itemByProductID: Map<string, number> = new Map(); // productID => index
private itemById: Map<number, number> = new Map();
private parent: PopUpShopData;
// setPlayerRecord的时候处理的数据
hasPushCnt: number = 0; // 已经推过几次了
hasPassCnt: number = 0; // 已经被连续无视过几次
latestItemId: number = 0; // 升降礼包最近一次的那档id
latestBought: boolean = false; // 最近一次是否购买
isPushing: boolean = false; // 是否有正在推送中
constructor(data: PopUpShopPackageInDb, parent: PopUpShopData) {
this.parent = parent
this.id = data.id;
this.conditionType = data.conditionType;
this.canLvUp = data.canLvUp;
this.duration = data.duration;
this.checkPushingItem = data.checkPushingItem;
this.refreshTimeType = data.refreshTimeType;
this.refreshDay = data.refreshDay;
this.pushCnt = data.pushCnt;
this.passCnt = data.passCnt;
for(let item of data.items) {
let obj = new PopShopItem(item, this);
this.items.push(obj);
this.itemByProductID.set(obj.productID, this.items.length - 1);
this.itemById.set(obj.id, this.items.length - 1);
}
for(let key of data.keyItem) {
this.keyItem.push(new PopUpShopKeyItem(key));
}
}
// vip分档
public getVipLv() {
return this.parent.vipLv;
}
public getLv() {
return this.parent.lv;
}
public findItemByProductID(productID: string) {
let index = this.itemByProductID.get(productID);
return this.items[index];
}
public findItemById(id: number) {
let index = this.itemById.get(id);
return this.items[index];
}
public addPlayerRecord(data: ActivityPopUpShopModelType) {
let now = new Date();
this.hasPushCnt++;
for(let key of this.keyItem) {
key.addHasBoughtCnt(data.items);
}
if(data.beginTime <= now && data.endTime > now) { // 正在进行中
for(let itemData of data.items) {
let item = this.findItemByProductID(itemData.productID);
if(item) {
item.setPlayerRecords(data, itemData);
console.log('#### item.isPushing', item.isPushing)
if(item.isPushing) this.isPushing = true;
}
}
} else if (data.beginTime < now && data.endTime < now) { // 已经结束了的
if(data.hasBought) { // 是否看到了,但是没有买
this.hasPassCnt = 0;
} else {
this.hasPassCnt ++;
}
}
for(let itemData of data.items) {
let item = this.findItemByProductID(itemData.productID);
if(item) {
item.addPushCnt();
}
}
}
public setLatestBought(data: ActivityPopUpShopModelType) {
this.latestItemId = data.items[0].id;
this.latestBought = data.items[0].hasBoughtCnt > 0;
}
// 检查这个礼包是否可以推送
public checkPackageCanPush(conditionType: POP_UP_SHOP_CONDITION_TYPE) {
// 1. 达成条件类型是否符合
console.log('######### 3.5.1', this.conditionType, conditionType, this.conditionType != conditionType)
if(this.conditionType != conditionType) return false;
// 2. 判断同类型是否是已经有在持续中的
console.log('######### 3.5.2', this.checkPushingItem, this.isPushing);
if(this.checkPushingItem && this.isPushing) return false;
// 3. 自然日内推送次数
console.log('######### 3.5.3', this.canLvUp, this.pushCnt, this.hasPushCnt, this.pushCnt != -1, this.hasPushCnt >= this.pushCnt)
if(this.canLvUp == 1 && this.pushCnt != -1 && this.hasPushCnt >= this.pushCnt) return false;
// 4. 连续无视次数
console.log('######### 3.5.4', this.hasPassCnt, this.passCnt, this.passCnt > 0 && this.hasPassCnt >= this.passCnt)
if(this.passCnt > 0 && this.hasPassCnt >= this.passCnt) return false;
return true;
}
public getItemsByCondition(param: PopUpConditionParamInter) {
let items: PopShopItem[] = [], minItem: PopShopItem, maxItem: PopShopItem;
for(let item of this.items) {
if(!item.checkItemCanPush(this.conditionType, param)) continue; // 达成任务条件
if(!minItem) minItem = item;
if(!maxItem || maxItem.id > item.id) maxItem = item;
if(this.canLvUp == 1) { // 升降礼包
if(this.latestBought && item.id == this.latestItemId + 1 ) { // 最近一次买了,升级
items.push(item);
} else if(!this.latestBought && item.id == this.latestItemId - 1 ) {
items.push(item);
}
} else {
items.push(item);
}
}
if(!items.length && maxItem && minItem) items.push(this.latestBought? maxItem: minItem);
return items;
}
public async getEffectTime() {
let now = new Date();
let beginTime = now;
let endTime = moment(now).add(this.duration, 'h').toDate();
let effectBeginTime: Date, effectEndTime: Date;
switch(this.refreshTimeType) {
case POP_UP_SHOP_REFRESH_TIME_TYPE.NO:
effectBeginTime = new Date(this.parent.beginTime);
effectEndTime = new Date(this.parent.endTime);
break;
case POP_UP_SHOP_REFRESH_TIME_TYPE.NATURAL_DAY:
effectBeginTime = moment(now).startOf('d').add(REFRESH_TIME, 'h').toDate();
effectEndTime = moment(now).endOf('d').add(REFRESH_TIME, 'h').toDate();
break;
case POP_UP_SHOP_REFRESH_TIME_TYPE.NATURAL_WEEK:
effectBeginTime = moment(now).startOf('w').add(REFRESH_TIME, 'h').toDate();
effectEndTime = moment(now).endOf('w').add(REFRESH_TIME, 'h').toDate();
break;
case POP_UP_SHOP_REFRESH_TIME_TYPE.NATURAL_MONTH:
effectBeginTime = moment(now).startOf('month').add(REFRESH_TIME, 'h').toDate();
effectEndTime = moment(now).endOf('month').add(REFRESH_TIME, 'h').toDate();
break;
case POP_UP_SHOP_REFRESH_TIME_TYPE.DAYS:
let gap = moment(now).diff(this.parent.beginTime, 'd');
let n = Math.floor(gap / this.refreshDay);
effectBeginTime = moment(this.parent.beginTime).startOf('d').add(n * this.refreshDay, 'd').add(REFRESH_TIME, 'h').toDate();
effectEndTime = moment(this.parent.beginTime).startOf('d').add((n + 1) * this.refreshDay, 'd').add(REFRESH_TIME, 'h').toDate();
break;
case POP_UP_SHOP_REFRESH_TIME_TYPE.PVP:
let pvpConfig = await PVPConfigModel.findCurPVPConfig();
effectBeginTime = new Date((pvpConfig?.seasonStartTime??nowSeconds()) * 1000);
effectEndTime = new Date((pvpConfig?.seasonEndTime??nowSeconds()) * 1000);
break;
}
return {
beginTime, endTime, effectBeginTime, effectEndTime
}
}
public pushPackage(popUpShopRec: ActivityPopUpShopModelType) {
this.addPlayerRecord(popUpShopRec);
let items: PopUpShopItemShow[] = [];
for(let item of popUpShopRec.items) {
let itemObj = this.findItemByProductID(item.productID);
if(itemObj && itemObj.isPushing) {
let obj = new PopUpShopItemShow(itemObj);
items.push(obj);
}
}
return items
}
}
// 礼包,购买以此为单位
export class PopShopItem {
id: number; // 档位唯一id升降档的时候按照id顺序排升档id+降档id-
name: string; // 礼包名
param: string;
conditionParam: number[]; // 条件类型的参数填法根据conditionType表判断
price: number; // 商品价格
productID: string; // 商品id支付时使用
consume: string; // 消耗资源(这里优先rmb的price价格其次资源消耗)
reward: string; // 任务奖励,格式:1&3&1(类型&id&数量) 类型定义:1.英雄2.物品
rebate: number; // 折扣用于显示
buyCnt: number; // 购买多少次后消失原来的count字段
pushCnt: number;
canLvUp: number;
vipLv: number = 0;
lv: number = 0;
rewardInter: RewardInter[] = [];
parent: PopUpShopPackage;
hasBoughtCnt: number = 0;
isPushing: boolean = false; // 是否有推送
hasPushCnt: number = 0;
code: string = ''; // 推送唯一code
hasShown: boolean = false; // 客户端是否已经展示
beginTime: number = 0; // 推送开始时间
endTime: number = 0; // 推送结束时间
constructor(data: PopUpShopItemInDb, parent: PopUpShopPackage) {
this.id = data.id;
this.name = data.name;
this.param = data.conditionParam;
this.conditionParam = parseNumberList(data.conditionParam);
this.price = data.price;
this.productID = data.productID;
this.consume = data.consume;
this.reward = data.reward;
this.rewardInter = stringWithTypeToRewardInter(data.reward);
this.rebate = data.rebate;
this.buyCnt = data.buyCnt;
this.pushCnt = parent.pushCnt;
this.canLvUp = parent.canLvUp;
this.parent = parent;
if(data.vipLv != undefined) this.vipLv = data.vipLv;
if(data.lv != undefined) this.lv = data.lv;
}
public setPlayerRecords(data: ActivityPopUpShopModelType, item: ActivityPopUpShopItem) {
this.code = data.code;
this.hasShown = data.hasShown;
this.beginTime = data.beginTime.getTime();
this.endTime = data.endTime.getTime();
this.hasBoughtCnt = item.hasBoughtCnt;
if(this.hasBoughtCnt < this.buyCnt) {
this.isPushing = true;
}
}
public addPushCnt() {
this.hasPushCnt++;
}
public checkItemCanPush(conditionType: POP_UP_SHOP_CONDITION_TYPE, param: PopUpConditionParamInter) {
console.log('##### xyz?1', this.hasBoughtCnt >= this.buyCnt)
if(this.hasBoughtCnt >= this.buyCnt) return false;
console.log('##### xyz?2', this.parent.getVipLv(), this.vipLv, this.parent.getVipLv() < this.vipLv)
if(this.parent.getVipLv() < this.vipLv || this.parent.getLv() < this.lv) return false;
console.log('##### xyz?3', this.parent.getLv(), this.lv, this.parent.getLv() < this.lv)
if(this.parent.getLv() < this.lv) return false;
console.log('##### xyz?4', this.checkKeyTime())
if(!this.checkKeyTime()) return false
console.log('##### xyz?5', this.canLvUp, this.hasPushCnt, this.pushCnt, this.pushCnt != -1, this.hasPushCnt >= this.pushCnt)
if(this.canLvUp == 0 && this.pushCnt != -1 && this.hasPushCnt >= this.pushCnt) return false
console.log('##### xyz?6', conditionType, this.conditionParam, param)
switch(conditionType) {
case POP_UP_SHOP_CONDITION_TYPE.LV_TO:
return param.oldLv < this.conditionParam[0] && param.newLv >= this.conditionParam[0];
case POP_UP_SHOP_CONDITION_TYPE.GET_HERO_BY_QUALITY:
return param.quality == this.conditionParam[0];
case POP_UP_SHOP_CONDITION_TYPE.GACHA_RES_NOT_ENOUGH:
case POP_UP_SHOP_CONDITION_TYPE.TERAPH_RES_NOT_ENOUGH:
return true;
case POP_UP_SHOP_CONDITION_TYPE.MAIN_BATTLE:
return param.warId == this.conditionParam[0];
case POP_UP_SHOP_CONDITION_TYPE.PVP:
return param.seasonWinNum >= this.conditionParam[0];
case POP_UP_SHOP_CONDITION_TYPE.EQUIP_STAR:
return param.equipStar >= this.conditionParam[0];
case POP_UP_SHOP_CONDITION_TYPE.AUCTION:
return true;
default:
return false;
}
}
public checkKeyTime() {
let keyItem = this.parent.keyItem;
console.log('####', keyItem)
for(let key of keyItem) {
let hasCurKey = this.rewardInter.find(cur => cur.id == key.gid);
console.log('#### hasCurKey', hasCurKey, key.hasBoughtCnt, key.max)
if(hasCurKey && key.hasBoughtCnt > key.max) return false;
}
return true;
}
public updateItem(data: ActivityPopUpShopItem) {
this.hasBoughtCnt = data.hasBoughtCnt;
return new PopUpShopItemShow(this);
}
}
// 关键道具使用次数
export class PopUpShopKeyItem {
gid: number; // 关键资源id
max: number; // 关键资源上限Y
hasBoughtCnt: number = 0; // 已购买次数
constructor(data: PopUpShopKeyItemIbDb) {
if(data) {
this.gid = data.gid;
this.max = data.max;
}
}
addHasBoughtCnt(items: ActivityPopUpShopItem[]) {
console.log('###### addHasBoughtCnt', items)
for(let { rewards, hasBoughtCnt } of items) {
for(let { id, count } of rewards) {
if(id == this.gid) this.hasBoughtCnt += count * hasBoughtCnt;
}
}
}
}
export class PopUpShopVipLevel {
lv: number; // 几级
minPayMoney: number; // 最低金额
maxPayMoney: number; // 最高金额5000+的填-1
constructor(data: PopUpShopVipLevelInDb) {
this.lv = data.lv;
this.minPayMoney = data.minPayMoney;
this.maxPayMoney = data.maxPayMoney;
}
}
export interface PopUpConditionParamInter {
oldLv?: number;
newLv?: number;
quality?: number;
warId?: number;
seasonWinNum?: number;
equipStar?: number;
}
// 用于显示
class PopUpShopShow {
activityId: number; // 活动id
type: number; // 活动类型 ACTIVITY_TYPE
beginTime: number; // 活动开始时间13位时间戳
endTime: number; // 活动开始时间13位时间戳
todayIndex: number; // 活动后第几天
delayDay: number; // 延迟多少天开始
roundIndex: number; // 如果是周期活动活动轮回第几个周期
nextRefreshTime: number; // 如果是周期活动,下一次开始时间
// 以上为通用属性
items: PopUpShopItemShow[] = []; // 同一类型的礼包都在一个packages下面
constructor(data: PopUpShopData) {
let baseData = data.getBaseKeys();
for(let key in baseData) {
this[key] = baseData[key];
}
let itemsByCode = new Map<string, PopShopItem[]>();
for(let pkg of data.packages) {
for(let item of pkg.items) {
// let { id, isPushing, hasBoughtCnt, buyCnt, hasPushCnt, pushCnt } = item;
// console.log('**** PopUpShopShow', { id, isPushing, hasBoughtCnt, buyCnt, hasPushCnt, pushCnt })
if(item.isPushing && item.code) {
if(!itemsByCode.has(item.code)) {
itemsByCode.set(item.code, []);
}
itemsByCode.get(item.code).push(item);
}
}
}
for(let [_, items] of itemsByCode) {
let hasItemsNotAllBought = items.filter(item => item.hasBoughtCnt < item.buyCnt).length > 0;
if(hasItemsNotAllBought) {
for(let item of items) {
let obj = new PopUpShopItemShow(item);
this.items.push({...obj});
}
}
}
}
}
export class PopUpShopItemShow {
code: string; // 本次推送的code用于购买中传给param
packageId: number; // 礼包类型id
endTime: number; // 礼包消失时间
hasShown: boolean; // 礼包是否已经在客户端展示
id: number; // 档位唯一id升降档的时候按照id顺序排升档id+降档id-
name: string; // 礼包
price: number; // 商品价格
productID: string; // 商品id支付时使用
consume: string; // 消耗资源(这里优先rmb的price价格其次资源消耗)
reward: string; // 任务奖励,格式:1&3&1(类型&id&数量) 类型定义:1.英雄2.物品
rebate: number; // 折扣用于显示
buyCnt: number; // 需要购买的次数
hasBoughtCnt: number; // 已购买的次数
constructor(data: PopShopItem) {
this.code = data.code;
this.hasShown = data.hasShown;
this.packageId = data.parent.id;
this.endTime = data.endTime;
this.id = data.id;
this.name = data.name;
this.price = data.price;
this.productID = data.productID;
this.consume = data.consume;
this.reward = data.reward;
this.rebate = data.rebate;
this.buyCnt = data.buyCnt;
this.hasBoughtCnt = data.hasBoughtCnt;
}
}