From f0a5809104f10b226fecfbc0d310fefe5cbef17f Mon Sep 17 00:00:00 2001 From: luying Date: Fri, 7 Apr 2023 17:57:15 +0800 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20feat(37=E9=9C=80=E6=B1=82):=20?= =?UTF-8?q?=E4=BF=AE=E6=94=B9=E8=BF=94=E5=88=A9=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../servers/activity/handler/rebateHandler.ts | 78 ++++++++++++ .../app/services/activity/rebateService.ts | 42 +++++++ game-server/app/services/checkParam.ts | 6 + .../app/services/normalBattleService.ts | 5 - game-server/app/services/orderService.ts | 28 ----- shared/consts/constModules/sysConst.ts | 1 + shared/consts/statusCode.ts | 2 + shared/db/HistoryOrder.ts | 34 +++-- shared/domain/activityField/rebateField.ts | 119 ++++++++++++++++++ .../resource/jsons/dic_zyz_activityType.json | 6 + 10 files changed, 275 insertions(+), 46 deletions(-) create mode 100644 game-server/app/servers/activity/handler/rebateHandler.ts create mode 100644 game-server/app/services/activity/rebateService.ts create mode 100644 shared/domain/activityField/rebateField.ts diff --git a/game-server/app/servers/activity/handler/rebateHandler.ts b/game-server/app/servers/activity/handler/rebateHandler.ts new file mode 100644 index 000000000..2925ae8d8 --- /dev/null +++ b/game-server/app/servers/activity/handler/rebateHandler.ts @@ -0,0 +1,78 @@ +import { Application, BackendSession, HandlerService } from 'pinus'; +import { genCode, resResult } from '../../../pubUtils/util'; +import { ITEM_CHANGE_REASON, STATUS } from '../../../consts'; +import { addReward, stringToRewardParam } from '../../../services/activity/giftPackageService'; +import { getPlayerRebateDataShow, getPlayerRebateData } from '../../../services/activity/rebateService'; +import { HistoryOrderModel } from '../../../db/HistoryOrder'; +import { UserModel } from '../../../db/User'; + + +export default function (app: Application) { + new HandlerService(app, {}); + return new RebateHandler(app); +} + +export class RebateHandler { + constructor(private app: Application) { + } + + async getData(msg: { activityId: number }, session: BackendSession) { + const { activityId } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + const uid = session.get('userid'); + + let playerData = await getPlayerRebateDataShow(activityId, roleId, serverId, uid); + return resResult(STATUS.SUCCESS, { + playerData + }) + } + + /** + * @description 领取奖励 + * @param {{ activityId: number}} msg + * @param {BackendSession} session + * @memberof RebateHandler + */ + async receiveReward(msg: { activityId: number, day: number }, session: BackendSession) { + const { activityId, day } = msg; + const roleId = session.get('roleId'); + const serverId = session.get('serverId'); + const sid = session.get('sid'); + const roleName = session.get('roleName'); + const uid = session.get('userid'); + + let playerData = await getPlayerRebateData(activityId, roleId, serverId, uid); + let item = playerData.findRebateByDay(day); + if(!item) return resResult(STATUS.WRONG_PARMS); + if(item.day > playerData.todayIndex) return resResult(STATUS.ACTIVITY_REBATE_DAY_NOT_REACH); + if(item.hasReceived) return resResult(STATUS.ACTIVITY_REBATE_HAS_RECEIVED); + + console.log('####', playerData.channelId, roleId, day) + await HistoryOrderModel.receive(playerData.channelId, roleId, day); + let rewardArray = stringToRewardParam(item.rewards); + let { goods, addHeros } = await addReward(roleId, roleName, sid, serverId, rewardArray, ITEM_CHANGE_REASON.RECEIVE_REBATE); + + return resResult(STATUS.SUCCESS, { + activityId, day, + hasReceived: true, + goods, addHeros + }); + } + + // debug接口,让自己能返利 + async debugSetRebate(msg: { totalPay: number }, session: BackendSession) { + const { totalPay } = msg; + const uid = session.get('userid'); + + let user = await UserModel.findUserByUid(uid); + if(!user.channelId) { + user = await UserModel.findOneAndUpdate({ uid }, { $set: { channelId: genCode(10) } }, { new: true }).lean(); + } + + await HistoryOrderModel.deleteMany({ channelId: user.channelId }); + await HistoryOrderModel.insertMany([{ channelId: user.channelId, totalPay }]); + + return resResult(STATUS.SUCCESS); + } +} diff --git a/game-server/app/services/activity/rebateService.ts b/game-server/app/services/activity/rebateService.ts new file mode 100644 index 000000000..05b78c456 --- /dev/null +++ b/game-server/app/services/activity/rebateService.ts @@ -0,0 +1,42 @@ +import { HistoryOrderModel } from "../../db/HistoryOrder"; +import { UserModel } from "../../db/User"; +import { RebateData } from "../../domain/activityField/rebateField"; +import { getRoleCreateTime, getServerCreateTime } from "../redisService"; +import { getActivityById } from "./activityService"; + +/** + * 玩家活动数据 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getPlayerRebateData(activityId: number, serverId: number, roleId: string, uid: number) { + let activityData = await getActivityById(activityId); + let createTime = await getRoleCreateTime(roleId); + let serverTime = await getServerCreateTime(serverId); + let playerData = new RebateData(activityData, createTime, serverTime); + let user = await UserModel.findUserByUid(uid); + if(user.channelId) { + let playerRecords = await HistoryOrderModel.findByChannelId(user.channelId); + playerData.setPlayerRecords(playerRecords, user); + } + return playerData; +} + +/** + * 玩家活动数据显示 + * + * @param {number} serverId 区Id + * @param {number} activityId 活动Id + * @param {string} roleId 角色Id + * + */ +export async function getPlayerRebateDataShow(activityId: number, serverId: number, roleId: string, uid: number) { + let playerData = await getPlayerRebateData(activityId, serverId, roleId, uid); + 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 d7a721b83..0ebf52a5d 100644 --- a/game-server/app/services/checkParam.ts +++ b/game-server/app/services/checkParam.ts @@ -488,6 +488,11 @@ export function checkRouteParam(route: string, msg: any) { if(!checkNaturalNumbers(msg.activityId, msg.pageIndex, msg.dayIndex)) return false; break; } + case 'activity.rebateHandler.receiveReward': + { + if(!checkNaturalNumbers(msg.activityId, msg.day)) return false; + break; + } case "battle.barrageHandler.getBarrageList": { if(!checkNaturalStrings(msg.rid)) return false; @@ -2085,6 +2090,7 @@ export function checkRouteParam(route: string, msg: any) { case "chat.chatHandler.debugPushMessage": case "activity.monthlyFundHandler.debugSendMonthlyFundReward": case "activity.bindPhoneHandler.debugSetGiftCodeStatus": + case 'activity.rebateHandler.debugSetRebate': { if (msg.magicWord !== DEBUG_MAGIC_WORD || !isDevelopEnv()) return false; diff --git a/game-server/app/services/normalBattleService.ts b/game-server/app/services/normalBattleService.ts index f867e9183..e04c5983c 100644 --- a/game-server/app/services/normalBattleService.ts +++ b/game-server/app/services/normalBattleService.ts @@ -20,7 +20,6 @@ import { calculateCeWithRole } from './playerCeService'; import { sendMessageToUserWithSuc } from './pushService'; import { ActionPointModel } from '../db/ActionPoint'; import { GK_MAIN, GK_MAINELITE, RECHARGE } from '../pubUtils/dicParam'; -import { rebateHistoryOrder } from './orderService'; import { updateGVGBattleRoleInfo } from './gvg/gvgTeamService'; export async function roleLevelup(type: KING_EXP_RATIO_TYPE, roleId: string, kingExp: number = 0, session: BackendSession) { @@ -53,10 +52,6 @@ export async function roleLevelup(type: KING_EXP_RATIO_TYPE, roleId: string, kin // await calculateCeWithRole(HERO_SYSTEM_TYPE.ROLE_LV, roleId, serverId, sid, { lv: newLv }); await updateRoleOnlineInfo(roleId, { lv: newLv }); - // 达到等级返利 - if(lv < RECHARGE.RECHARGE_RMB_RETURN_LEVEL_LIMITED && newLv >= RECHARGE.RECHARGE_RMB_RETURN_LEVEL_LIMITED) { - rebateHistoryOrder(roleId, role.userInfo?.uid); - } await updateGVGBattleRoleInfo(serverId, roleId, { lv }); } diff --git a/game-server/app/services/orderService.ts b/game-server/app/services/orderService.ts index bc529b6f5..bac27a4e2 100644 --- a/game-server/app/services/orderService.ts +++ b/game-server/app/services/orderService.ts @@ -31,10 +31,6 @@ import { addGuildPay } from './activity/guildPayService'; import { sendMessageToUserWithSuc } from './pushService'; import { checkParamPrice, needRebate } from '../pubUtils/sdkUtil'; import { checkShopCanBuyInOrder, makeShopOrder } from './shopService'; -import { UserModel } from '../db/User'; -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'; @@ -424,28 +420,4 @@ export async function refundOrderFromRedisPub(message: string) { await UserOrderModel.refund(order.roleId, order.localOrderID, message); return true -} - -export async function rebateHistoryOrder(roleId: string, uid: number) { - let user = await UserModel.findUserByUid(uid); - if(user) { - let historyOrders = await HistoryOrderModel.findByChannelId(user.channelId); - if(needRebate() && historyOrders.length > 0) { - for(let historyOrder of historyOrders) { - if(!historyOrder.isReceived) { - let totalPay = historyOrder.totalPay; - if(totalPay > 0) { - let gold = 0; - let goods = parseGoodStr(RECHARGE.RECHARGE_RMB_RETURN_RATIO).map(({ id, count }) => { - let result = Math.floor(count * totalPay) - gold += result; - return { id, count: result } - }); - await sendMailByContent(MAIL_TYPE.REBATE, roleId, { goods, params: [`${totalPay}`, `${gold}`] }); - await HistoryOrderModel.receive(historyOrder._id, roleId); - } - } - } - } - } } \ No newline at end of file diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 4dc8d1686..120db76bb 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -1156,6 +1156,7 @@ export enum ITEM_CHANGE_REASON { ACT_WEEKLY_FUND_SIGN = 182, // 周基金签到 ACT_MONTHLY_FUND_BUY = 183, // 月基金一次性购买 ACT_MONTHLY_FUND_SIGN = 184, // 月基金签到 + RECEIVE_REBATE = 185, // 领取返利 } export enum TA_EVENT { diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index eedc59b07..5952e2327 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -671,6 +671,8 @@ export const STATUS = { ACTIVITY_MONTHLY_FUND_HAS_SIGN: { code: 60063, simStr: '本次签到奖励已领取' }, ACTIVITY_PUBLIC_ACCOUNT_WAIT: { code: 60064, simStr: '请在公众号输入口令' }, ACTIVITY_PUBLIC_ACCOUNT_RECEIVED: { code: 60065, simStr: '已领取' }, + ACTIVITY_REBATE_DAY_NOT_REACH: { code: 60066, simStr: '暂不可领取' }, + ACTIVITY_REBATE_HAS_RECEIVED: { code: 60067, simStr: '奖励已领取' }, // GM后台相关状态 60000 - 69999 GM_ERR_PASSWORD: { code: 60001, simStr: '账号或密码错误' }, diff --git a/shared/db/HistoryOrder.ts b/shared/db/HistoryOrder.ts index 5ddd0009a..ba6cbc6a1 100644 --- a/shared/db/HistoryOrder.ts +++ b/shared/db/HistoryOrder.ts @@ -1,43 +1,51 @@ import BaseModel from './BaseModel'; import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; +class ReceiveRecord { + + @prop({ required: true }) + day: number; // 领取的玩家id + + @prop({ required: true }) + receiveRoleId: string; // 领取的玩家id + + @prop({ required: true }) + time: Date; // 领取时间 +} + /** - * 累计充值活动 + * 返利 */ @index({ channelId: 1 }) -@index({ id: 1 }) export default class HistoryOrder extends BaseModel { - @prop({ required: true }) + @prop({ required: false }) serverId: number; // 区号 @prop({ required: true }) channelId: string; // 37账号id - @prop({ required: true }) + @prop({ required: false }) roleId: string; // 当时的玩家id - @prop({ required: true }) + @prop({ required: false }) roleName: string; // 当时的玩家名 @prop({ required: true }) totalPay: number; // 总支付金额 - @prop({ required: true }) - isReceived: boolean; // 是否领取 - - @prop({ required: true }) - receiveRoleId: string; // 领取的玩家id + @prop({ required: false, type: ReceiveRecord, _id: false }) + receiveRecords: ReceiveRecord[]; // 是否领取 public static async findByChannelId(channelId: string) { let result: HistoryOrderModelType[] = await HistoryOrderModel.find({ channelId }).lean(); return result; } - public static async receive(id: string, roleId: string) { - await HistoryOrderModel.findByIdAndUpdate(id, { $set: { isReceived: true, receiveRoleId: roleId } }, { new: true }).lean(); - } + public static async receive(channelId: string, roleId: string, day: number) { + await HistoryOrderModel.updateMany({ channelId }, { $push: { receiveRecords: { day, receiveRoleId: roleId, time: new Date() } } }, { new: true }); + } } export const HistoryOrderModel = getModelForClass(HistoryOrder); diff --git a/shared/domain/activityField/rebateField.ts b/shared/domain/activityField/rebateField.ts new file mode 100644 index 000000000..915d2f47d --- /dev/null +++ b/shared/domain/activityField/rebateField.ts @@ -0,0 +1,119 @@ +import moment = require('moment'); +import { ActivityModelType } from '../../db/Activity'; +import { HistoryOrderModelType } from '../../db/HistoryOrder'; + +import { ActivityBase } from './activityField'; +import { deltaDays } from '../../pubUtils/util'; +import { UserType } from '../../db/User'; + +// 数据库 +interface ItemDataInDb { + day: number; // 第几天 + percent: number; // 百分比 + unitReward: string; // 每1元充值金额对应的奖励 +} + +interface RebatesDataInDb { + rebates: ItemDataInDb[]; +} + +class ItemData { + day: number; // 第几天 + percent: number; // 百分比,10%在这里填10,这天玩家可以领取的奖励就是 "充值金额*percent/100*unitReward" + rewards: string; // type&id&count, 玩家可以领取的奖励 + unitReward: string; // 单价 + hasReceived: boolean = false; // 是否已经领取 + + constructor(item: ItemDataInDb) { + this.day = item.day; + this.percent = item.percent; + this.unitReward = item.unitReward; + } + + public setRewards(totalPay: number) { + this.rewards = multiRewards(this.unitReward, totalPay * this.percent / 100); + } + + public setHasReceive() { + this.hasReceived = true; + } +} + +// 数据 +export class RebateData extends ActivityBase { + originEndTime: number = 0; + rebates: ItemData[] = []; + totalPay: number = 0; + playerCreateTime: number = 0; + channelId: string; + + constructor(activityData: ActivityModelType, createTime: number, serverTime: number) { + super(activityData, createTime, serverTime); + this.initData(activityData.data); + this.originEndTime = this.endTime; + this.playerCreateTime = createTime; + this.nextRefreshTime = moment('2100-01-01').valueOf(); + this.endTime = moment('2100-01-01').valueOf(); + this.todayIndex = deltaDays(moment(createTime).toDate(), new Date) + 1; + } + + public initData(data: string) { + let dataObj: RebatesDataInDb = JSON.parse(data); + for(let item of (dataObj?.rebates||[])) this.rebates.push(new ItemData(item)); + } + + public setPlayerRecords(playerRecords: HistoryOrderModelType[], user: UserType) { + for(let record of playerRecords) { + this.totalPay += record.totalPay; + } + for(let rebate of this.rebates) { + rebate.setRewards(this.totalPay); + } + + if(playerRecords.length > 0) { + let { receiveRecords = [] } = playerRecords[0]; + for(let { day } of receiveRecords) { + let rebate = this.findRebateByDay(day); + if(rebate) rebate.setHasReceive(); + } + } + this.channelId = user.channelId; + } + + public findRebateByDay(day: number) { + return this.rebates.find(cur => cur.day == day); + } + + private checkReceivedAll() { + return !this.rebates.find(cur => !cur.hasReceived); + } + + public getShowResult() { + console.log('###', this.totalPay) + if(this.totalPay <= 0) return null; + console.log('###', this.checkReceivedAll()) + if(this.checkReceivedAll()) return null; + console.log('###', this.originEndTime, this.playerCreateTime) + if(this.originEndTime < this.playerCreateTime) return null; + + return { + ...this.getBaseKeys(), + rebates: this.rebates, + originEndTime: this.originEndTime, + totalPay: this.totalPay, + } + } + +} + +function multiRewards(rewards: string, multi: number) { + let arr = rewards.split('|'); + return arr.map(str => { + let [type, id, count] = str.split('&')||[]; + if(type && id && count) { + let _count = Math.floor(parseInt(count) * multi); + if(isNaN(_count)) return null; + return `${type}&${id}&${_count}`; + } + }).filter(cur => cur).join('|'); +} \ 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 d56f95e35..28b232ce6 100644 --- a/shared/resource/jsons/dic_zyz_activityType.json +++ b/shared/resource/jsons/dic_zyz_activityType.json @@ -286,5 +286,11 @@ "activityType": 52, "name": "MONTHLY_FUND", "string": "月基金" + }, + { + "id": 53, + "activityType": 53, + "name": "REBATE", + "string": "返利" } ] \ No newline at end of file