diff --git a/game-server/app/servers/activity/handler/activityHandler.ts b/game-server/app/servers/activity/handler/activityHandler.ts index 83ad9a4c6..ad82fc362 100644 --- a/game-server/app/servers/activity/handler/activityHandler.ts +++ b/game-server/app/servers/activity/handler/activityHandler.ts @@ -7,6 +7,7 @@ import { addReward, stringToRewardParam, useGiftPackage } from '../../../service import { ActivityFirstGiftModel } from '../../../db/ActivityFirstGift'; import { signInActivity, signInVIPActivity } from '../../../services/signInService'; import { growthFundActivity } from '../../../services/growthFundService'; +import { newPlayerLimitPackageActivity } from '../../../services/limitPackageService'; export default function (app: Application) { return new ActivityHandler(app); @@ -129,7 +130,17 @@ export class ActivityHandler { }); } } - + //新手限定RMB购买礼包 + { + let data = await newPlayerLimitPackageActivity(serverId, roleId); + if (data) { + playerActivityArray.push({ + type: ACTIVITY_TYPE.NEW_PLAYER_LIMIT_PACKAGE, + activityId: data.activityId, + data, + }); + } + } return resResult(STATUS.SUCCESS, { playerActivityArray }); } diff --git a/game-server/app/servers/activity/handler/treasureHuntHandler.ts b/game-server/app/servers/activity/handler/treasureHuntHandler.ts new file mode 100644 index 000000000..a435625c1 --- /dev/null +++ b/game-server/app/servers/activity/handler/treasureHuntHandler.ts @@ -0,0 +1,58 @@ +import { Application, BackendSession } from 'pinus'; +import { resResult } from '../../../pubUtils/util'; +import { STATUS, ACTIVITY_RESOURCES_TYPE, ACTIVITY_TYPE } from '../../../consts'; +import { getPlayerTreasureHuntData, getTreasureHuntData } from '../../../services/treasureHuntService'; + + +export default function (app: Application) { + return new TreasureHuntHandler(app); +} + +export class TreasureHuntHandler { + constructor(private app: Application) { + } + + /************************寻宝骑兵活动****************************/ + + /** + * @description 获取寻宝骑兵活动的数据 + * @param {BackendSession} session + * @memberof TreasureHuntHandler + */ + async getTreasureHuntActivity(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + let { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex, activityData } = await getTreasureHuntData(serverId); + if (!activityData) { + return resResult(STATUS.ACTIVITY_MISSING, {}); + } + let playerData = await getPlayerTreasureHuntData(activityId, serverId, roleId, huntRoundIndex, huntBeginTime, huntEndTime); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } + + /** + * @description 寻宝骑兵购买每日商店中的商品 + * @param {BackendSession} session + * @memberof TreasureHuntHandler + */ + async buyGoods(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + let { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex, activityData } = await getTreasureHuntData(serverId); + if (!activityData) { + return resResult(STATUS.ACTIVITY_MISSING, {}); + } + if (activityId !== huntActivityId) { + return resResult(STATUS.ACTIVITY_MISSING, {}); + } + + let playerData = await getPlayerTreasureHuntData(activityId, serverId, roleId, huntRoundIndex, huntBeginTime, huntEndTime,); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } +} diff --git a/game-server/app/services/limitPackageService.ts b/game-server/app/services/limitPackageService.ts index 15cb7c1d7..16cd63045 100644 --- a/game-server/app/services/limitPackageService.ts +++ b/game-server/app/services/limitPackageService.ts @@ -14,15 +14,15 @@ import { LimitShopData, ShopItem } from '../domain/activityField/limitShopField' export async function newPlayerLimitPackageActivity(serverId: number, roleId: string) { let activityArray: ActivityModelType[] = await ActivityModel.findOpenActivityByType(serverId, ACTIVITY_TYPE.NEW_PLAYER_LIMIT_PACKAGE, new Date) - activityArray = activityArray.sort((a, b) => { - return b.activityId - a.activityId - }); if (activityArray.length == 0) { return null; } let activityData = activityArray[0]; - let playerData = await getPlayerLimitPackageData(activityData.activityId, serverId, roleId); - return playerData + let playerData = new LimitShopData(activityData); + + let playerRecord: ActivityShopModelType = await ActivityShopModel.findData(activityData.activityId, roleId, playerData.roundIndex); + playerData.setPlayerRecords(playerRecord); + return playerData; } /** diff --git a/game-server/app/services/treasureHuntService.ts b/game-server/app/services/treasureHuntService.ts new file mode 100644 index 000000000..b0457764d --- /dev/null +++ b/game-server/app/services/treasureHuntService.ts @@ -0,0 +1,155 @@ +import moment = require('moment'); +import { ACTIVITY_TYPE, SERVER_OPEN_TIME, TASK_TYPE } from '../consts'; +import { ActivityModel, ActivityModelType } from '../db/Activity'; +import { ActivityTreasureHuntShopModel, ActivityTreasureHuntShopModelType } from '../db/ActivityTreasureHuntShop'; +import { ServerTempModel, ServerTempModelType } from '../db/ServerTemp'; +import { RewardParam } from '../domain/activityField/rewardField'; +import { TreasureHuntData } from '../domain/activityField/treasureHuntField'; +import { deltaDays } from '../pubUtils/util'; +import { addReward, stringToRewardParam } from './giftPackageService'; + +/** + * 获取活动数据 + * + * @param {number} serverId 区Id + * @param {number} type 活动类型 ACTIVITY_TYPE + * @param {string} roleId 角色Id + * + */ + +export async function treasureHuntActivity(serverId: number, roleId: string) { + let { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex, activityData } = await getTreasureHuntData(serverId); + if (!activityData) { + return null; + } + let playerData = await getPlayerTreasureHuntData(activityData.activityId, serverId, roleId, huntRoundIndex, huntBeginTime, huntEndTime); + return playerData +} + +/** + * 玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getPlayerTreasureHuntData(activityId: number, serverId: number, roleId: string, huntRoundIndex: number, huntBeginTime: Date, huntEndTime: Date) { + let activityData: ActivityModelType = await ActivityModel.findActivity(serverId, activityId, true); + let dayIndex = 0; + let playerRecord: ActivityTreasureHuntShopModelType = await ActivityTreasureHuntShopModel.findTreasureData(serverId, activityId, roleId, huntRoundIndex, dayIndex); + + let playerData = new TreasureHuntData(activityData); + playerData.beginTime = moment(huntBeginTime).valueOf(); + playerData.endTime = moment(huntEndTime).valueOf(); + playerData.todayIndex = deltaDays(huntBeginTime, new Date) + 1;; + playerData.roundIndex = huntRoundIndex; + // playerData.setPlayerRecords(playerRecord); + return playerData; +} + +//获取当前活动的周期等数据 +export async function getTreasureHuntData(serverId: number) { + let tempData: ServerTempModelType = await ServerTempModel.findData(serverId); + let now = moment(new Date()).toDate(); + let activityData: ActivityModelType = null; //当前活动数据 + + if (!tempData) {//开始新周期 + let { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex } = await getNewActivityData(serverId); + tempData = await ServerTempModel.updateTreasureHuntData(serverId, huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex); + activityData = await ActivityModel.findActivity(serverId, huntActivityId, true); + return { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex, activityData } + } + if (now > tempData.huntEndTime) {//活动过期 + let { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex } = await getNextActivityData(serverId, tempData.huntActivityId, tempData.huntEndTime, tempData.huntRoundIndex); + tempData = await ServerTempModel.updateTreasureHuntData(serverId, huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex); + activityData = await ActivityModel.findActivity(serverId, huntActivityId, true); + return { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex, activityData } + } + if (!activityData) { + activityData = await ActivityModel.findActivity(serverId, tempData.huntActivityId, true); + } + return { + huntActivityId: tempData.huntActivityId, + huntBeginTime: tempData.huntBeginTime, + huntEndTime: tempData.huntEndTime, + huntRoundIndex: tempData.huntRoundIndex, + activityData + } +} + +async function getNewActivityData(serverId: number) { + let huntActivityId: number = 0; // 寻宝骑兵-活动Id + let huntEndTime: Date = null; // 寻宝骑兵-结束时间 + let huntRoundIndex: number = 0; // 寻宝骑兵-周期数 + let activityArray: ActivityModelType[] = await ActivityModel.findActivityByType(serverId, ACTIVITY_TYPE.TREASURE_HUNT, 1)//activityId从小到大排序 + let treasureHuntDataArray: TreasureHuntData[] = []; + for (let obj of activityArray) { + treasureHuntDataArray.push(new TreasureHuntData(obj)); + } + let isOver = false; + let huntBeginTime = moment(SERVER_OPEN_TIME).startOf('d').add(1, 'd').toDate(); + let curDate = moment(new Date()).toDate() + while (!isOver) { + huntRoundIndex++; + for (let data of treasureHuntDataArray) { + let endTime = moment(huntBeginTime).add(data.day, 'd').toDate(); + if (huntBeginTime < curDate && curDate < endTime) { + huntEndTime = moment(endTime).add(-1, 'm').toDate() + huntActivityId = data.activityId; + isOver = true; + } else { + huntBeginTime = endTime; + } + } + if (huntRoundIndex >= 365) {//max + isOver = true; + } + } + return { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex } +} + +async function getNextActivityData(serverId: number, oldHuntActivityId: number, oldHuntEndTime: Date, oldHuntRoundIndex: number) { + let huntActivityId: number = 0; // 寻宝骑兵-活动Id + let huntEndTime: Date = null; // 寻宝骑兵-结束时间 + let huntRoundIndex: number = oldHuntRoundIndex; // 寻宝骑兵-周期数 + let activityArray: ActivityModelType[] = await ActivityModel.findActivityByType(serverId, ACTIVITY_TYPE.TREASURE_HUNT, 1)//activityId从小到大排序 + let treasureHuntDataArray: TreasureHuntData[] = []; + for (let obj of activityArray) { + treasureHuntDataArray.push(new TreasureHuntData(obj)); + } + let isOver = false; + let huntBeginTime = moment(oldHuntEndTime).add(1, 'd').startOf('d').toDate(); + let curDate = moment(new Date()).toDate() + + let index = treasureHuntDataArray.findIndex(obj => { return obj && obj.activityId === oldHuntActivityId }); + for (; index < treasureHuntDataArray.length; index++) { + let data = treasureHuntDataArray[index]; + let endTime = moment(huntBeginTime).add(data.day, 'd').toDate(); + if (huntBeginTime < curDate && curDate < endTime) { + huntEndTime = moment(endTime).add(-1, 'm').toDate() + huntActivityId = data.activityId; + isOver = true; + } else { + huntBeginTime = endTime; + } + } + + while (!isOver) { + huntRoundIndex++; + for (let data of treasureHuntDataArray) { + let endTime = moment(huntBeginTime).add(data.day, 'd').toDate(); + if (huntBeginTime < curDate && curDate < endTime) { + huntEndTime = moment(endTime).add(-1, 'm').toDate() + huntActivityId = data.activityId; + isOver = true; + } else { + huntBeginTime = endTime; + } + } + if (huntRoundIndex >= 365) {//max + isOver = true; + } + } + return { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex } +} diff --git a/shared/db/ActivityTreasureHunt.ts b/shared/db/ActivityTreasureHunt.ts new file mode 100644 index 000000000..b0d63cf4f --- /dev/null +++ b/shared/db/ActivityTreasureHunt.ts @@ -0,0 +1,91 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +import { ORDER_STATE } from '../consts'; + +/** + * 玩家寻宝猎人活动数据 +*/ +@index({ roleId: 1 }) + +export default class ActivityTreasureHunt extends BaseModel { + + @prop({ required: true }) + serverId: number; // 区号 + @prop({ required: true }) + roleId: string; // 用户id + @prop({ required: true }) + activityId: number; // 活动Id + @prop({ required: true }) + roundIndex: number; // 周期Id + + + + //保存平台订单号 + public static async saveOrderID(roleId: string, localOrderID: string, aliOrderID: string) { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOneAndUpdate({ roleId, localOrderID }, + { $set: { orderID: aliOrderID } }, + { new: true }).lean(true); + return result; + } + + //校验订单 + public static async check(roleId: string, localOrderID: string, message: string = '') { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOneAndUpdate({ roleId, localOrderID, state: { $ne: ORDER_STATE.RESULT_SUCCESS } }, + { $set: { state: ORDER_STATE.CHECK_ORDER, message } }, + { new: true }).lean(true); + return result; + } + + //订单支付失败 + public static async fail(roleId: string, localOrderID: string, message: string = '') { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOneAndUpdate({ roleId, localOrderID, state: { $ne: ORDER_STATE.RESULT_SUCCESS } }, + { $set: { state: ORDER_STATE.RESULT_FAIL, message } }, + { new: true }).lean(true); + return result; + } + + //订单支付成功 + public static async success(roleId: string, localOrderID: string, message: string = '') { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOneAndUpdate({ roleId, localOrderID, state: { $ne: ORDER_STATE.RESULT_SUCCESS } }, + { $set: { state: ORDER_STATE.RESULT_SUCCESS, message } }, + { new: true }).lean(true); + return result; + } + + //查询订单详情 + public static async findOrderByProductID(productID: string, roleId: string, activityId: number) { + let result: ActivityTreasureHuntType[] = await ActivityTreasureHuntModel.find({ productID, roleId, activityId }).lean(true); + return result; + } + + //查询订单详情 + public static async findPlayerOrder(productID: string, roleId: string, activityId: number, limit: number) { + let result: ActivityTreasureHuntType[] = await ActivityTreasureHuntModel.find({ productID, roleId, activityId }, {}).limit(limit).sort({ createdAt: -1 }).lean(true); + return result; + } + + //查询订单详情 + public static async findOrderByActivityID(activityId: number, roleId: string,) { + let result: ActivityTreasureHuntType[] = await ActivityTreasureHuntModel.find({ activityId, roleId }).lean(true); + return result; + } + + //查询订单详情 + public static async findOrder(localOrderID: string) { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOne({ localOrderID }).lean(true); + return result; + } + + //新增订单 + public static async applyOrder(serverId: number, roleId: string, productID: string, localOrderID: string, orderID: string, price: number, payType: number, activityId: number, message: string = '') { + let result: ActivityTreasureHuntType = await ActivityTreasureHuntModel.findOneAndUpdate({ serverId, roleId, productID, localOrderID, orderID, payType, activityId }, + { $set: { price, state: ORDER_STATE.APPLY, message } }, + { upsert: true, new: true }).lean(true); + return result; + } +} + +export const ActivityTreasureHuntModel = getModelForClass(ActivityTreasureHunt); + +export interface ActivityTreasureHuntType extends Pick, keyof ActivityTreasureHunt> { } +export type ActivityTreasureHuntTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/db/ActivityTreasureHuntShop.ts b/shared/db/ActivityTreasureHuntShop.ts new file mode 100644 index 000000000..371dd7b9e --- /dev/null +++ b/shared/db/ActivityTreasureHuntShop.ts @@ -0,0 +1,26 @@ +import ActivityShop from './ActivityShop'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + + + + +/** + * 活动系统 - 寻宝猎人-商店 +*/ +@index({ roleId: 1 }) + +export default class Activity_Treasure_Hunt_Shop extends ActivityShop { + @prop({ required: true }) + dayIndex: number; // 第几天 + + //根据活动id查询活动数据 + public static async findTreasureData(serverId: number, activityId: number, roleId: string, roundIndex: number, dayIndex: number) { + let result: ActivityTreasureHuntShopModelType = await ActivityTreasureHuntShopModel.findOne({ roleId, activityId, roundIndex, dayIndex }).lean(true); + return result; + } +} + +export const ActivityTreasureHuntShopModel = getModelForClass(Activity_Treasure_Hunt_Shop); + +export interface ActivityTreasureHuntShopModelType extends Pick, keyof Activity_Treasure_Hunt_Shop> { } +export type ActivityTreasureHuntShopModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/db/ServerTemp.ts b/shared/db/ServerTemp.ts new file mode 100644 index 000000000..94ddc222a --- /dev/null +++ b/shared/db/ServerTemp.ts @@ -0,0 +1,45 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + +/** + * 数据变量记录 +*/ +@index({ activityId: 1 }) + +export default class ServerTemp extends BaseModel { + @prop({ required: true }) + serverId: number; // 服Id + + @prop({ required: true }) + huntActivityId: number; // 寻宝骑兵-活动Id + @prop({ required: true }) + huntBeginTime: Date; // 寻宝骑兵-开启时间 + @prop({ required: true }) + huntEndTime: Date; // 寻宝骑兵-结束时间 + @prop({ required: true }) + huntRoundIndex: number; // 寻宝骑兵-周期数 + + //更新寻宝猎人活动数据 + public static async updateTreasureHuntData(serverId: number, huntActivityId: number, huntBeginTime: Date, huntEndTime: Date, huntRoundIndex: number) { + let result: ServerTempModelType = await ServerTempModel.findOneAndUpdate({ serverId }, + { $set: { huntActivityId, huntBeginTime, huntEndTime, huntRoundIndex } }, { upsert: true, new: true }).lean(true); + return result; + } + + //查询当前服务器的数据 + public static async findData(serverId: number) { + let result: ServerTempModelType = await ServerTempModel.findOne({ serverId }).lean(true); + return result; + } + + //删除活动 + public static async deleteActivity(serverId: number) { + let result = await ServerTempModel.deleteMany({ serverId }); + return result; + } +} + +export const ServerTempModel = getModelForClass(ServerTemp); + +export interface ServerTempModelType extends Pick, keyof ServerTemp> { } +export type ServerTempModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/domain/activityField/limitShopField.ts b/shared/domain/activityField/limitShopField.ts index b47faf02c..6d104989f 100644 --- a/shared/domain/activityField/limitShopField.ts +++ b/shared/domain/activityField/limitShopField.ts @@ -9,6 +9,9 @@ export class ShopItem { reward: string; //任务奖励,格式:1&3&1(类型&id&数量) 类型定义:1.英雄,2.物品 countMax: number = 0; //可购买的最大次数,0表示不限制 name: string; //名字 + price: number; //价格 + productID: string; //商品id + imageName: string; buyCount: number = 0; //购买过的次数 @@ -17,6 +20,10 @@ export class ShopItem { this.reward = data.reward; this.countMax = data.countMax; this.name = data.name; + this.price = data.price; + this.productID = data.productID; + this.imageName = data.imageName; + this.buyCount = 0; } } diff --git a/shared/domain/activityField/treasureHuntField.ts b/shared/domain/activityField/treasureHuntField.ts new file mode 100644 index 000000000..3ea7c3636 --- /dev/null +++ b/shared/domain/activityField/treasureHuntField.ts @@ -0,0 +1,139 @@ +import { ActivityModelType } from '../../db/Activity'; +import { UserOrderModelType } from '../../db/UserOrder'; +import { ActivityBase } from './activityField'; + +/************************************************************/ +//购买价格 +export class ConsumeData { + count: number; // 第几次购买,1开始 + consume: string; //消耗资源 "2&31002&400" + + constructor(data: any) { + this.count = data.count; + this.consume = data.consume; + } +} + +// 商品的数据 +export class TreasureHuntShopItem { + cellIndex: number; // 第几个,从1开始 + name: string; //名称 + price: number; // 价格RMB 每次购买价格不变 + productID: string; // 商品id + reward: string; //奖励 + countMax: number; //最大购买次数 + imageName: string; + consume: ConsumeData[]; //每次购买价格不同 + + buyCount: number = 0; //购买过的次数 + + constructor(data: any) { + this.cellIndex = data.cellIndex; + this.name = data.name; + this.price = data.price; + this.productID = data.productID; + this.reward = data.reward; + this.countMax = data.countMax; + this.imageName = data.imageName; + this.consume = []; + for (let obj of data.data) { + this.consume.push(new ConsumeData(obj)) + } + this.buyCount = 0; + } +} + +// 商店数据 +export class TreasureHuntShopData { + name: string = '';//页签名字 + list: Array = [];//商品 + + public initData(data: any) { + this.name = data.name; + let arr = data.data; + for (let obj of arr) { + console.log(JSON.stringify(obj)) + this.list.push(new TreasureHuntShopItem(obj)) + } + } + + constructor(data: any) { + this.initData(data) + } +} + +/************************************************************/ + +// 寻宝备战的数据 +// export class TreasureHuntShopItem { +// cellIndex: number; // 第几个,从1开始 +// name: string; //名称 +// price: number; // 价格RMB 每次购买价格不变 +// productID: string; // 商品id +// reward: string; //奖励 +// countMax: number; //最大购买次数 +// imageName: string; +// consume: ConsumeData[]; //每次购买价格不同 + +// buyCount: number = 0; //购买过的次数 + +// constructor(data: any) { +// this.cellIndex = data.cellIndex; +// this.name = data.name; +// this.price = data.price; +// this.productID = data.productID; +// this.reward = data.reward; +// this.countMax = data.countMax; +// this.imageName = data.imageName; +// this.consume = []; +// for (let obj of data.imageName) { +// this.consume.push(new ConsumeData(obj)) +// } +// this.buyCount = 0; +// } +// } + +// // 寻宝备战数据 +// export class TreasureHuntShopData { +// name: string = '';//页签名字 +// index: number = 0;//下标 +// list: Array = [];//商品 + +// public initData(data: any) { +// this.name = data.name; +// this.index = data.index; +// let arr = data.data; +// for (let obj of arr) { +// this.list.push(new TreasureHuntShopItem(obj)) +// } +// } + +// constructor(data: any) { +// this.initData(data) +// } +// } + +/************************************************************/ + +// 商店数据 +export class TreasureHuntData extends ActivityBase { + name: string = '';//活动名字 + day: string = '';//活动持续时间 + roundIndex = 0;//周期数 + shop: TreasureHuntShopData = null; + + public initData(data: any) { + let dataObj = JSON.parse(data); + this.name = dataObj.name; + this.day = dataObj.day; + + let arr = dataObj.data; + this.shop = new TreasureHuntShopData(arr[0]); + + } + + constructor(activityData: ActivityModelType) { + super(activityData) + this.initData(activityData.data) + } +} \ No newline at end of file