修改获取题目和上报答案的接口

This commit is contained in:
zhl 2021-04-27 16:58:36 +08:00
parent e21f4cd19f
commit 7399b31837
4 changed files with 121 additions and 34 deletions

View File

@ -36,6 +36,8 @@
```js ```js
{ {
session: "6080f330b9655b5c0467ee5a", // 当前局的id,提交答案时必须上报该字段
records: [{
"id": "6080f330b9655b5c0467ee5e", // 题目id "id": "6080f330b9655b5c0467ee5e", // 题目id
"title": "“大丈夫为国捐躯,死而无憾!”这话是谁说的?", // 问题 "title": "“大丈夫为国捐躯,死而无憾!”这话是谁说的?", // 问题
"answers": [ // 可选答案 "answers": [ // 可选答案
@ -45,7 +47,9 @@
"刘永福" "刘永福"
], ],
"type": 1 // 题目类型 1: 普通的文字选择题, 2: 图形 "type": 1 // 题目类型 1: 普通的文字选择题, 2: 图形
}]
} }
``` ```
### 2. 上报题目答案 ### 2. 上报题目答案
@ -63,6 +67,7 @@
| 字段 | 说明 | | 字段 | 说明 |
| -------- | -------------------------------------- | | -------- | -------------------------------------- |
| id | 题目id | | id | 题目id |
| session | 当局的id, 从关卡题目列表中获取 |
| level | 关卡id | | level | 关卡id |
| answer | 回答的选项 | | answer | 回答的选项 |
| type | 回答类型, 0: 正常, 1: 超时 | | type | 回答类型, 0: 正常, 1: 超时 |
@ -72,6 +77,18 @@
```js ```js
{ {
result: 1 //答题结果 1: 正确, 0 : 错误 result: 1 //答题结果 1: 正确, 0 : 错误
"stats": { // 当局的状态
"1111": {
"answer": [ // 每一题的结果
1,
0
],
"rightCount": 1, // 答对的数量
"errorCount": 1, // 答错的数量
"comboCount": 0, // 当前连续答对的数量
"maxCombo": 1 // 当局连续答对的最大数量
}
}
} }
``` ```

View File

