diff --git a/game-server/app/servers/activity/handler/novemberHandler.ts b/game-server/app/servers/activity/handler/novemberHandler.ts new file mode 100644 index 000000000..671d59004 --- /dev/null +++ b/game-server/app/servers/activity/handler/novemberHandler.ts @@ -0,0 +1,150 @@ +import { Application, BackendSession, HandlerService, } from 'pinus'; +import { getPlayerNovemberData, getPlayerNovemberDataShow } from '../../../services/activity/novemberServices'; +import { genCode, resResult } from '../../../pubUtils/util'; +import { ITEM_CHANGE_REASON, STATUS } from '../../../consts'; +import { ActivityNovemberRecModel, NovemberRecord } from '../../../db/ActivityNovemberRec'; +import { RewardInter } from '../../../pubUtils/interface'; +import { addItems, handleCost } from '../../../services/role/rewardService'; +import { stringToConsumeParam, stringToRewardParam } from '../../../services/activity/giftPackageService'; +import { getZeroPoint } from '../../../pubUtils/timeUtil'; + + +export default function (app: Application) { + new HandlerService(app, {}); + return new NovemberHandler(app); +} + +export class NovemberHandler { + constructor(private app: Application) { + } + + async getData(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerNovemberDataShow(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } + + async gameStart(msg: { activityId: number, menuId: number }, session: BackendSession) { + const { activityId, menuId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerNovemberData(activityId, serverId, roleId); + if (!playerData || !playerData.menuMap) return resResult(STATUS.ACTIVITY_MISSING); + let menu = playerData.menuMap.get(menuId); + if (!menu) return resResult(STATUS.ACTIVITY_MISSING); + if (playerData.todayIndex < menu.dayIndex) return resResult(STATUS.ACTIVITY_NOVEMBER_MENU_LOCK); + + + // 检测挑战次数 + if (menu.playCnt >= menu.maxPlayCnt) return resResult(STATUS.ACTIVITY_NOVEMBER_NO_NUM); + + const gameCode = genCode(10); + await ActivityNovemberRecModel.records(serverId, activityId, playerData.roundIndex, roleId, [{ + todayIndex: playerData.todayIndex, gameCode, time: new Date(), rewards: '', isSuccess: false, isSkip: false, menuId, isPass: false + }]); + + return resResult(STATUS.SUCCESS, { activityId, gameCode }); + } + + async gameEnd(msg: { activityId: number, gameCode: string, isSuccess: boolean }, session: BackendSession) { + const { activityId, gameCode, isSuccess } = msg; + const roleId: string = session.get('roleId'); + const roleName: string = session.get('roleName'); + const serverId: number = session.get('serverId'); + const sid: string = session.get('sid'); + + + let playerData = await getPlayerNovemberData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + if (!playerData.records || playerData.records.length == 0) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMECODE_NOT_FOUND); + let record = playerData.records.find(cur => cur.gameCode == gameCode); + if (!record) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMECODE_NOT_FOUND); + if (record.isPass) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMECODE_USE); + + let menuId = record.menuId; + let menu = playerData.menuMap.get(menuId); + if (!menu) return resResult(STATUS.ACTIVITY_MISSING); + + if (menu.playCnt >= menu.maxPlayCnt) return resResult(STATUS.ACTIVITY_NOVEMBER_NO_NUM); + + let rewards: string = menu.successRewards; + if (!isSuccess) rewards = menu.failRewards; + let playerRecord = await ActivityNovemberRecModel.gameEnd(serverId, activityId, playerData.roundIndex, roleId, gameCode, isSuccess, new Date(), rewards, true); + if (!playerRecord) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMECODE_NOT_FOUND); + playerData = await getPlayerNovemberData(activityId, serverId, roleId); + menu = playerData.menuMap.get(menuId); + + let goods: RewardInter[] = await addItems(roleId, roleName, sid, stringToRewardParam(rewards), ITEM_CHANGE_REASON.NOVEMBER_REWARD); + + return resResult(STATUS.SUCCESS, { activityId, menuId, playCnt: menu.playCnt, maxPlayCnt: menu.maxPlayCnt, goods }); + } + + async buyCnt(msg: { activityId: number, count: number, menuId: number }, session: BackendSession) { + const { activityId, count, menuId } = msg; + const roleId: string = session.get('roleId'); + const roleName: string = session.get('roleName'); + const serverId: number = session.get('serverId'); + const sid: string = session.get('sid'); + + let playerData = await getPlayerNovemberData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + let menu = playerData.menuMap.get(menuId); + if (!menu) return resResult(STATUS.ACTIVITY_MISSING); + if (playerData.todayIndex < menu.dayIndex) return resResult(STATUS.ACTIVITY_NOVEMBER_MENU_LOCK); + + if (menu.buyCnt + count > menu.maxBuyCnt) return resResult(STATUS.ACTIVITY_NOVEMBER_BUY_COUNT_OVER); + + // 扣材料 + let costResult = await handleCost(roleId, sid, stringToConsumeParam(menu.buyCost), ITEM_CHANGE_REASON.NOVEMBER_COST); + if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + + let buyResult = await ActivityNovemberRecModel.buyCnt(serverId, activityId, playerData.roundIndex, roleId, { menuId, buyCnt: count }, menu.isPushBuyRec); + if (!buyResult) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMECODE_NOT_FOUND); + + playerData = await getPlayerNovemberData(activityId, serverId, roleId); + menu = playerData.menuMap.get(menuId); + + return resResult(STATUS.SUCCESS, { activityId, menuId, buyCnt: menu.buyCnt, maxBuyCnt: menu.maxBuyCnt }); + } + + async gameSweep(msg: { activityId: number, count: number, menuId: number }, session: BackendSession) { + const { activityId, count, menuId } = msg; + const roleId: string = session.get('roleId'); + const roleName: string = session.get('roleName'); + const serverId: number = session.get('serverId'); + const sid: string = session.get('sid'); + + let playerData = await getPlayerNovemberData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + let menu = playerData.menuMap.get(menuId); + if (!menu) return resResult(STATUS.ACTIVITY_MISSING); + if (playerData.todayIndex < menu.dayIndex) return resResult(STATUS.ACTIVITY_NOVEMBER_MENU_LOCK); + + if (menu.playCnt + count > menu.maxPlayCnt) return resResult(STATUS.ACTIVITY_NOVEMBER_NO_NUM); + let isHasSuccess = playerData.records.find(cur => cur.isSuccess && !cur.isSkip && (getZeroPoint() * 1000 <= cur.time.getTime())); + if (!isHasSuccess) return resResult(STATUS.ACTIVITY_NOVEMBER_GAMESWEEP_LOCK); + + let rewards: RewardInter[] = []; + let records: NovemberRecord[] = []; + for (let i = 0; i < count; i++) { + records.push({ todayIndex: playerData.todayIndex, gameCode: 'sweep', time: new Date(), rewards: menu.successRewards, isSuccess: true, isSkip: true, menuId, isPass: true }) + rewards.push(...stringToRewardParam(menu.successRewards)); + } + await ActivityNovemberRecModel.records(serverId, activityId, playerData.roundIndex, roleId, records); + + let goods = await addItems(roleId, roleName, sid, rewards, ITEM_CHANGE_REASON.NOVEMBER_REWARD) + + playerData = await getPlayerNovemberData(activityId, serverId, roleId); + menu = playerData.menuMap.get(menuId); + + + return resResult(STATUS.SUCCESS, { activityId, menuId, playCnt: menu.playCnt, maxPlayCnt: menu.maxPlayCnt, goods }); + } +} \ No newline at end of file diff --git a/game-server/app/services/activity/activityService.ts b/game-server/app/services/activity/activityService.ts index 173255fc2..51a504451 100644 --- a/game-server/app/services/activity/activityService.ts +++ b/game-server/app/services/activity/activityService.ts @@ -53,6 +53,7 @@ import { getPlayerQixiDataShow } from './qixiService'; import { getPlayerMidAutumnDataShow } from './midAutumnService'; import { getPlayerAuthorGachaDataShow } from './authorGachaService'; import { getPlayerChongYangDataShow } from './chongyangService'; +import { getPlayerNovemberDataShow } from './novemberServices'; /** * 获取活动数据 @@ -308,6 +309,11 @@ export async function getActivity(serverId: number, roleId: string, uid: number, activityData = await getPlayerChongYangDataShow(activityId, serverId, roleId); break; } + case ACTIVITY_TYPE.NOVEMBER: + { + activityData = await getPlayerNovemberDataShow(activityId, serverId, roleId); + break; + } default: { console.log('未知活动类型.........', activityType) break; diff --git a/game-server/app/services/activity/novemberServices.ts b/game-server/app/services/activity/novemberServices.ts new file mode 100644 index 000000000..31ac3b0c8 --- /dev/null +++ b/game-server/app/services/activity/novemberServices.ts @@ -0,0 +1,23 @@ +import { ActivityNovemberRecModel } from "../../db/ActivityNovemberRec"; +import { NovemberData } from "../../domain/activityField/novemberField"; +import { getRoleCreateTime, getServerCreateTime } from "../redisService"; +import { getActivityById } from "./activityService"; + +export async function getPlayerNovemberData(activityId: number, serverId: number, roleId: string) { + let activityData = await getActivityById(activityId); + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new NovemberData(activityData, createTime, serverTime); + let playerRecord = await ActivityNovemberRecModel.findData(serverId, activityId, playerData.roundIndex, roleId); + playerData.setPlayerRecords(playerRecord); + return playerData; +} + + +export async function getPlayerNovemberDataShow(activityId: number, serverId: number, roleId: string) { + let playerData = await getPlayerNovemberData(activityId, serverId, roleId); + if (playerData && playerData.canShow && playerData.canShow()) { + return playerData.getShowResult(); + } + return null +} \ No newline at end of file diff --git a/game-server/app/services/checkParam.ts b/game-server/app/services/checkParam.ts index a54f9debb..2f079acaa 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -2328,6 +2328,33 @@ export function checkRouteParam(route: string, msg: any) { if (!checkNaturalNumbers(msg.activityId, msg.dayIndex, msg.count)) return false; break; } + case 'activity.novemberHandler.getData': + { + if (!checkNaturalNumbers(msg.activityId)) return false; + break; + } + case 'activity.novemberHandler.gameStart': + { + if (!checkNaturalNumbers(msg.activityId, msg.menuId)) return false; + break; + } + case 'activity.novemberHandler.gameEnd': + { + if (!checkNaturalNumbers(msg.activityId)) return false; + if (!checkNaturalStrings(msg.gameCode)) return false; + if (!checkBoolean(msg.isSuccess)) return false; + break; + } + case 'activity.novemberHandler.buyCnt': + { + if (!checkNaturalNumbers(msg.activityId, msg.count, msg.menuId)) return false; + break; + } + case 'activity.novemberHandler.gameSweep': + { + if (!checkNaturalNumbers(msg.activityId, msg.count, msg.menuId)) return false; + break; + } case 'activity.activityHandler.debugActivityMemory': case 'activity.popUpShopHandler.debugPushPopUpShop': case 'activity.popUpShopHandler.debugPushPopUpInterval': diff --git a/shared/consts/constModules/activityConst.ts b/shared/consts/constModules/activityConst.ts index 78c4a9309..41448c108 100644 --- a/shared/consts/constModules/activityConst.ts +++ b/shared/consts/constModules/activityConst.ts @@ -77,6 +77,7 @@ export enum ACTIVITY_TYPE { GROWTH_FUND_AUTHOR = 62, //列传普通密卷 GROWTH_FUND_AUTHOR_VIP = 63, //列传高级密卷 CHONGYANG = 64, //重阳集会 + NOVEMBER = 65, // 辜月集会 } /** diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 9b5d47ec2..83f6d87bb 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1279,8 +1279,10 @@ export enum ITEM_CHANGE_REASON { RECEIVE_COLLECT_REWARD = 202, // 领取图鉴奖励 RECEIVE_ROUGE_SCORE_REWARD = 203, // 领取积分奖励 REBORN_COST = 204, // 重生优化,传承 - CHONGYANG_REWARD = 205, // 重阳集会 + CHONGYANG_REWARD = 205, // 重阳集会奖励 CHONGYANG_COST = 206, // 重阳集会购买消耗 + NOVEMBER_REWARD = 207, // 辜月集会奖励 + NOVEMBER_COST = 208, // 辜月集会购买消耗 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index b9799833a..d3655c30b 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -713,6 +713,12 @@ export const STATUS = { ACTIVITY_CHONGYANG_BUY_COUNT_OVER: { code: 50087, simStr: '购买次数不足' }, ACTIVITY_CHONGYANG_GAMECODE_USE: { code: 50088, simStr: '游戏已经结算过' }, ACTIVITY_CHONGYANG_GAMESWEEP_LOCK: { code: 50088, simStr: '纯净召唤未开启' }, + ACTIVITY_NOVEMBER_NO_NUM: { code: 50089, simStr: '游戏次数不足' }, + ACTIVITY_NOVEMBER_GAMECODE_NOT_FOUND: { code: 50090, simStr: '未找到该id' }, + ACTIVITY_NOVEMBER_GAMECODE_USE: { code: 50091, simStr: '游戏已经结算过' }, + ACTIVITY_NOVEMBER_BUY_COUNT_OVER: { code: 50092, simStr: '购买次数不足' }, + ACTIVITY_NOVEMBER_GAMESWEEP_LOCK: { code: 50093, simStr: '纯净召唤未开启' }, + ACTIVITY_NOVEMBER_MENU_LOCK: { code: 50094, simStr: '菜谱未解锁' }, diff --git a/shared/db/ActivityNovemberRec.ts b/shared/db/ActivityNovemberRec.ts new file mode 100644 index 000000000..931a4eaf8 --- /dev/null +++ b/shared/db/ActivityNovemberRec.ts @@ -0,0 +1,79 @@ +import { getModelForClass, index, prop, DocumentType } from "@typegoose/typegoose"; +import BaseModel from "./BaseModel"; + +@index({ roleId: 1, activityId: 1, serverId: 1, roundIndex: 1 }) + + +export class NovemberRecord { + @prop({ required: true }) + todayIndex: number; // 今天index + @prop({ required: true }) + gameCode: string; // 唯一挑战id + @prop({ required: true }) + time: Date; // 时间 + @prop({ required: true }) + rewards: string; // 奖励 + @prop({ required: true }) + isSuccess: boolean; // 是否制作成功 + @prop({ required: true }) + isSkip: boolean // 是否纯净召唤 + @prop({ required: true }) + menuId: number //菜单id + @prop({ required: true }) + isPass: boolean // gameEnd后置为true +} + +export class NovemberBuyRec { + @prop({ required: true }) + menuId: number //菜单id + + @prop({ required: true }) + buyCnt: number //购买次数 +} +export default class Activity_November_Rec extends BaseModel { + @prop({ required: true }) + serverId: number; // 服Id + + @prop({ required: true }) + activityId: number; // 活动Id + + @prop({ required: true }) + roundIndex: number; // 第几轮 + + @prop({ required: true }) + roleId: string; // 用户Id + + @prop({ required: true, type: NovemberBuyRec, default: [], _id: false }) + buyRec: NovemberBuyRec[] // 购买 + + @prop({ required: true, type: NovemberRecord, _id: false }) + records: NovemberRecord[]; // 记录 + + public static async findData(serverId: number, activityId: number, roundIndex: number, roleId: string) { + let result: ActivityNovemberRecModelType = await ActivityNovemberRecModel.findOne({ serverId, roleId, activityId, roundIndex }).lean(); + return result; + } + + public static async records(serverId: number, activityId: number, roundIndex: number, roleId: string, records: NovemberRecord[]) { + let result: ActivityNovemberRecModelType = await ActivityNovemberRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex }, { $push: { records: { $each: records } }, $setOnInsert: { buyRec: [] } }, { new: true, upsert: true }).lean(); + return result; + } + + public static async gameEnd(serverId: number, activityId: number, roundIndex: number, roleId: string, gameCode: string, isSuccess: boolean, time: Date, rewards: string, isPass: boolean) { + let result: ActivityNovemberRecModelType = await ActivityNovemberRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'records.gameCode': gameCode }, + { $set: { 'records.$.time': time, 'records.$.isSuccess': isSuccess, 'records.$.rewards': rewards, 'records.$.isPass': isPass } }, { new: true }).lean(); + return result; + } + + public static async buyCnt(serverId: number, activityId: number, roundIndex: number, roleId: string, buyRec: NovemberBuyRec, isPushBuyRec: boolean) { + let result: ActivityNovemberRecModelType; + if (isPushBuyRec) result = await ActivityNovemberRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex }, { $push: { buyRec } }, { new: true, upsert: true }).lean(); + else result = await ActivityNovemberRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'buyRec.menuId': buyRec.menuId }, { $inc: { 'buyRec.buyCnt': buyRec.buyCnt }, $setOnInsert: { records: [] } }, { new: true, upsert: true }).lean(); + return result; + } +} + +export const ActivityNovemberRecModel = getModelForClass(Activity_November_Rec); + +export interface ActivityNovemberRecModelType extends Pick, keyof Activity_November_Rec> { } +export type ActivityNovemberRecModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/domain/activityField/novemberField.ts b/shared/domain/activityField/novemberField.ts new file mode 100644 index 000000000..52ba5364b --- /dev/null +++ b/shared/domain/activityField/novemberField.ts @@ -0,0 +1,91 @@ +// 节日活动 - 重阳集会 +import { ActivityModelType } from '../../db/Activity'; +import { ActivityNovemberRecModelType, NovemberRecord, } from '../../db/ActivityNovemberRec'; +import { ActivityBase } from './activityField'; + +// 后台格式 +interface NovemberDataInDb { + dayIndex: number; // 天数 + menuId: number; // 菜谱id + buyCost: string; // 对应菜谱购买一次次数的消耗,type&id&count + dailyBuyCnt: number; // 对应菜谱每天可以购买的次数 + dailyFreeCnt: number; // 对应菜谱每天可以免费的次数 + successRewards: string;// 对应菜谱每局的成功奖励 type&id&count + failRewards: string; // 对应菜谱每局的失败安慰奖励 type&id&count +}; + + +interface NovemberDataReturn { + dayIndex: number; // 第几天 + menuId: number; // 菜谱id + buyCost: string; // 对应菜谱购买一次的消耗,type&id&count + buyCnt: number; // 对应菜谱累积到现在已经购买了的次数 + maxBuyCnt: number; // 对应菜谱累积到现在可购买的次数,buyCnt(); + + constructor(activityData: ActivityModelType, createTime: number, serverTime: number) { + super(activityData, createTime, serverTime) + this.initData(activityData.data) + } + + public initData(data: string): void { + let novemberData: NovemberDataInDb[] = JSON.parse(data); + if (!novemberData || novemberData.length == 0) return; + for (const obj of novemberData) { + let buyCost = obj?.buyCost || '&'; + let buyCnt = 0; + let maxBuyCnt = this.todayIndex * (obj?.dailyBuyCnt || 0); + let playCnt = 0; + let maxPlayCnt = this.todayIndex * (obj?.dailyFreeCnt || 0); + let successRewards = obj?.successRewards || '&'; + let failRewards = obj?.failRewards || '&' + this.menuMap.set(obj.menuId, { dayIndex: obj.dayIndex, menuId: obj.menuId, buyCost, buyCnt, maxBuyCnt, playCnt, maxPlayCnt, successRewards, failRewards, isPushBuyRec: true }) + } + + } + + public setPlayerRecords(playerData: ActivityNovemberRecModelType) { + this.updatePlayerRecord(playerData); + } + + public updatePlayerRecord(playerData: ActivityNovemberRecModelType) { + if (!playerData) return; + if (playerData.records) { + this.records = playerData.records; + for (const { menuId, isPass } of playerData.records) { + if (!isPass) continue; + let menu = this.menuMap.get(menuId); + menu.playCnt += 1; + this.menuMap.set(menuId, menu); + + } + } + if (playerData.buyRec) { + for (const { menuId, buyCnt } of playerData.buyRec) { + let menu = this.menuMap.get(menuId); + menu.buyCnt += buyCnt; + menu.maxPlayCnt += buyCnt; + menu.isPushBuyRec = false; + this.menuMap.set(menuId, menu); + } + } + } + + public getShowResult() { + return { + ...this.getBaseKeys(), + menus: [...this.menuMap.values()] + } + } +} \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_activityType.json b/shared/resource/jsons/dic_zyz_activityType.json index a632adb97..c57cc4dcf 100644 --- a/shared/resource/jsons/dic_zyz_activityType.json +++ b/shared/resource/jsons/dic_zyz_activityType.json @@ -358,5 +358,11 @@ "activityType": 64, "name": "MID_AUTUMN", "string": "重阳集会活动" + }, + { + "id": 65, + "activityType": 65, + "name": "MID_AUTUMN", + "string": "辜月集会活动" } ] \ No newline at end of file