diff --git a/game-server/app/servers/activity/handler/qixiHandler.ts b/game-server/app/servers/activity/handler/qixiHandler.ts new file mode 100644 index 000000000..d29722fc7 --- /dev/null +++ b/game-server/app/servers/activity/handler/qixiHandler.ts @@ -0,0 +1,148 @@ +import { Application, BackendSession, HandlerService, } from 'pinus'; +import { genCode, getRandSingleEelm, parseNumberList, resResult } from '../../../pubUtils/util'; +import { ITEM_CHANGE_REASON, STATUS } from '../../../consts'; +import { addReward, stringToConsumeParam, stringToRewardParam } from '../../../services/activity/giftPackageService'; +import { addItems, handleCost } from '../../../services/role/rewardService'; +import { getPlayerQixiData, getPlayerQixiDataShow } from '../../../services/activity/qixiService'; +import { ActivityQixiRecModel } from '../../../db/ActivityQixiRec'; +import { RewardInter } from '../../../pubUtils/interface'; + +export default function (app: Application) { + new HandlerService(app, {}); + return new EntertainHandler(app); +} + +export class EntertainHandler { + constructor(private app: Application) { + } + + /** + * @description 获取火神祭祀活动数据 + * @param {{ activityId: number}} msg + * @param {BackendSession} session + * @memberof EntertainHandler + */ + async getData(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerQixiDataShow(activityId, serverId, roleId); + + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } + + /** + * @description 召唤鹊桥游戏 + * @param {{ activityId: number }} msg + * @param {BackendSession} session + * @memberof EntertainHandler + */ + async gameStart(msg: { activityId: number, progress: number }, session: BackendSession) { + const { activityId, progress } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerQixiData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + // 挑战次数 + if(playerData.progress != progress) return resResult(STATUS.ACTIVITY_QIXI_PROGRESS_NOT_FIT); + if(playerData.playCnt >= playerData.freeCnt + playerData.buyCnt) return resResult(STATUS.ACTIVITY_QIXI_NO_NUM); + + const gameCode = genCode(10); + let playerRecord = await ActivityQixiRecModel.record(serverId, activityId, playerData.roundIndex, roleId, { + todayIndex: playerData.recordTodayIndex, gameCode, time: new Date(), rewards: '', progress, afterProgress: progress, hasPass: false + }); + playerData.updatePlayerRecord(playerRecord); + + return resResult(STATUS.SUCCESS, { + activityId, + todayPlayCnt: playerData.todayPlayCnt, + playCnt: playerData.playCnt, + freeCnt: playerData.freeCnt, + gameCode, + progress: playerData.progress + }); + } + + + /** + * @description 召唤鹊桥游戏 + * @param {{ activityId: number }} msg + * @param {BackendSession} session + * @memberof EntertainHandler + */ + async gameEnd(msg: { activityId: number, gameCode: string, progress: number, addProgress: number }, session: BackendSession) { + const { activityId, gameCode, progress, addProgress } = 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 getPlayerQixiData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + // 挑战次数 + if(playerData.progress != progress) return resResult(STATUS.ACTIVITY_QIXI_PROGRESS_NOT_FIT); + if(playerData.playCnt >= playerData.freeCnt + playerData.buyCnt) return resResult(STATUS.ACTIVITY_QIXI_NO_NUM); + + let curRecord = playerData.records.find(cur => cur.gameCode == gameCode); + if(!curRecord) return resResult(STATUS.ACTIVITY_QIXI_GAMECODE_NOT_FOUND); + if(curRecord.hasPass) return resResult(STATUS.ACTIVITY_QIXI_GAMECODE_HAS_PLAY); + + let playerRecord = await ActivityQixiRecModel.addProgress(serverId, activityId, playerData.roundIndex, roleId, gameCode, addProgress, new Date()); + if(!playerRecord) return resResult(STATUS.ACTIVITY_QIXI_GAMECODE_NOT_FOUND); + playerData.updatePlayerRecord(playerRecord); + let goods: RewardInter[] = []; + if(playerData.progress >= playerData.maxProgress) { + await ActivityQixiRecModel.recordReward(serverId, activityId, playerData.roundIndex, roleId, gameCode, playerData.rewards); + let rewards = stringToRewardParam(playerData.rewards); + goods = await addItems(roleId, roleName, sid, rewards, ITEM_CHANGE_REASON.QIXI_REWARD) + } + + return resResult(STATUS.SUCCESS, { + activityId, + todayPlayCnt: playerData.todayPlayCnt, + playCnt: playerData.playCnt, + freeCnt: playerData.freeCnt, + gameCode, + progress: playerData.progress, + goods + }); + } + + /** + * @description 购买次数 + * @param {{ activityId: number, id: number, count: number}} msg + * @param {BackendSession} session + * @memberof EntertainHandler + */ + async buyCnt(msg: { activityId: number, count: number }, session: BackendSession) { + const { activityId, count } = msg; + const roleId = session.get('roleId'); + const sid = session.get('sid'); + const serverId = session.get('serverId'); + + let playerData = await getPlayerQixiData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + // 可购买次数 + if(playerData.buyCnt + count > playerData.maxBuyCnt) return resResult(STATUS.ACTIVITY_QIXI_BUY_COUNT_OVER); + if(playerData.playCnt < playerData.freeCnt) return resResult(STATUS.ACTIVITY_QIXI_CANNOT_BUY); + // 扣材料 + let costResult = await handleCost(roleId, sid, stringToConsumeParam(playerData.buyCost), ITEM_CHANGE_REASON.ACT_ENTERTAIN_BUY_COST); + if(!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + + // 保存数据 + let buildResult = await ActivityQixiRecModel.buyCnt(serverId, activityId, playerData.roundIndex, roleId, count); + // // 更新数据 + playerData.updatePlayerRecord(buildResult); + + return resResult(STATUS.SUCCESS, { + activityId, + maxBuyCnt: playerData.maxBuyCnt, + buyCnt: playerData.buyCnt + }); + } +} diff --git a/game-server/app/services/activity/activityService.ts b/game-server/app/services/activity/activityService.ts index ecdf37ff4..d958e95f9 100644 --- a/game-server/app/services/activity/activityService.ts +++ b/game-server/app/services/activity/activityService.ts @@ -49,6 +49,7 @@ import { getPlayerRebateDataShow } from './rebateService'; import { getWebviewDataShow } from './webviewService'; import { getPlayerDragonBoatDataShow } from './dragonBoatService'; import { getPlayerEntertainDataShow } from './entertainService'; +import { getPlayerQixiDataShow } from './qixiService'; /** * 获取活动数据 @@ -280,6 +281,11 @@ export async function getActivity(serverId: number, roleId: string, uid: number, activityData = await getPlayerEntertainDataShow(activityId, serverId, roleId); break } + case ACTIVITY_TYPE.QIXI: + { + activityData = await getPlayerQixiDataShow(activityId, serverId, roleId); + break; + } default: { console.log('未知活动类型.........', activityType) break; diff --git a/game-server/app/services/activity/qixiService.ts b/game-server/app/services/activity/qixiService.ts new file mode 100644 index 000000000..5519235a3 --- /dev/null +++ b/game-server/app/services/activity/qixiService.ts @@ -0,0 +1,38 @@ +import { ActivityQixiRecModel } from "../../db/ActivityQixiRec"; +import { QixiData } from "../../domain/activityField/qixiField"; +import { getRoleCreateTime, getServerCreateTime } from "../redisService"; +import { getActivityById } from "./activityService"; + +/** + * 玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getPlayerQixiData(activityId: number, serverId: number, roleId: string) { + let activityData = await getActivityById(activityId); + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new QixiData(activityData, createTime, serverTime); + let playerRecord = await ActivityQixiRecModel.findData(serverId, activityId, playerData.roundIndex, roleId); + playerData.setPlayerRecords(playerRecord); + return playerData; +} + +/** + * 玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getPlayerQixiDataShow(activityId: number, serverId: number, roleId: string) { + let playerData = await getPlayerQixiData(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 e3c39efd8..2484aa347 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -129,6 +129,7 @@ export function checkRouteParam(route: string, msg: any) { case 'activity.bindPhoneHandler.receiveGiftCode': case 'activity.bindPhoneHandler.skipOutSide': case 'activity.dragonBoatHandler.getData': + case 'activity.qixiHandler.getData': { if (!checkNaturalNumbers(msg.activityId)) return false; break; @@ -2052,6 +2053,22 @@ export function checkRouteParam(route: string, msg: any) { if (!checkNaturalNumbers(msg.activityId, msg.count)) return false; break; } + case 'activity.qixiHandler.gameStart': + { + if (!checkNaturalNumbers(msg.activityId, msg.progress)) return false; + break; + } + case 'activity.qixiHandler.gameEnd': + { + if (!checkNaturalNumbers(msg.activityId, msg.progress, msg.addProgress)) return false; + if (!checkNaturalStrings(msg.gameCode)) return false; + break; + } + case 'activity.qixiHandler.buyCnt': + { + if (!checkNaturalNumbers(msg.activityId, msg.count)) return false; + break; + } case 'activity.activityHandler.debugActivityMemory': case 'activity.popUpShopHandler.debugPushPopUpShop': case 'activity.popUpShopHandler.debugPushPopUpInterval': diff --git a/game-server/app/services/redisService.ts b/game-server/app/services/redisService.ts index f5d214706..847ffc85e 100644 --- a/game-server/app/services/redisService.ts +++ b/game-server/app/services/redisService.ts @@ -175,6 +175,7 @@ export async function findKeys(key: string) { return keys } + /********排行榜结束 */ diff --git a/shared/consts/constModules/activityConst.ts b/shared/consts/constModules/activityConst.ts index bfee7912b..6c826ab09 100644 --- a/shared/consts/constModules/activityConst.ts +++ b/shared/consts/constModules/activityConst.ts @@ -69,6 +69,7 @@ export enum ACTIVITY_TYPE { WEBVIEW = 54, // 平台活动页面 DRAGON_BOAT = 55, // 龙舟 ENTERTAIN = 56, // 宴请百家 + QIXI = 57, // 七夕活动 } /** diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index e8b9fcefe..024cc3c40 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1205,7 +1205,8 @@ export enum ITEM_CHANGE_REASON { AUTHOR_BOOK_STAR_RETURN = 191, // 诸子列传升星并发返回 AUTHOR_BOOK_SUB_RESET = 192, // 重置列传 DECOMPOSE_SPIRIT = 193, // 分解英灵 - BUY_SPIRIT = 194, // 分解英灵 + BUY_SPIRIT = 194, // 购买英灵 + QIXI_REWARD = 195, // 七夕活动奖励 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 324d57e5b..8aa0e6050 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -694,6 +694,13 @@ export const STATUS = { ACTIVITY_DRAGON_BOAT_CANNOT_BUY: { code: 50073, simStr: '不可在免费次数未用完的时候购买次数' }, ACTIVITY_ENTERTAIN_NO_NUM: { code: 50074, simStr: '宴请武将次数已满' }, ACTIVITY_ENTERTAIN_BUY_COUNT_OVER: { code: 50075, simStr: '购买次数不足' }, + ACTIVITY_QIXI_NO_NUM: { code: 50076, simStr: '召唤喜鹊次数不足' }, + ACTIVITY_QIXI_PROGRESS_NOT_FIT: { code: 50077, simStr: '进度数值不匹配' }, + ACTIVITY_QIXI_GAMECODE_NOT_FOUND: { code: 50078, simStr: '未找到该id' }, + ACTIVITY_QIXI_GAMECODE_HAS_PLAY: { code: 50079, simStr: '该挑战已结算过' }, + ACTIVITY_QIXI_BUY_COUNT_OVER: { code: 50080, simStr: '购买挑战次数不足' }, + ACTIVITY_QIXI_CANNOT_BUY: { code: 50081, simStr: '不可在免费次数未用完的时候购买次数' }, + // GM后台相关状态 60000 - 69999 GM_ERR_PASSWORD: { code: 60001, simStr: '账号或密码错误' }, diff --git a/shared/db/ActivityQixiRec.ts b/shared/db/ActivityQixiRec.ts new file mode 100644 index 000000000..9d841ba70 --- /dev/null +++ b/shared/db/ActivityQixiRec.ts @@ -0,0 +1,75 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + +/** + * 七夕雀巢 +*/ + +export class QixiRecord { + @prop({ required: true }) + todayIndex: number; // 今天index + @prop({ required: true }) + gameCode: string; // 唯一挑战id + @prop({ required: true }) + progress: number; // 到哪个进度 + @prop({ required: true }) + afterProgress: number; // 到哪个进度 + @prop({ required: true }) + time: Date; // 时间 + @prop({ required: true }) + rewards: string; // 奖励 + @prop({ required: true }) + hasPass: boolean; // 奖励 +} + +@index({ roleId: 1, activityId: 1 }) + +export default class Activity_Qixi_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 }) + buyCnt: number; // 购买次数 + + @prop({ required: true, type: QixiRecord, _id: false }) + record: QixiRecord[]; // 记录 + + public static async findData(serverId: number, activityId: number, roundIndex: number, roleId: string) { + let result: ActivityQixiRecModelType = await ActivityQixiRecModel.findOne({ serverId, roleId, activityId, roundIndex }).lean(); + return result; + } + + public static async record(serverId: number, activityId: number, roundIndex: number, roleId: string, record: QixiRecord) { + let result: ActivityQixiRecModelType = await ActivityQixiRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex }, { $push: { record }, $setOnInsert: { buyCnt: 0 } }, { new: true, upsert: true }).lean(); + return result; + } + + public static async addProgress(serverId: number, activityId: number, roundIndex: number, roleId: string, gameCode: string, addProgress: number, time: Date) { + let result: ActivityQixiRecModelType = await ActivityQixiRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'record.gameCode': gameCode }, { $inc: { 'record.$.afterProgress': addProgress }, $set: { 'record.$.time': time, 'record.$.hasPass': true } }, { new: true }).lean(); + return result; + } + + public static async recordReward(serverId: number, activityId: number, roundIndex: number, roleId: string, gameCode: string, rewards: string) { + let result: ActivityQixiRecModelType = await ActivityQixiRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, 'record.gameCode': gameCode }, { $set: { 'record.$.rewards': rewards } }, { new: true }).lean(); + return result; + } + + public static async buyCnt(serverId: number, activityId: number, roundIndex: number, roleId: string, count: number) { + let result: ActivityQixiRecModelType = await ActivityQixiRecModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex }, { $inc: { buyCnt: count }, $setOnInsert: { record: [] } }, { new: true, upsert: true }).lean(); + return result; + } +} + +export const ActivityQixiRecModel = getModelForClass(Activity_Qixi_Rec); + +export interface ActivityQixiRecModelType extends Pick, keyof Activity_Qixi_Rec> { } +export type ActivityQixiRecModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/domain/activityField/qixiField.ts b/shared/domain/activityField/qixiField.ts new file mode 100644 index 000000000..a3a841504 --- /dev/null +++ b/shared/domain/activityField/qixiField.ts @@ -0,0 +1,98 @@ +// 节日活动 - 七夕鹊桥 +import { ActivityModelType } from '../../db/Activity'; +import { ActivityQixiRecModelType, QixiRecord } from '../../db/ActivityQixiRec'; +import { ActivityBase } from './activityField'; + +// 后台格式 + +interface QixiDataInDb { + buyCost: string; // 购买一次宴请次数的消耗,type&id&count + dailyBuyCnt: number; // 每天可以购买的次数 + freeCnt: number; // 每天可以免费划船的次数 + maxProgress: number; // 最大进度 + rewards: string; // 奖励 +} + +export class QixiData extends ActivityBase { + buyCost: string; // 购买一次次数的消耗,type&id&count + dailyBuyCnt: number; // 每天可以购买的次数 + freeCntDaily: number; // 每天可以免费的次数 + maxProgress: number; // 最大进度 + rewards: string; // 奖励 + + freeCnt: number = 0; // 累积到现在可以免费的次数 + maxBuyCnt: number = 0; // 累积到现在可以购买的次数 + buyCnt: number = 0; // 累积到现在已经购买了的次数 + todayPlayCnt: number = 0; // 今天玩的次数 + playCnt: number = 0; // 总玩的次数 + progress: number = 0; // 当前的进度条 + + recordTodayIndex: number = 0; + records: QixiRecord[] = []; + + constructor(activityData: ActivityModelType, createTime: number, serverTime: number) { + super(activityData, createTime, serverTime) + this.initData(activityData.data) + } + + public initData(data: string): void { + let dataObj: QixiDataInDb = JSON.parse(data); + if (!dataObj) return; + + this.buyCost = dataObj.buyCost || '&'; + this.dailyBuyCnt = dataObj.dailyBuyCnt || 0; + this.freeCntDaily = dataObj.freeCnt || 0; + this.maxProgress = dataObj.maxProgress || 0; + this.rewards = dataObj.rewards || '&'; + this.freeCnt = this.freeCntDaily * this.todayIndex; + this.maxBuyCnt = this.todayIndex * this.dailyBuyCnt; + this.recordTodayIndex = this.todayIndex; + } + + public setPlayerRecords(playerData: ActivityQixiRecModelType) { + this.updatePlayerRecord(playerData); + if(this.progress >= this.maxProgress) this.progress = 0; + } + + public updatePlayerRecord(playerData: ActivityQixiRecModelType) { + if (!playerData) return; + this.buyCnt = playerData.buyCnt || 0; + this.todayPlayCnt = 0; + this.playCnt = 0; + this.recordTodayIndex = this.todayIndex; + if(playerData.record) { + playerData.record.sort((a, b) => a.time.getTime() - b.time.getTime()); + for(let data of playerData.record) { + let { todayIndex, afterProgress, progress, hasPass } = data; + if(todayIndex == this.todayIndex && hasPass) this.todayPlayCnt ++; + if(hasPass) this.playCnt ++; + this.progress = afterProgress||progress; + this.records.push(data); + } + if(this.todayPlayCnt >= this.freeCntDaily && this.playCnt < this.freeCnt) { + for(let i = 1; i <= this.todayIndex; i++) { + let times = playerData.record.filter(cur => cur.todayIndex == i && cur.hasPass).length; + if(times < this.freeCntDaily) { + this.recordTodayIndex = i; break; + } + } + } + } + } + + public getShowResult() { + return { + ...this.getBaseKeys(), + buyCost: this.buyCost, + dailyBuyCnt: this.dailyBuyCnt, + freeCnt: this.freeCnt, + maxBuyCnt: this.maxBuyCnt, + buyCnt: this.buyCnt, + todayPlayCnt: this.todayPlayCnt, + playCnt: this.playCnt, + maxProgress: this.maxProgress, + progress: this.progress, + rewards: this.rewards, + } + } +} \ 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 1c97d0033..2c6c879f5 100644 --- a/shared/resource/jsons/dic_zyz_activityType.json +++ b/shared/resource/jsons/dic_zyz_activityType.json @@ -310,5 +310,11 @@ "activityType": 56, "name": "ENTERTAIN", "string": "宴请百家" + }, + { + "id": 57, + "activityType": 57, + "name": "QIXI", + "string": "七夕活动" } ] \ No newline at end of file