@ -1,38 +1,41 @@
import BaseController from '../../common/base.controller' import BaseController from '../../common/base.controller'
import { role, router } from '../../decorators/router' import { role, router } from '../../decorators/router'
import { Puzzle } from '../../models/content/Puzzle' import { Puzzle } from '../../models/content/Puzzle'
import { PuzzleSession } from '../../models/match/PuzzleSession' import {
PuzzleSession,
PuzzleStatusClass
} from '../../models/match/PuzzleSession'
import { ZError } from '../../common/ZError' import { ZError } from '../../common/ZError'
const transformRecord = function (records: any[]) {
return records.map(o => {
let answers = []
for (let i = 1; i <= 4; i++) {
if (o[`a${ i }`]) {
answers.push(o[`a${ i }`])
}
}
answers.randomSort()
return {
id: o._id,
title: o.question,
answers,
type: 1
}
})
}
class PuzzleController extends BaseController { class PuzzleController extends BaseController {
@role('anon') @role('anon')
@router('post /api/:accountid/puzzle/list') @router('post /api/:accountid/puzzle/list')
async list(req, res) { async list(req, res) {
let { shop, level, accountid } = req.params let { shop, level, accountid } = req.params
let count = 10 let count = 10
let records = await Puzzle.aggregate([ let records = await Puzzle.randomQuestions({}, count)
{ $match: { status: 1, is_hide: 0, deleted: 0 } },
{ $sample: { size: count } }
]).exec()
let history = new PuzzleSession({ shop, level }) let history = new PuzzleSession({ shop, level })
history.members = [accountid] history.members.set(accountid, new PuzzleStatusClass())
history.questions = records.map(o => o.id) history.questions = records.map(o => o._id)
await history.save() await history.save()
const results = records.map(o => { const results = transformRecord(records)
let answers = []
for (let i = 1; i <= 4; i++) {
if (o[`a${ i }`]) {
answers.push(o[`a${ i }`])
}
}
answers.randomSort()
return {
id: o._id,
title: o.question,
answers,
type: 1
}
})
return { return {
session: history.id, session: history.id,
records: results records: results
@ -42,10 +45,24 @@ class PuzzleController extends BaseController {
@role('anon') @role('anon')
@router('post /api/:accountid/puzzle/answer') @router('post /api/:accountid/puzzle/answer')
async report(req, res) { async report(req, res) {
let { id, answer, type } = req.params let { id, answer, type, session, accountid } = req.params
if (!id) { if (!id || !session) {
throw new ZError(11, 'param mismatch') throw new ZError(11, 'param mismatch')
} }
let history = await PuzzleSession.findById(session)
if (!history) {
throw new ZError(13, 'not found match info')
}
if (!history.members.has(accountid)) {
throw new ZError(14, 'not in current match')
}
if (!history.questions.find(o => o == id)) {
throw new ZError(16, 'current question not in current match')
}
let statMap = history.members.get(accountid)
if (statMap.questions.find(o => o == id)) {
throw new ZError(15, 'current question already answered')
}
let record = await Puzzle.findById(id) let record = await Puzzle.findById(id)
if (!record) { if (!record) {
throw new ZError(12, 'question not found') throw new ZError(12, 'question not found')
@ -54,6 +71,18 @@ class PuzzleController extends BaseController {
if (type == 1) { if (type == 1) {
result = 0 result = 0
} }
return { result } statMap.answer.push(result)
statMap.questions.push(id)
if (result == 1) {
statMap.rightCount ++
statMap.comboCount ++
statMap.maxCombo = Math.max(statMap.maxCombo, statMap.comboCount)
} else {
statMap.errorCount ++
statMap.comboCount = 0
}
history.markModified('members')
await history.save()
return { result, stats: history.members }
} }
} }

View File

@ -82,6 +82,16 @@ class PuzzleClass extends BaseModule {
public static async nextQuestion(this: ReturnModelType<typeof PuzzleClass>, id: string) { public static async nextQuestion(this: ReturnModelType<typeof PuzzleClass>, id: string) {
return this.findOne({deleted: 0, is_hide: 0, _id: {$gt: id }}).exec() return this.findOne({deleted: 0, is_hide: 0, _id: {$gt: id }}).exec()
} }
public static async randomQuestions(this: ReturnModelType<typeof PuzzleClass>, options: any, count: number) {
let filters = {status: 1, is_hide: 0, deleted: 0}
Object.assign(options)
return Puzzle.aggregate([
{ $match: filters },
{ $sample: { size: count } }
]).exec()
}
} }
export const Puzzle = getModelForClass(PuzzleClass, { existingConnection: PuzzleClass.db }) export const Puzzle = getModelForClass(PuzzleClass, { existingConnection: PuzzleClass.db })

View File

@ -1,29 +1,60 @@
import { dbconn } from '../../decorators/dbconn' import { dbconn } from '../../decorators/dbconn'
import { getModelForClass, modelOptions, prop } from '@typegoose/typegoose' import { getModelForClass, modelOptions, prop } from '@typegoose/typegoose'
import { BaseModule } from '../Base' import { BaseModule } from '../Base'
export class PuzzleStatusClass {
@dbconn('second') @prop({default: []})
@modelOptions({ schemaOptions: { collection: 'question_category' } }) answer: number[]
@prop({ type: () => [String], default: [] })
questions: string[]
/**
*
* @type {number}
*/
@prop({default: 0})
rightCount: number
/**
*
* @type {number}
*/
@prop({default: 0})
errorCount: number
/**
*
* @type {number}
*/
@prop({default: 0})
comboCount: number
/**
*
* @type {number}
*/
@prop({default: 0})
maxCombo: number
}
@dbconn()
@modelOptions({ schemaOptions: { collection: 'puzzle_session' } })
class PuzzleSessionClass extends BaseModule { class PuzzleSessionClass extends BaseModule {
@prop({ type: () => [String] }) @prop({ _id: false, type: PuzzleStatusClass, default: new Map() })
members: string[] public members: Map<string, PuzzleStatusClass>
@prop({ type: () => [String] }) @prop({ type: () => [String] })
questions: string[] public questions: string[]
@prop() @prop()
shop: string public shop: string
@prop() @prop()
level: number public level: number
@prop() @prop()
room: string public room: string
@prop({default: 0}) @prop({default: 0})
status: number public status: number
} }
export const PuzzleSession = getModelForClass(PuzzleSessionClass, { existingConnection: PuzzleSessionClass.db }) export const PuzzleSession = getModelForClass(PuzzleSessionClass, { existingConnection: PuzzleSessionClass.db })