diff --git a/game-server/app/servers/activity/handler/monthlyFundHandler.ts b/game-server/app/servers/activity/handler/monthlyFundHandler.ts new file mode 100644 index 000000000..514836816 --- /dev/null +++ b/game-server/app/servers/activity/handler/monthlyFundHandler.ts @@ -0,0 +1,73 @@ +import { Application, BackendSession, HandlerService, } from 'pinus'; +import { resResult } from '../../../pubUtils/util'; +import { ITEM_CHANGE_REASON, STATUS } from '../../../consts'; +import { getMonthlyFundData, getMonthlyFundDataShow } from '../../../services/activity/monthlyFundService'; +import { ActivityWeeklyFundModel } from '../../../db/ActivityWeeklyFund'; +import { addReward, stringToRewardParam } from '../../../services/activity/giftPackageService'; +import { ActivityMonthlyFundModel } from '../../../db/ActivityMonthlyFund'; + + +export default function (app: Application) { + new HandlerService(app, {}); + return new MonthlyFundHandler(app); +} + +export class MonthlyFundHandler { + constructor(private app: Application) { + } + + /************************月基金****************************/ + + /** + * @description 获取数据 + * @param {BackendSession} session + * @memberof MonthlyFundHandler + */ + async getData(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + + let playerData = await getMonthlyFundDataShow(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + return resResult(STATUS.SUCCESS, playerData); + } + + /** + * @description 签到 + * @param {BackendSession} session + * @memberof MonthlyFundHandler + */ + async sign(msg: { activityId: number, pageIndex: number, dayIndex: number }, session: BackendSession) { + + const { activityId, pageIndex, dayIndex } = msg; + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const sid = session.get('sid'); + const serverId = session.get('serverId'); + + let playerData = await getMonthlyFundData(activityId, serverId, roleId); + if (!playerData) return resResult(STATUS.ACTIVITY_MISSING); + + let pageData = playerData.findPage(pageIndex); + if (!pageData) return resResult(STATUS.ACTIVITY_MISSING); + + if(!pageData.hasBought) return resResult(STATUS.ACTIVITY_MONTHLY_FUND_NOT_BOUGHT); + + let signReward = pageData.findSignReward(dayIndex); + if(!signReward) return resResult(STATUS.ACTIVITY_MONTHLY_FUND_NOT_FOUND); + if(signReward.hasReceived) return resResult(STATUS.ACTIVITY_MONTHLY_FUND_HAS_SIGN); + if(signReward.dayIndex > playerData.todayIndex) return resResult(STATUS.ACTIVITY_MONTHLY_FUND_LOCK); + + let record = await ActivityMonthlyFundModel.sign(serverId, activityId, roleId, playerData.roundIndex, pageIndex, dayIndex, playerData.todayIndex); + if(!record) return resResult(STATUS.ACTIVITY_MONTHLY_FUND_HAS_SIGN); + + let rewardParamArr = stringToRewardParam(signReward.reward); + let { goods, addHeros } = await addReward(roleId, roleName, sid, serverId, rewardParamArr, ITEM_CHANGE_REASON.ACT_MONTHLY_FUND_SIGN); + + return resResult(STATUS.SUCCESS, { + activityId, dayIndex, pageIndex, goods, addHeros + }); + } +} diff --git a/game-server/app/services/activity/activityService.ts b/game-server/app/services/activity/activityService.ts index 6e7f7cb73..fc872f83b 100644 --- a/game-server/app/services/activity/activityService.ts +++ b/game-server/app/services/activity/activityService.ts @@ -44,6 +44,7 @@ import { getBindPhoneDataShow } from './bindPhoneService'; import { getPlayerForgeDataShow } from './forgeService'; import { getPlayerMiniGameDataShow } from './miniGameService'; import { getWeeklyFundDataShow } from './weeklyFundService'; +import { getMonthlyFundData } from './monthlyFundService'; /** * 获取活动数据 @@ -250,6 +251,11 @@ export async function getActivity(serverId: number, roleId: string, uid: number, activityData = await getWeeklyFundDataShow(activityId, serverId, roleId); break } + case ACTIVITY_TYPE.MONTHLY_FUND: + { + activityData = await getMonthlyFundData(activityId, serverId, roleId); + break + } default: { console.log('未知活动类型.........', activityType) break; diff --git a/game-server/app/services/activity/monthlyFundService.ts b/game-server/app/services/activity/monthlyFundService.ts index e69de29bb..e3549fa58 100644 --- a/game-server/app/services/activity/monthlyFundService.ts +++ b/game-server/app/services/activity/monthlyFundService.ts @@ -0,0 +1,92 @@ +import { ACTIVITY_TYPE, ITEM_CHANGE_REASON, STATUS } from "../../consts"; +import { ActivityModelType } from "../../db/Activity"; +import { ActivityMonthlyFundModel } from "../../db/ActivityMonthlyFund"; +import { MonthlyFundData } from "../../domain/activityField/monthlyFundField"; +import { getRoleCreateTime, getServerCreateTime } from "../redisService"; +import { getActivityById } from "./activityService"; +import { stringToRewardParam, addReward } from "./giftPackageService"; + +/** + * 月基金玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getMonthlyFundData(activityId: number, serverId: number, roleId: string) { + let activityData = await getActivityById(activityId); + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new MonthlyFundData(activityData, createTime, serverTime); + let playerRecords = await ActivityMonthlyFundModel.findData(serverId, activityId, playerData.roundIndex, roleId); + playerData.setPlayerRecords(playerRecords); + return playerData; +} + +/** + * 月基金 玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getMonthlyFundDataShow(activityId: number, serverId: number, roleId: string) { + let playerData = await getMonthlyFundData(activityId, serverId, roleId); + if(playerData && playerData.canShow && playerData.canShow()) { + return playerData.getShowResult(); + } + return null +} + +/** + * 月基金 是否可以购买 + * @param roleId 玩家id + * @param serverId 服 + * @param activityData 活动数据 + * @returns + */ +export async function checkMonthlyFund(roleId: string, serverId: number, activityData: ActivityModelType, productID: string) { + if(!activityData || activityData.type !== ACTIVITY_TYPE.MONTHLY_FUND) return false; + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new MonthlyFundData(activityData, createTime, serverTime); + let playerRecords = await ActivityMonthlyFundModel.findData(serverId, activityData.activityId, playerData.roundIndex, roleId); + playerData.setPlayerRecords(playerRecords); + let page = playerData.findByProductID(productID); + return !page.hasBought; +} + +/** + * 周基金购买 + * @param roleId 玩家id + * @param roleName 玩家名 + * @param sid + * @param serverId 服 + * @param activityId 活动 + * @param productID 商品id + * @returns + */ +export async function makeMonthlyFund(roleId: string, roleName: string, sid: string, serverId: number, activityId: number, productID: string) { + let activityData: ActivityModelType = await getActivityById(activityId); + if (!activityData) { + return STATUS.ACTIVITY_MISSING; + } + if (activityData.type !== ACTIVITY_TYPE.MONTHLY_FUND) { + return STATUS.ACTIVITY_TYPE_ERROR; + } + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new MonthlyFundData(activityData, createTime, serverTime); + let page = playerData.findByProductID(productID); + await ActivityMonthlyFundModel.buy(serverId, activityId, roleId, playerData.roundIndex, page.pageIndex, productID); + + let rewardParamArr = stringToRewardParam(page.onceReward); + let result = await addReward(roleId, roleName, sid, serverId, rewardParamArr, ITEM_CHANGE_REASON.ACT_WEEKLY_FUND_BUY); + + return { + code: 0, + data: Object.assign(result, { activityId: activityId, pageIndex: page.pageIndex, roundIndex: playerData.roundIndex }) + } +} \ No newline at end of file diff --git a/game-server/app/services/checkParam.ts b/game-server/app/services/checkParam.ts index 407027fcf..c8b43977b 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -122,6 +122,7 @@ export function checkRouteParam(route: string, msg: any) { case 'activity.forgeHandler.getForgeActivity': case 'activity.miniGameHandler.getMiniGameActivity': case 'activity.weeklyFundHandler.getData': + case 'activity.monthlyFundHandler.getData': { if(!checkNaturalNumbers(msg.activityId)) return false; break; @@ -478,6 +479,11 @@ export function checkRouteParam(route: string, msg: any) { if(!checkNaturalNumbers(msg.activityId, msg.dayIndex)) return false; break; } + case 'activity.monthlyFundHandler.sign': + { + if(!checkNaturalNumbers(msg.activityId, msg.pageIndex, msg.dayIndex)) return false; + break; + } case "battle.barrageHandler.getBarrageList": { if(!checkNaturalStrings(msg.rid)) return false; diff --git a/game-server/app/services/orderService.ts b/game-server/app/services/orderService.ts index e3bebcdaa..142e2eb36 100644 --- a/game-server/app/services/orderService.ts +++ b/game-server/app/services/orderService.ts @@ -36,6 +36,7 @@ import { HistoryOrderModel } from '../db/HistoryOrder'; import { sendMailByContent } from './mailService'; import { RECHARGE } from '../pubUtils/dicParam'; import { checkWeeklyFund, makeWeeklyFund } from './activity/weeklyFundService'; +import { checkMonthlyFund, makeMonthlyFund } from './activity/monthlyFundService'; export async function checkOrderCanBuy(roleId: string, serverId: number, activityData: ActivityModelType, productID: string, paramStr: string) { let activityId = activityData.activityId; @@ -59,6 +60,10 @@ export async function checkOrderCanBuy(roleId: string, serverId: number, activit { return await checkWeeklyFund(roleId, serverId, activityData); } + case ACTIVITY_TYPE.MONTHLY_FUND: + { + return await checkMonthlyFund(roleId, serverId, activityData, productID); + } } return true } @@ -180,6 +185,11 @@ export async function makeOrder(orderInfo: UserOrderModelType, sid: string) { rewardResult = await makeWeeklyFund(roleId, roleInfo.roleName, sid, orderInfo.serverId, orderInfo.activityId, orderInfo.productID) break; } + case ACTIVITY_TYPE.MONTHLY_FUND: // 月基金 + { + rewardResult = await makeMonthlyFund(roleId, roleInfo.roleName, sid, orderInfo.serverId, orderInfo.activityId, orderInfo.productID) + break; + } default: rewardResult = STATUS.ERROR_TYPE; break; diff --git a/shared/consts/constModules/activityConst.ts b/shared/consts/constModules/activityConst.ts index eaa9a69e7..cb03b4ee7 100644 --- a/shared/consts/constModules/activityConst.ts +++ b/shared/consts/constModules/activityConst.ts @@ -64,6 +64,7 @@ export enum ACTIVITY_TYPE { GROUP_SHOP = 49, // 团购 BIND_PHONE = 50, // 绑定手机号 WEEKLY_FUND = 51, // 周基金 + MONTHLY_FUND = 52, // 月基金 } /** diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 5d36b5e0d..9defcb3c0 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1150,6 +1150,8 @@ export enum ITEM_CHANGE_REASON { ACT_MINI_GAME_BUY_CNT = 180, // 小游戏花元宝买 ACT_WEEKLY_FUND_BUY = 181, // 周基金一次性购买 ACT_WEEKLY_FUND_SIGN = 182, // 周基金签到 + ACT_MONTHLY_FUND_BUY = 183, // 月基金一次性购买 + ACT_MONTHLY_FUND_SIGN = 184, // 月基金签到 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 0bbc6176b..910a8d1c9 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -659,6 +659,10 @@ export const STATUS = { ACTIVITY_WEEKLY_FUND_NOT_FOUND: { code: 50057, simStr: '未找到该配置' }, ACTIVITY_WEEKLY_FUND_LOCK: { code: 50058, simStr: '本次签到暂未解锁' }, ACTIVITY_WEEKLY_FUND_HAS_SIGN: { code: 50059, simStr: '本次签到奖励已领取' }, + ACTIVITY_MONTHLY_FUND_NOT_BOUGHT: { code: 50060, simStr: '月基金未购买' }, + ACTIVITY_MONTHLY_FUND_NOT_FOUND: { code: 50061, simStr: '未找到该配置' }, + ACTIVITY_MONTHLY_FUND_LOCK: { code: 50062, simStr: '本次签到暂未解锁' }, + ACTIVITY_MONTHLY_FUND_HAS_SIGN: { code: 60063, simStr: '本次签到奖励已领取' }, // GM后台相关状态 60000 - 69999 GM_ERR_PASSWORD: { code: 60001, simStr: '账号或密码错误' }, diff --git a/shared/db/ActivityMonthlyFund.ts b/shared/db/ActivityMonthlyFund.ts new file mode 100644 index 000000000..8a709dc5a --- /dev/null +++ b/shared/db/ActivityMonthlyFund.ts @@ -0,0 +1,66 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + +/** + * 周基金 +*/ + +class SignRecord { + @prop({ required: true }) + dayIndex: number; // 第几天的签到 + @prop({ required: true }) + todayIndex: number; // 签到当天是第几天 + @prop({ required: true }) + time: Date; // 时间戳 + + constructor(dayIndex: number, todayIndex: number) { + this.dayIndex = dayIndex; + this.todayIndex = todayIndex; + this.time = new Date(); + } +} + +@index({ roleId: 1, activityId: 1 }) + +export default class Activity_Monthly_Fund extends BaseModel { + @prop({ required: true }) + serverId: number; // 服Id + + @prop({ required: true }) + activityId: number; // 活动Id + + @prop({ required: true }) + roleId: string; // 用户Id + + @prop({ required: true }) + roundIndex: number; // 第几轮 + + @prop({ required: true }) + pageIndex: number; // 第几页 + + @prop({ required: true }) + productID: string; // 商品id + + @prop({ required: true, type: SignRecord, _id: false }) + record: SignRecord[]; // 铸造记录 + + public static async findData(serverId: number, activityId: number, roundIndex: number, roleId: string) { + let result: ActivityMonthlyFundModelType[] = await ActivityMonthlyFundModel.find({ serverId, roleId, activityId, roundIndex }).lean(); + return result; + } + + public static async buy(serverId: number, activityId: number, roleId: string, roundIndex: number, pageIndex: number, productID: string) { + let result: ActivityMonthlyFundModelType = await ActivityMonthlyFundModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, pageIndex }, { $setOnInsert: { record: [], productID } }, { new: true, upsert: true }).lean(); + return result; + } + + public static async sign(serverId: number, activityId: number, roleId: string, roundIndex: number, pageIndex: number, dayIndex: number, todayIndex: number) { + let result: ActivityMonthlyFundModelType = await ActivityMonthlyFundModel.findOneAndUpdate({ serverId, roleId, activityId, roundIndex, pageIndex, 'record.dayIndex': { $ne: dayIndex } }, { $push: { record: new SignRecord(dayIndex, todayIndex)} }, { new: true }).lean(); + return result; + } +} + +export const ActivityMonthlyFundModel = getModelForClass(Activity_Monthly_Fund); + +export interface ActivityMonthlyFundModelType extends Pick, keyof Activity_Monthly_Fund> { } +export type ActivityMonthlyFundModelTypeParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/domain/activityField/monthlyFundField.ts b/shared/domain/activityField/monthlyFundField.ts new file mode 100644 index 000000000..6ce6b7c47 --- /dev/null +++ b/shared/domain/activityField/monthlyFundField.ts @@ -0,0 +1,129 @@ +// 周基金 +import { ActivityModelType } from '../../db/Activity'; +import { ActivityMonthlyFundModelType } from '../../db/ActivityMonthlyFund'; +import { ActivityBase } from './activityField'; + +interface MonthlyFundRewardInDb { + dayIndex: number; // 第几天 + reward: string; // 每天签到的奖励 type&id&count +} + +interface MonthlyFundPageInDb { + pageIndex: number; + name: string; + productID: string; // 商品id,商品表 + price: number; // 购买价格 + onceReward: string; // 购买后立刻可以获得的奖励 type&id&count + rewards: MonthlyFundRewardInDb[]; // 每天签到可领取的奖励 + rebate: number; // 显示用的返利倍数 + buyEndDay: number; // 购买截止时间 +} + +interface MonthlyFundDataInDb { + list: MonthlyFundPageInDb[]; +} + +class MonthlyFundReward { + dayIndex: number; // 第几天 + reward: string; // 每天签到的奖励 type&id&count + hasReceived: boolean = false; // 是否领取过 + + constructor(data: MonthlyFundRewardInDb) { + this.dayIndex = data.dayIndex; + this.reward = data.reward; + } + + public setHasReceived() { + this.hasReceived = true; + } +} + +class MonthlyFundPage { + pageIndex: number; // 第几页 + name: string; // 基金 + productID: string; // 商品id,商品表 + price: number; // 购买价格 + onceReward: string; // 购买后立刻可以获得的奖励 type&id&count + rebate: number; // 显示用的返利倍数 + buyEndTime: number = 0; // 购买截止时间 + rewards: MonthlyFundReward[] = []; // 每天签到可领取的奖励 + + hasBought: boolean = false; // 是否购买了 + + constructor(dataObj: MonthlyFundPageInDb, beginTime: number) { + this.pageIndex = dataObj.pageIndex; + this.name = dataObj.name; + this.productID = dataObj.productID; + this.price = dataObj.price; + this.onceReward = dataObj.onceReward; + this.rebate = dataObj.rebate; + this.buyEndTime = beginTime + dataObj.buyEndDay * 86400000; + for(let reward of (dataObj.rewards||[])) { + this.rewards.push(new MonthlyFundReward(reward)); + } + } + + public setPlayerRecord(playerData: ActivityMonthlyFundModelType) { + if(!playerData) return; + this.hasBought = true; + for(let { dayIndex } of playerData.record) { + let reward = this.rewards.find(cur => cur.dayIndex == dayIndex); + if(reward) reward.setHasReceived(); + } + } + + public findSignReward(dayIndex: number) { + return this.rewards.find(cur => cur.dayIndex == dayIndex); + } + + private hasReceivedAll() { + return !this.rewards.find(cur => !cur.hasReceived); + } + + public getShowResult() { + if(this.buyEndTime < Date.now() && !this.hasBought) return null; + if(this.hasReceivedAll()) return null + return this; + } +} + +export class MonthlyFundData extends ActivityBase { + + list: MonthlyFundPage[] = []; + + constructor(activityData: ActivityModelType, createTime: number, serverTime: number) { + super(activityData, createTime, serverTime) + this.initData(activityData.data) + } + + public initData(data: string): void { + let dataObj: MonthlyFundDataInDb = JSON.parse(data); + if(!dataObj) return; + for(let data of (dataObj.list||[])) { + let obj = new MonthlyFundPage(data, this.beginTime); + if(obj) this.list.push(obj); + } + } + + public setPlayerRecords(playerDatas: ActivityMonthlyFundModelType[]) { + for(let playerData of playerDatas) { + let data = this.list.find(cur => cur.pageIndex == playerData.pageIndex); + if(data) data.setPlayerRecord(playerData); + } + } + + public findPage(pageIndex: number) { + return this.list.find(cur => cur.pageIndex == pageIndex); + } + + public findByProductID(productID: string) { + return this.list.find(cur => cur.productID == productID); + } + + public getShowResult() { + return { + ...this.getBaseKeys(), + list: this.list.map(data => data.getShowResult()).filter(cur => cur) + } + } +} \ No newline at end of file