diff --git a/doc/api.md b/doc/api.md index b55dcfa..48def83 100644 --- a/doc/api.md +++ b/doc/api.md @@ -97,7 +97,14 @@ "timeLeft": 1, // 当局剩余时间 "gameResult": 0 // 当局的游戏结果, 单人的话和上一层gameResult相同 } - } + }, + "rewards": [{ + coupon: '优惠券的id', + name: '优惠券名', + count: 1, //数量 + couponUrl: '优惠券详情图的url', + ids: ['获取记录的短id'] + }] } ``` ### 3. 开始匹配 diff --git a/src/api/controllers/exam.controller.ts b/src/api/controllers/exam.controller.ts index e30e7a4..ff67e57 100644 --- a/src/api/controllers/exam.controller.ts +++ b/src/api/controllers/exam.controller.ts @@ -13,7 +13,8 @@ import { transformRecord, updateExamRank } from '../../services/GameLogic' -import { validShopId } from '../../models/shop/Shop' +import { Shop, validShopId } from '../../models/shop/Shop' +import { UserReward } from '../../models/UserReward' class ExamController extends BaseController { @role('anon') @@ -34,6 +35,11 @@ class ExamController extends BaseController { } let params = {} let results: any[] = [] + let shopData = await Shop.fetchByID(shop) + if (!shopData) { + throw new ZError(13, 'shop not found') + } + shop = shopData.id let history = new PuzzleSession({ shop, level: 0}) if (record.source === 0) { if (record.qtypes && record.qtypes.length > 0) { @@ -141,8 +147,23 @@ class ExamController extends BaseController { rspData.rankList = rankList rspData.userRank = userRank rspData.rankTotal = rankTotal + let examData = await ShopExam.findById(history.activityId) + let rewardList = [] + if (!examData && examData.rewardInfo && examData.rewardInfo.length > 0) { + let rewards = examData.getReward(statMap.score) + for (let reward of rewards) { + let geted = await UserReward.examRewardGeted(history, accountid, reward.id) + if (geted) { + continue + } + const data = await UserReward.addOneRecord(history, accountid, reward.id, reward.coupon, reward.count) + rewardList.push(data) + } + } + rspData.rewords = rewardList } rspData.gameResult = gameResult + rspData.rewords = {} return rspData } } diff --git a/src/api/controllers/puzzle.controller.ts b/src/api/controllers/puzzle.controller.ts index 9d5c957..aa37a33 100644 --- a/src/api/controllers/puzzle.controller.ts +++ b/src/api/controllers/puzzle.controller.ts @@ -28,6 +28,7 @@ import { updateSingleRank } from '../../services/GameLogic' import { Shop, validShopId } from '../../models/shop/Shop' +import { ShopActivity } from '../../models/shop/ShopActivity' class PuzzleController extends BaseController { @@ -57,7 +58,7 @@ class PuzzleController extends BaseController { } const cfg = fetchLevelCfg(level) let params = {} - let typeArr = await fetchSinglePuzzleType({shopId: shop, levelCfg: cfg}) + let { shopId, typeArr } = await fetchSinglePuzzleType({shopId: shop, levelCfg: cfg}) if (typeArr?.length > 0) { params = {$or: [{tag: {$in: typeArr}}, {sub_tag: {$in: typeArr}}]} } @@ -65,7 +66,7 @@ class PuzzleController extends BaseController { let record2 = await Puzzle.randomQuestions(Object.assign(params, {quality: 2}), 5) let record3 = await Puzzle.randomQuestions(Object.assign(params, {quality: 3}), 5) const records = (record1.concat(record2)).concat(record3) - let history = new PuzzleSession({ shop, level }) + let history = new PuzzleSession({ shop: shopId, level }) history.members.set(accountid, new PuzzleStatusClass()) for (let record of records) { history.questions.set(record.id, record.a1) @@ -203,7 +204,7 @@ class PuzzleController extends BaseController { @role('anon') @router('post /api/:accountid/puzzle/match') async joinMultipleGame(req, res) { - const { shop, aid, accountid, debug_begin_sec, debug_qcount } = req.params + let { shop, aid, accountid, debug_begin_sec, debug_qcount } = req.params let data: any = { shop, maxTime: 3600, accountid } let beginSecond = (debug_begin_sec ? +debug_begin_sec : 20) beginSecond = (Math.max(2, beginSecond)) * 1000 @@ -213,12 +214,19 @@ class PuzzleController extends BaseController { * 如果没有人请求, 说明没人参加, 也没必要开启定时了 * TODO:: 读取店铺活动配置 */ - let activityId = '1111111' - let roomId = '' let sessionId = '' let sessionMatch = '' let beginTime = 0 + if (!aid) { + throw new ZError(12, 'params no match') + } + let activity = await ShopActivity.findById(aid) + let shopData = await Shop.fetchByID(shop) + shop = shopData.id + if (!activity) { + throw new ZError(13, 'activity not found') + } let result = new RoomState().isLock(shop) try { await retry>(async () => { @@ -226,7 +234,7 @@ class PuzzleController extends BaseController { throw new RoomLockErr('') } new RoomState().lock(shop) - let history = await PuzzleSession.findOne({ shop, status: {$in: [0, 1]}, type: 1 }) + let history = await PuzzleSession.findOne({ shop, status: {$in: [0, 1]}, type: 1, activityId: aid }) if (history) { // if (history && !history.hasExpired()) { beginTime = history.begin @@ -261,6 +269,7 @@ class PuzzleController extends BaseController { roomId = rsp.data?.room?.roomId sessionId = rsp.data?.sessionId history = new PuzzleSession({shop, status: 0, type: 1}) + history.activityId = aid let memberData = new PuzzleStatusClass() memberData.sessionId = sessionId history.members.set(accountid, memberData) diff --git a/src/models/UserReward.ts b/src/models/UserReward.ts index 83bcc75..ccc4498 100644 --- a/src/models/UserReward.ts +++ b/src/models/UserReward.ts @@ -10,6 +10,7 @@ import { customAlphabet, } from 'nanoid' import { Shop } from './shop/Shop' import { Coupon } from './shop/Coupon' import { getCouponUrl } from '../services/File' +import { PuzzleSessionClass } from './match/PuzzleSession' const nanoid = customAlphabet('2345678abcdefghjkmnpqrstwxy', 10) @@ -60,6 +61,9 @@ class UserRewardClass extends BaseModule { @prop() public coupon: string + @prop() + public rewardId: string + /** * 状态 * 0: 未使用 @@ -102,6 +106,50 @@ class UserRewardClass extends BaseModule { } } + public compactInfo() { + return { + id: this.sid, + couponUrl: getCouponUrl(this.sid, this.coupon), + } + } + + public static async addOneRecord(history: PuzzleSessionClass, accountId: string, rewardId: string, coupon: string, count: number, ) { + let ids: string[] = [] + let cdata = await Coupon.findById(coupon) + for (let i = 0; i < count; i++) { + let record = new UserReward({ + accountId, + shop: history.shop, + sessionId: history._id, + status: 0, + rewardId + }) + if (history.type === 1) { + record.activityId = history.activityId + } + await record.save() + ids.push(record.sid) + } + return { + coupon, + name: cdata.name, + count, + couponUrl: getCouponUrl(history.shop, coupon), + ids + } + } + + /** + * 检查挑战的奖励是否已领取 + * @param {PuzzleSessionClass} history + * @param {string} accountId + * @param rewardId + * @return {Promise} + */ + public static async examRewardGeted(history: PuzzleSessionClass, accountId: string, rewardId: string) { + let records = await UserReward.find({accountId, examId: history.activityId, rewardId, shop: history.shop}, {_id: 1}).limit(1) + return records.length > 0 + } } export const UserReward = getModelForClass(UserRewardClass, {existingConnection: UserRewardClass.db}) diff --git a/src/models/shop/ShopActivity.ts b/src/models/shop/ShopActivity.ts index 2a36197..cd93e5b 100644 --- a/src/models/shop/ShopActivity.ts +++ b/src/models/shop/ShopActivity.ts @@ -9,8 +9,9 @@ import { noJson } from '../../decorators/nojson' import { Severity } from '@typegoose/typegoose/lib/internal/constants' import moment from 'moment' import { GameEnv } from '../../config/GameEnv' +import { Base } from '@typegoose/typegoose/lib/defaultClasses' -export class ActivityRewardClass { +export class ActivityRewardClass extends Base{ @prop() public id: number @prop() @@ -172,6 +173,24 @@ export class ShopActivityClass extends BaseModule { result.next = getNextTime(record, result.current) return result } + + /** + * 根据排名获取奖励 + * @param {number} rank + * @return {null | {count: number, id: string}} + */ + public getReward(rank: number) { + if (!this.rewardInfo || this.rewardInfo.length === 0) { + return null + } + this.rewardInfo.sort((a, b) => a.rank - b.rank) + for (let reward of this.rewardInfo) { + if (rank < reward.rank || (reward.rankEnd && rank < reward.rankEnd)) { + return {id: reward._id + '', coupon:reward.coupon, count: reward.count} + } + } + return null + } } export const ShopActivity = getModelForClass(ShopActivityClass, { existingConnection: ShopActivityClass.db }) diff --git a/src/models/shop/ShopExam.ts b/src/models/shop/ShopExam.ts index 8a62f69..1c01244 100644 --- a/src/models/shop/ShopExam.ts +++ b/src/models/shop/ShopExam.ts @@ -8,6 +8,7 @@ import { BaseModule } from '../Base' import { noJson } from '../../decorators/nojson' import { Severity } from '@typegoose/typegoose/lib/internal/constants' import { Base } from '@typegoose/typegoose/lib/defaultClasses' +import { UserReward } from '../UserReward' export class ShopPuzzleClass extends Base{ @prop() @@ -22,7 +23,7 @@ export class ShopPuzzleClass extends Base{ public a4: string } -export class ExamRewardClass { +export class ExamRewardClass extends Base{ @prop() public id: number @prop() @@ -179,6 +180,19 @@ export class ShopExamClass extends BaseModule { } }) } + public getReward(score: number) { + if (!this.rewardInfo || this.rewardInfo.length === 0) { + return [] + } + this.rewardInfo.sort((a, b) => a.rank - b.rank) + let results = [] + for (let reward of this.rewardInfo) { + if (score >= reward.rank) { + results.push({id: reward._id + '', coupon:reward.coupon, count: reward.count}) + } + } + return results + } } export const ShopExam = getModelForClass(ShopExamClass, { existingConnection: ShopExamClass.db }) diff --git a/src/services/GameLogic.ts b/src/services/GameLogic.ts index 06b9658..b309bd6 100644 --- a/src/services/GameLogic.ts +++ b/src/services/GameLogic.ts @@ -1,6 +1,6 @@ import { beginGame, - endGame, + endGame, sendMsg, sendQuestion, updateRound, updateScore @@ -16,6 +16,7 @@ import { mission_vo } from '../config/parsers/mission_vo' import { ShopActivity } from '../models/shop/ShopActivity' import { getAccountRank, getRankCount, getRankList, updateRank } from './Rank' import { PuzzleRank } from '../models/match/PuzzleRank' +import { UserReward } from '../models/UserReward' export function transformRecord(records: any[]) { @@ -64,7 +65,12 @@ export function checkSubFinish(history: any, qid: string) { */ export async function startGame(roomId: string, sessionId: string) { let history = await PuzzleSession.findById(sessionId) - let records = await Puzzle.randomQuestions({status: 1, is_hide: 0, deleted: 0, dp: 1}, history.total) + let typeArr = await fetchPvpPuzzleType({shopId: history.shop, aid: history.activityId}) + let params = {} + if (typeArr?.length > 0) { + params = {$or: [{tag: {$in: typeArr}}, {sub_tag: {$in: typeArr}}]} + } + let records = await Puzzle.randomQuestions(params, history.total) await beginGame(roomId, {}) for (let record of records) { history.questions.set(record.id, record.a1) @@ -85,6 +91,8 @@ export async function sendOneQuestion(history: any) { history.status = 9 await history.save() await endGame(roomId, {closeTime: BaseConst.ROOM_AUTO_CLOSE_TIME}) + //TODO:: 发送奖励 + await sendReward(history) return } const questions:string[] = Array.from(history.questions.keys()) @@ -114,6 +122,31 @@ export async function sendOneQuestion(history: any) { }, history.scheduleKey) } +/** + * 发送活动奖励 + * @param history + * @return {Promise} + */ +export async function sendReward(history: any) { + let activity = await ShopActivity.findById(history.activity) + if (activity.rewardInfo.length === 0) { + return + } + let members = [] + for (let [ key, val ] of history.members) { + members.push({accountId: key, score: val.score, sessionId: val.sessionId}) + } + members.sort((a, b) => a.score - b.score) + for (let i = 0, len = members.length; i < len; i++) { + const adata = activity.getReward(i) + if (!adata) { + continue + } + const data = await UserReward.addOneRecord(history, members[i].accountId, adata.id, adata.coupon, adata.count) + await sendMsg(history.room, members[i].sessionId, 'rewords', data) + } +} + export function calcExamScore(time: number, combo: number) { const cfg = new GameEnv() return cfg.pvpBaseScore + time / 100 * cfg.pvpTimeRate + combo * cfg.pvpComboRate @@ -281,7 +314,7 @@ export async function fetchSinglePuzzleType({shopId, levelCfg} : {shopId: string if (typeArr.length === 0 && levelCfg?.keyID) { typeArr = levelCfg.keyID.split(':') } - return typeArr + return {shopId: shop.id, typeArr} } /**