diff --git a/game-server/app/servers/role/handler/shopHandler.ts b/game-server/app/servers/role/handler/shopHandler.ts index b7c6388b6..d0b471a43 100644 --- a/game-server/app/servers/role/handler/shopHandler.ts +++ b/game-server/app/servers/role/handler/shopHandler.ts @@ -6,11 +6,12 @@ import { UserShopModel } from "../../../db/UserShop"; import { handleCost, addItems } from "../../../services/role/rewardService"; import { SHOP } from "../../../pubUtils/dicParam"; import { HeroModel } from "../../../db/Hero"; -import { checkShopInPurchase, getShopDicById, getShopListByType, getShopPrice } from "../../../services/shopService"; +import { checkShopInPurchase, getShopDicById, getShopListByType, getShopPrice, getShopMaxBuyTimes } from "../../../services/shopService"; import { RewardInter } from "../../../pubUtils/interface"; import { UserShopTypeModel } from "../../../db/UserShopType"; import { nowSeconds } from "../../../pubUtils/timeUtil"; import { isGoodsHidden } from "../../../services/dataService"; +import { RoleModel } from './../../../db/Role'; export default function(app: Application) { return new ShopHandler(app); @@ -70,14 +71,28 @@ export class ShopHandler { return resResult(checkResult); } + // 总计可买次数(从配置表读取) + const totalCanBuyTimes = getShopMaxBuyTimes(dicShopItem.price); + if (!totalCanBuyTimes || totalCanBuyTimes <= 0) { + return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); + } + + // 最大已买次数 = 总计可买次数 - 本次要买次数 + const maxAlreadyBuyTimes = totalCanBuyTimes - count; + // 消耗 let cost = getShopPrice(dicShopItem.money, count, userShop?.count||0, dicShopItem.price); let costResult = await handleCost(roleId, sid, cost, ITEM_CHANGE_REASON.SHOP_PURCHASE); if(!costResult) return resResult(STATUS.BATTLE_CONSUMES_NOT_ENOUGH); // 次数 - userShop = await UserShopModel.purchase(roleId, roleName, activityId, dicShopItem, count, seasonNum); - if(!userShop) return resResult(STATUS.BUY_COUNT_OVER); + userShop = await UserShopModel.protectedPurchase(roleId, roleName, activityId, dicShopItem, count, seasonNum, maxAlreadyBuyTimes); + if (!userShop) { + // rollback 消耗 + let role = await RoleModel.findByRoleId(roleId); + addItems(roleId, role.roleName, sid, cost, ITEM_CHANGE_REASON.SHOP_PURCHASE); + return resResult(STATUS.BUY_COUNT_OVER); + } // 获得 let reward = [{ diff --git a/game-server/app/services/shopService.ts b/game-server/app/services/shopService.ts index 4989d4504..edcbacae2 100644 --- a/game-server/app/services/shopService.ts +++ b/game-server/app/services/shopService.ts @@ -106,6 +106,22 @@ export function getShopPrice(goodId: number, count: number, buyCount: number, st }] } +/** + * 获取商品的最大购买次数 + * + * @export + * @param {string} str 比如: 1&0|2&50|3&100|4&150|5&200 + */ +export function getShopMaxBuyTimes(str: string) { + let arr = parseShopPrice(str); + for (let { times, maxTimes } of arr) { + if (maxTimes === -1) { + return times; + } + } + return 0; +} + function parseShopPrice(str: string) { let result = new Array<{ times: number, price: number, maxTimes: number }>(); if (!str) return result; diff --git a/shared/db/UserShop.ts b/shared/db/UserShop.ts index 8c1c1626c..c8e877ca1 100644 --- a/shared/db/UserShop.ts +++ b/shared/db/UserShop.ts @@ -95,6 +95,33 @@ export default class UserShop extends BaseModel { return rec; } + /** + * 防止并发操作的购买 + * + * @static + * @param {string} roleId + * @param {string} roleName + * @param {number} activityId + * @param {{ id: number, goodId: number, refreshType: number, shop: number, type: number, createTime?: number }} dicShopItem + * @param {number} inc + * @param {number} seasonNum + * @param {number} maxAlreadyBuyTimes: 最大已买次数,通过这个参数防止并发购买 + * @return {*} + * @memberof UserShop + */ + public static async protectedPurchase(roleId: string, roleName: string, activityId: number, dicShopItem: { id: number, goodId: number, refreshType: number, shop: number, type: number, createTime?: number }, inc: number, seasonNum: number, maxAlreadyBuyTimes: number) { + let code = genCode(8); + let timeCondition = this.getRefreshCondition(seasonNum); + let { id, goodId, refreshType, shop, type, createTime = 0 } = dicShopItem; + + let rec: UserShopType = await UserShopModel.findOneAndUpdate( + { roleId, itemId: id, $or: timeCondition, shop, type, createTime, count: {$lte: maxAlreadyBuyTimes} }, + { $setOnInsert: { roleName, code, goodId, refreshType, seasonNum }, $inc: { count: inc }, $set: { activityId } }, + { new: true, upsert: true } + ).lean(); + return rec; + } + public static async deleteAccount(roleId: string) { let result = await UserShopModel.deleteMany({roleId}); return result; diff --git a/shared/resource/jsons/server_const.json b/shared/resource/jsons/server_const.json index e9d1ce6c6..13b2f65f3 100644 --- a/shared/resource/jsons/server_const.json +++ b/shared/resource/jsons/server_const.json @@ -25,7 +25,8 @@ { "id": 6, "desc": "获取信息", "route": "connector.entryHandler.getData", "param": {}, "interval": 1000 }, { "id": 7, "desc": "刷新信息", "route": "connector.entryHandler.refresh", "param": {}, "interval": 1000 }, { "id": 8, "desc": "捐赠", "route": "guild.donateHandler.donate", "param": {}, "interval": 200 }, - { "id": 9, "desc": "碾压镇念塔", "route": "battle.towerBattleHandler.skipTower", "param": {}, "interval": 200 } + { "id": 9, "desc": "碾压镇念塔", "route": "battle.towerBattleHandler.skipTower", "param": {}, "interval": 200 }, + { "id": 10, "desc": "商店购买商品", "route": "role.shopHandler.purchase", "param": {}, "interval": 50 } ], "TIME_STAMP_OVER": 900000, "ACCESS_CODE_EXPIRE_TIME": 900000,